summaryrefslogtreecommitdiffstats
path: root/sys/gnu/fs/reiserfs/reiserfs_vnops.c
blob: 12fe40918ffc8ba95c25c12e1b8eb494b1814640 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*-
 * Copyright 2000 Hans Reiser
 * See README for licensing and copyright details
 * 
 * Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
 * 
 * $FreeBSD$
 */

#include <gnu/fs/reiserfs/reiserfs_fs.h>

static vop_access_t	reiserfs_access;
static vop_bmap_t	reiserfs_bmap;
static vop_getattr_t	reiserfs_getattr;
static vop_open_t	reiserfs_open;
static vop_pathconf_t	reiserfs_pathconf;
static vop_readlink_t	reiserfs_readlink;
static vop_strategy_t	reiserfs_strategy;
static vop_vptofh_t	reiserfs_vptofh;

/* Global vfs data structures for ReiserFS */
struct vop_vector reiserfs_vnodeops = {
	.vop_default      = &default_vnodeops,

	.vop_access       = reiserfs_access,
	.vop_bmap         = reiserfs_bmap,
	.vop_cachedlookup = reiserfs_lookup,
	.vop_getattr      = reiserfs_getattr,
	.vop_inactive     = reiserfs_inactive,
	.vop_lookup       = vfs_cache_lookup,
	.vop_open         = reiserfs_open,
	.vop_reclaim      = reiserfs_reclaim,
	.vop_read         = reiserfs_read,
	.vop_readdir      = reiserfs_readdir,
	.vop_readlink     = reiserfs_readlink,
	.vop_pathconf     = reiserfs_pathconf,
	.vop_strategy     = reiserfs_strategy,
	.vop_vptofh       = reiserfs_vptofh,
};

struct vop_vector reiserfs_specops = {
	.vop_default  = &default_vnodeops,

	.vop_access   = reiserfs_access,
	.vop_getattr  = reiserfs_getattr,
	.vop_inactive = reiserfs_inactive,
	.vop_reclaim  = reiserfs_reclaim,
};

/* -------------------------------------------------------------------
 * vnode operations
 * -------------------------------------------------------------------*/

static int
reiserfs_access(struct vop_access_args *ap)
{
	int error;
	struct vnode *vp = ap->a_vp;
	struct reiserfs_node *ip = VTOI(vp);
	accmode_t accmode = ap->a_accmode;

	/*
	 * Disallow write attempts on read-only file systems; unless the file
	 * is a socket, fifo, or a block or character device resident on the
	 * file system.
	 */
	if (accmode & VWRITE) {
		switch (vp->v_type) {
		case VDIR:
		case VLNK:
		case VREG:
			if (vp->v_mount->mnt_flag & MNT_RDONLY) {
				reiserfs_log(LOG_DEBUG,
				    "no write access (read-only fs)\n");
				return (EROFS);
			}
			break;
		default:
			break;
		}
	}

	/* If immutable bit set, nobody gets to write it. */
	if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) {
		reiserfs_log(LOG_DEBUG, "no write access (immutable)\n");
		return (EPERM);
	}

	error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
	    ap->a_accmode, ap->a_cred, NULL);
	return (error);
}

static int
reiserfs_getattr(struct vop_getattr_args *ap)
{
	struct vnode *vp         = ap->a_vp;
	struct vattr *vap        = ap->a_vap;
	struct reiserfs_node *ip = VTOI(vp);

	vap->va_fsid      = dev2udev(ip->i_dev);
	vap->va_fileid    = ip->i_number;
	vap->va_mode      = ip->i_mode & ~S_IFMT;
	vap->va_nlink     = ip->i_nlink;
	vap->va_uid       = ip->i_uid;
	vap->va_gid       = ip->i_gid;
	//XXX vap->va_rdev      = ip->i_rdev;
	vap->va_size      = ip->i_size;
	vap->va_atime     = ip->i_atime;
	vap->va_mtime     = ip->i_mtime;
	vap->va_ctime     = ip->i_ctime;
	vap->va_flags     = ip->i_flags;
	vap->va_gen       = ip->i_generation;
	vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
	vap->va_bytes     = dbtob((u_quad_t)ip->i_blocks);
	vap->va_type      = vp->v_type;
	//XXX vap->va_filerev   = ip->i_modrev;

	return (0);
}

/* Return POSIX pathconf information applicable to ReiserFS filesystems */
static int
reiserfs_pathconf(struct vop_pathconf_args *ap)
{
	switch (ap->a_name) {
	case _PC_LINK_MAX:
		*ap->a_retval = REISERFS_LINK_MAX;
		return (0);
	case _PC_NAME_MAX:
		*ap->a_retval =
		    REISERFS_MAX_NAME(VTOI(ap->a_vp)->i_reiserfs->s_blocksize);
		return (0);
	case _PC_PATH_MAX:
		*ap->a_retval = PATH_MAX;
		return (0);
	case _PC_PIPE_BUF:
		*ap->a_retval = PIPE_BUF;
		return (0);
	case _PC_CHOWN_RESTRICTED:
		*ap->a_retval = 1;
		return (0);
	case _PC_NO_TRUNC:
		*ap->a_retval = 1;
		return (0);
	default:
		return (EINVAL);
	}
}

static int
reiserfs_open(struct vop_open_args *ap)
{
	/* Files marked append-only must be opened for appending. */
	if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
	    (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
		return (EPERM);

	vnode_create_vobject(ap->a_vp, VTOI(ap->a_vp)->i_size, ap->a_td);

	return (0);
}

/* Return target name of a symbolic link */
static int
reiserfs_readlink(struct vop_readlink_args *ap)
{
	struct vnode *vp = ap->a_vp;

	reiserfs_log(LOG_DEBUG, "redirect to VOP_READ()\n");
	return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
}

/* Bmap converts the logical block number of a file to its physical
 * block number on the disk. */
static int
reiserfs_bmap(ap)
	struct vop_bmap_args /* {
				struct vnode *a_vp;
				daddr_t a_bn;
				struct bufobj **a_bop;
				daddr_t *a_bnp;
				int *a_runp;
				int *a_runb;
				} */ *ap;
{
	daddr_t blkno;
	struct buf *bp;
	struct cpu_key key;
	struct item_head *ih;

	struct vnode *vp = ap->a_vp;
	struct reiserfs_node *ip = VTOI(vp);
	struct reiserfs_sb_info *sbi = ip->i_reiserfs;
	INITIALIZE_PATH(path);

	/* Prepare the key to look for the 'block'-th block of file
	 * (XXX we suppose that statfs.f_iosize == sbi->s_blocksize) */
	make_cpu_key(&key, ip, (off_t)ap->a_bn * sbi->s_blocksize + 1,
	    TYPE_ANY, 3);

	/* Search item */
	if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) {
		reiserfs_log(LOG_DEBUG, "position not found\n");
		pathrelse(&path);
		return (ENOENT);
	}

	bp = get_last_bp(&path);
	ih = get_ih(&path);

	if (is_indirect_le_ih(ih)) {
		/* Indirect item can be read by the underlying layer, instead of
		 * VOP_STRATEGY. */
		int i;
		uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih);
		reiserfs_log(LOG_DEBUG, "found an INDIRECT item\n");
		blkno = get_block_num(ind_item, path.pos_in_item);

		/* Read-ahead */
		if (ap->a_runb) {
			uint32_t count = 0;
			for (i = path.pos_in_item - 1; i >= 0; --i) {
				if ((blkno - get_block_num(ind_item, i)) !=
				    count + 1)
					break;
				++count;
			}

			/*
			 * This count isn't expressed in DEV_BSIZE base but
			 * in fs' own block base
			 * (see sys/vm/vnode_pager.c:vnode_pager_addr())
			 */
			*ap->a_runb = count;
			reiserfs_log(LOG_DEBUG,
			    " read-ahead: %d blocks before\n", *ap->a_runb);
		}
		if (ap->a_runp) {
			uint32_t count = 0;
			/*
			 * ih is an uint32_t array, that's why we use
			 * its length (in bytes) divided by 4 to know
			 * the number of items
			 */
			for (i = path.pos_in_item + 1;
			    i < ih_item_len(ih) / 4; ++i) {
				if ((get_block_num(ind_item, i) - blkno) !=
				    count + 1)
					break;
				++count;
			}

			/*
			 * This count isn't expressed in DEV_BSIZE base but
			 * in fs' own block base
			 * (see sys/vm/vnode_pager.c:vnode_pager_addr()) */
			*ap->a_runp = count;
			reiserfs_log(LOG_DEBUG,
			    " read-ahead: %d blocks after\n", *ap->a_runp);
		}

		/* Indirect items can be read using the device VOP_STRATEGY */
		if (ap->a_bop)
			*ap->a_bop = &VTOI(ap->a_vp)->i_devvp->v_bufobj;

		/* Convert the block number into DEV_BSIZE base */
		blkno *= btodb(sbi->s_blocksize);
	} else {
		/*
		 * Direct item are not DEV_BSIZE aligned, VOP_STRATEGY will
		 * have to handle this case specifically
		 */
		reiserfs_log(LOG_DEBUG, "found a DIRECT item\n");
		blkno = ap->a_bn;

		if (ap->a_runp)
			*ap->a_runp = 0;
		if (ap->a_runb)
			*ap->a_runb = 0;

		/* Direct item must be read by reiserfs_strategy */
		if (ap->a_bop)
			*ap->a_bop = &vp->v_bufobj;
	}

	if (ap->a_bnp)
		*ap->a_bnp = blkno;

	pathrelse(&path);

	if (ap->a_bnp) {
		reiserfs_log(LOG_DEBUG, "logical block: %ju (%ju),"
		    " physical block: %ju (%ju)\n",
		    (intmax_t)ap->a_bn,
		    (intmax_t)(ap->a_bn / btodb(sbi->s_blocksize)),
		    (intmax_t)*ap->a_bnp,
		    (intmax_t)(*ap->a_bnp / btodb(sbi->s_blocksize)));
	}

	return (0);
}

/* Does simply the same as reiserfs_read. It's called when reiserfs_bmap find
 * an direct item. */
static int
reiserfs_strategy(struct vop_strategy_args /* {
					      struct vnode *a_vp;
					      struct buf *a_bp;
					      } */ *ap)
{
	int error;
	struct uio auio;
	struct iovec aiov;
	struct reiserfs_node *ip;
	struct buf *bp = ap->a_bp;
	struct vnode *vp = ap->a_vp;

	reiserfs_log(LOG_DEBUG, "logical block: %ju,"
	    " physical block: %ju\n", (intmax_t)bp->b_lblkno,
	    (intmax_t)bp->b_blkno);

	ip = VTOI(vp);

	if (bp->b_iocmd == BIO_READ) {
		/* Prepare the uio structure */
		reiserfs_log(LOG_DEBUG, "prepare uio structure\n");
		aiov.iov_base = bp->b_data;
		aiov.iov_len  = MIN(bp->b_bcount, ip->i_size);
		reiserfs_log(LOG_DEBUG, "  vector length: %ju\n",
		    (intmax_t)aiov.iov_len);

		auio.uio_iov    = &aiov;
		auio.uio_iovcnt = 1;
		auio.uio_offset = 0;
		auio.uio_rw     = UIO_READ;
		auio.uio_segflg = UIO_SYSSPACE;
		auio.uio_td     = curthread;
		auio.uio_resid  = bp->b_bcount;
		reiserfs_log(LOG_DEBUG, "  buffer length: %u\n",
		    auio.uio_resid);

		reiserfs_log(LOG_DEBUG, "reading block #%ju\n",
		    (intmax_t)bp->b_blkno);
		error = reiserfs_get_block(ip, bp->b_blkno, 0, &auio);
	} else {
		/* No write support yet */
		error = (EOPNOTSUPP);
		bp->b_error    = error;
		bp->b_ioflags |= BIO_ERROR;
	}

	if (error) {
		bp->b_ioflags |= BIO_ERROR;
		bp->b_error = error;
	}

	bufdone(bp);
	return (0);
}

/*
 * Vnode pointer to File handle
 */
static int
reiserfs_vptofh(struct vop_vptofh_args /* {
					  struct vnode *a_vp;
					  struct fid *a_fhp;
					  } */ *ap)
{
	struct rfid *rfhp;
	struct reiserfs_node *ip;

	ip = VTOI(ap->a_vp);
	reiserfs_log(LOG_DEBUG,
	    "fill *fhp with inode (dirid=%d, objectid=%d)\n",
	    ip->i_ino, ip->i_number);

	rfhp = (struct rfid *)ap->a_fhp;
	rfhp->rfid_len      = sizeof(struct rfid);
	rfhp->rfid_dirid    = ip->i_ino;
	rfhp->rfid_objectid = ip->i_number;
	rfhp->rfid_gen      = ip->i_generation;

	reiserfs_log(LOG_DEBUG, "return it\n");
	return (0);
}
OpenPOWER on IntegriCloud