xref: /netbsd-src/sys/fs/sysvbfs/sysvbfs_vnops.c (revision 4d12bfcd155352508213ace5ccc59ce930ea2974)
1 /*	$NetBSD: sysvbfs_vnops.c,v 1.48 2013/05/15 16:44:03 pooka Exp $	*/
2 
3 /*-
4  * Copyright (c) 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: sysvbfs_vnops.c,v 1.48 2013/05/15 16:44:03 pooka Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/resource.h>
38 #include <sys/vnode.h>
39 #include <sys/namei.h>
40 #include <sys/dirent.h>
41 #include <sys/malloc.h>
42 #include <sys/lockf.h>
43 #include <sys/unistd.h>
44 #include <sys/fcntl.h>
45 #include <sys/kauth.h>
46 #include <sys/buf.h>
47 
48 #include <miscfs/genfs/genfs.h>
49 
50 #include <fs/sysvbfs/sysvbfs.h>
51 #include <fs/sysvbfs/bfs.h>
52 
53 #ifdef SYSVBFS_VNOPS_DEBUG
54 #define	DPRINTF(fmt, args...)	printf(fmt, ##args)
55 #else
56 #define	DPRINTF(arg...)		((void)0)
57 #endif
58 #define	ROUND_SECTOR(x)		(((x) + 511) & ~511)
59 
60 MALLOC_JUSTDEFINE(M_SYSVBFS_VNODE, "sysvbfs vnode", "sysvbfs vnode structures");
61 MALLOC_DECLARE(M_BFS);
62 
63 static void sysvbfs_file_setsize(struct vnode *, size_t);
64 
65 int
66 sysvbfs_lookup(void *arg)
67 {
68 	struct vop_lookup_args /* {
69 		struct vnode *a_dvp;
70 		struct vnode **a_vpp;
71 		struct componentname *a_cnp;
72 	} */ *a = arg;
73 	struct vnode *v = a->a_dvp;
74 	struct sysvbfs_node *bnode = v->v_data;
75 	struct bfs *bfs = bnode->bmp->bfs;	/* my filesystem */
76 	struct vnode *vpp = NULL;
77 	struct bfs_dirent *dirent = NULL;
78 	struct componentname *cnp = a->a_cnp;
79 	int nameiop = cnp->cn_nameiop;
80 	const char *name = cnp->cn_nameptr;
81 	int namelen = cnp->cn_namelen;
82 	int error;
83 
84 	DPRINTF("%s: %s op=%d %d\n", __func__, name, nameiop,
85 	    cnp->cn_flags);
86 
87 	*a->a_vpp = NULL;
88 
89 	KASSERT((cnp->cn_flags & ISDOTDOT) == 0);
90 
91 	if ((error = VOP_ACCESS(a->a_dvp, VEXEC, cnp->cn_cred)) != 0) {
92 		return error;	/* directory permission. */
93 	}
94 
95 	/* Deny last component write operation on a read-only mount */
96 	if ((cnp->cn_flags & ISLASTCN) && (v->v_mount->mnt_flag & MNT_RDONLY) &&
97 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
98 		return EROFS;
99 
100 	if (namelen == 1 && name[0] == '.') {	/* "." */
101 		vref(v);
102 		*a->a_vpp = v;
103 	} else {				/* Regular file */
104 		if (!bfs_dirent_lookup_by_name(bfs, cnp->cn_nameptr,
105 		    &dirent)) {
106 			if (nameiop != CREATE && nameiop != RENAME) {
107 				DPRINTF("%s: no such a file. (1)\n",
108 				    __func__);
109 				return ENOENT;
110 			}
111 			if ((error = VOP_ACCESS(v, VWRITE, cnp->cn_cred)) != 0)
112 				return error;
113 			return EJUSTRETURN;
114 		}
115 
116 		/* Allocate v-node */
117 		if ((error = sysvbfs_vget(v->v_mount, dirent->inode, &vpp)) != 0) {
118 			DPRINTF("%s: can't get vnode.\n", __func__);
119 			return error;
120 		}
121 		*a->a_vpp = vpp;
122 	}
123 
124 	return 0;
125 }
126 
127 int
128 sysvbfs_create(void *arg)
129 {
130 	struct vop_create_args /* {
131 		struct vnode *a_dvp;
132 		struct vnode **a_vpp;
133 		struct componentname *a_cnp;
134 		struct vattr *a_vap;
135 	} */ *a = arg;
136 	struct sysvbfs_node *bnode = a->a_dvp->v_data;
137 	struct sysvbfs_mount *bmp = bnode->bmp;
138 	struct bfs *bfs = bmp->bfs;
139 	struct mount *mp = bmp->mountp;
140 	struct bfs_dirent *dirent;
141 	struct bfs_fileattr attr;
142 	struct vattr *va = a->a_vap;
143 	kauth_cred_t cr = a->a_cnp->cn_cred;
144 	int err = 0;
145 
146 	DPRINTF("%s: %s\n", __func__, a->a_cnp->cn_nameptr);
147 	KDASSERT(a->a_vap->va_type == VREG);
148 	attr.uid = kauth_cred_geteuid(cr);
149 	attr.gid = kauth_cred_getegid(cr);
150 	attr.mode = va->va_mode;
151 
152 	if ((err = bfs_file_create(bfs, a->a_cnp->cn_nameptr, 0, 0, &attr))
153 	    != 0) {
154 		DPRINTF("%s: bfs_file_create failed.\n", __func__);
155 		goto unlock_exit;
156 	}
157 
158 	if (!bfs_dirent_lookup_by_name(bfs, a->a_cnp->cn_nameptr, &dirent))
159 		panic("no dirent for created file.");
160 
161 	if ((err = sysvbfs_vget(mp, dirent->inode, a->a_vpp)) != 0) {
162 		DPRINTF("%s: sysvbfs_vget failed.\n", __func__);
163 		goto unlock_exit;
164 	}
165 	bnode = (*a->a_vpp)->v_data;
166 	bnode->update_ctime = true;
167 	bnode->update_mtime = true;
168 	bnode->update_atime = true;
169 
170  unlock_exit:
171 	/* unlock parent directory */
172 	vput(a->a_dvp);	/* locked at sysvbfs_lookup(); */
173 
174 	return err;
175 }
176 
177 int
178 sysvbfs_open(void *arg)
179 {
180 	struct vop_open_args /* {
181 		struct vnode *a_vp;
182 		int  a_mode;
183 		kauth_cred_t a_cred;
184 	} */ *a = arg;
185 	struct vnode *v = a->a_vp;
186 	struct sysvbfs_node *bnode = v->v_data;
187 	struct bfs_inode *inode = bnode->inode;
188 	struct bfs *bfs = bnode->bmp->bfs;
189 	struct bfs_dirent *dirent;
190 
191 	DPRINTF("%s:\n", __func__);
192 	KDASSERT(v->v_type == VREG || v->v_type == VDIR);
193 
194 	if (!bfs_dirent_lookup_by_inode(bfs, inode->number, &dirent))
195 		return ENOENT;
196 	bnode->update_atime = true;
197 	if ((a->a_mode & FWRITE) && !(a->a_mode & O_APPEND)) {
198 		bnode->size = 0;
199 	} else {
200 		bnode->size = bfs_file_size(inode);
201 	}
202 	bnode->data_block = inode->start_sector;
203 
204 	return 0;
205 }
206 
207 int
208 sysvbfs_close(void *arg)
209 {
210 	struct vop_close_args /* {
211 		struct vnodeop_desc *a_desc;
212 		struct vnode *a_vp;
213 		int  a_fflag;
214 		kauth_cred_t a_cred;
215 	} */ *a = arg;
216 	struct vnode *v = a->a_vp;
217 	struct sysvbfs_node *bnode = v->v_data;
218 	struct bfs_fileattr attr;
219 
220 	DPRINTF("%s:\n", __func__);
221 
222 	if (v->v_mount->mnt_flag & MNT_RDONLY)
223 		goto out;
224 
225 	uvm_vnp_setsize(v, bnode->size);
226 
227 	memset(&attr, 0xff, sizeof attr);	/* Set VNOVAL all */
228 	if (bnode->update_atime)
229 		attr.atime = time_second;
230 	if (bnode->update_ctime)
231 		attr.ctime = time_second;
232 	if (bnode->update_mtime)
233 		attr.mtime = time_second;
234 	bfs_inode_set_attr(bnode->bmp->bfs, bnode->inode, &attr);
235 
236 	VOP_FSYNC(a->a_vp, a->a_cred, FSYNC_WAIT, 0, 0);
237 
238  out:
239 	return 0;
240 }
241 
242 static int
243 sysvbfs_check_possible(struct vnode *vp, struct sysvbfs_node *bnode,
244     mode_t mode)
245 {
246 
247 	if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
248 		return EROFS;
249 
250 	return 0;
251 }
252 
253 static int
254 sysvbfs_check_permitted(struct vnode *vp, struct sysvbfs_node *bnode,
255     mode_t mode, kauth_cred_t cred)
256 {
257 	struct bfs_fileattr *attr = &bnode->inode->attr;
258 
259 	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode,
260 	    vp->v_type, attr->mode), vp, NULL, genfs_can_access(vp->v_type,
261 	    attr->mode, attr->uid, attr->gid, mode, cred));
262 }
263 
264 int
265 sysvbfs_access(void *arg)
266 {
267 	struct vop_access_args /* {
268 		struct vnode	*a_vp;
269 		int		a_mode;
270 		kauth_cred_t	a_cred;
271 	} */ *ap = arg;
272 	struct vnode *vp = ap->a_vp;
273 	struct sysvbfs_node *bnode = vp->v_data;
274 	int error;
275 
276 	DPRINTF("%s:\n", __func__);
277 
278 	error = sysvbfs_check_possible(vp, bnode, ap->a_mode);
279 	if (error)
280 		return error;
281 
282 	error = sysvbfs_check_permitted(vp, bnode, ap->a_mode, ap->a_cred);
283 
284 	return error;
285 }
286 
287 int
288 sysvbfs_getattr(void *v)
289 {
290 	struct vop_getattr_args /* {
291 		struct vnode *a_vp;
292 		struct vattr *a_vap;
293 		kauth_cred_t a_cred;
294 	} */ *ap = v;
295 	struct vnode *vp = ap->a_vp;
296 	struct sysvbfs_node *bnode = vp->v_data;
297 	struct bfs_inode *inode = bnode->inode;
298 	struct bfs_fileattr *attr = &inode->attr;
299 	struct sysvbfs_mount *bmp = bnode->bmp;
300 	struct vattr *vap = ap->a_vap;
301 
302 	DPRINTF("%s:\n", __func__);
303 
304 	vap->va_type = vp->v_type;
305 	vap->va_mode = attr->mode;
306 	vap->va_nlink = attr->nlink;
307 	vap->va_uid = attr->uid;
308 	vap->va_gid = attr->gid;
309 	vap->va_fsid = bmp->devvp->v_rdev;
310 	vap->va_fileid = inode->number;
311 	vap->va_size = bfs_file_size(inode);
312 	vap->va_blocksize = BFS_BSIZE;
313 	vap->va_atime.tv_sec = attr->atime;
314 	vap->va_mtime.tv_sec = attr->mtime;
315 	vap->va_ctime.tv_sec = attr->ctime;
316 	vap->va_birthtime.tv_sec = 0;
317 	vap->va_gen = 1;
318 	vap->va_flags = 0;
319 	vap->va_rdev = 0;	/* No device file */
320 	vap->va_bytes = vap->va_size;
321 	vap->va_filerev = 0;
322 	vap->va_vaflags = 0;
323 
324 	return 0;
325 }
326 
327 int
328 sysvbfs_setattr(void *arg)
329 {
330 	struct vop_setattr_args /* {
331 		struct vnode *a_vp;
332 		struct vattr *a_vap;
333 		kauth_cred_t a_cred;
334 		struct proc *p;
335 	} */ *ap = arg;
336 	struct vnode *vp = ap->a_vp;
337 	struct vattr *vap = ap->a_vap;
338 	struct sysvbfs_node *bnode = vp->v_data;
339 	struct bfs_inode *inode = bnode->inode;
340 	struct bfs_fileattr *attr = &inode->attr;
341 	struct bfs *bfs = bnode->bmp->bfs;
342 	kauth_cred_t cred = ap->a_cred;
343 	int error;
344 
345 	DPRINTF("%s:\n", __func__);
346 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
347 		return EROFS;
348 
349 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
350 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
351 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
352 	    ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL))
353 		return EINVAL;
354 
355 	if (vap->va_flags != VNOVAL)
356 		return EOPNOTSUPP;
357 
358 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
359 		uid_t uid =
360 		    (vap->va_uid != (uid_t)VNOVAL) ? vap->va_uid : attr->uid;
361 		gid_t gid =
362 		    (vap->va_gid != (gid_t)VNOVAL) ? vap->va_gid : attr->gid;
363 		error = kauth_authorize_vnode(cred,
364 		    KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL,
365 		    genfs_can_chown(cred, attr->uid, attr->gid, uid, gid));
366 		if (error)
367 			return error;
368 		attr->uid = uid;
369 		attr->gid = gid;
370 	}
371 
372 	if (vap->va_size != VNOVAL)
373 		switch (vp->v_type) {
374 		case VDIR:
375 			return EISDIR;
376 		case VCHR:
377 		case VBLK:
378 		case VFIFO:
379 			break;
380 		case VREG:
381 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
382 				return EROFS;
383 			sysvbfs_file_setsize(vp, vap->va_size);
384 			break;
385 		default:
386 			return EOPNOTSUPP;
387 		}
388 
389 	if (vap->va_mode != (mode_t)VNOVAL) {
390 		mode_t mode = vap->va_mode;
391 		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY,
392 		    vp, NULL, genfs_can_chmod(vp->v_type, cred, attr->uid,
393 		    attr->gid, mode));
394 		if (error)
395 			return error;
396 		attr->mode = mode;
397 	}
398 
399 	if ((vap->va_atime.tv_sec != VNOVAL) ||
400 	    (vap->va_mtime.tv_sec != VNOVAL) ||
401 	    (vap->va_ctime.tv_sec != VNOVAL)) {
402 		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
403 		    NULL, genfs_can_chtimes(vp, vap->va_vaflags, attr->uid,
404 		    cred));
405 		if (error)
406 			return error;
407 
408 		if (vap->va_atime.tv_sec != VNOVAL)
409 			attr->atime = vap->va_atime.tv_sec;
410 		if (vap->va_mtime.tv_sec != VNOVAL)
411 			attr->mtime = vap->va_mtime.tv_sec;
412 		if (vap->va_ctime.tv_sec != VNOVAL)
413 			attr->ctime = vap->va_ctime.tv_sec;
414 	}
415 
416 	bfs_inode_set_attr(bfs, inode, attr);
417 
418 	return 0;
419 }
420 
421 int
422 sysvbfs_read(void *arg)
423 {
424 	struct vop_read_args /* {
425 		struct vnode *a_vp;
426 		struct uio *a_uio;
427 		int a_ioflag;
428 		kauth_cred_t a_cred;
429 	} */ *a = arg;
430 	struct vnode *v = a->a_vp;
431 	struct uio *uio = a->a_uio;
432 	struct sysvbfs_node *bnode = v->v_data;
433 	struct bfs_inode *inode = bnode->inode;
434 	vsize_t sz, filesz = bfs_file_size(inode);
435 	int err;
436 	const int advice = IO_ADV_DECODE(a->a_ioflag);
437 
438 	DPRINTF("%s: type=%d\n", __func__, v->v_type);
439 	switch (v->v_type) {
440 	case VREG:
441 		break;
442 	case VDIR:
443 		return EISDIR;
444 	default:
445 		return EINVAL;
446 	}
447 
448 	while (uio->uio_resid > 0) {
449 		if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0)
450 			break;
451 
452 		err = ubc_uiomove(&v->v_uobj, uio, sz, advice,
453 		    UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(v));
454 		if (err)
455 			break;
456 		DPRINTF("%s: read %ldbyte\n", __func__, sz);
457 	}
458 
459 	return  sysvbfs_update(v, NULL, NULL, UPDATE_WAIT);
460 }
461 
462 int
463 sysvbfs_write(void *arg)
464 {
465 	struct vop_write_args /* {
466 		struct vnode *a_vp;
467 		struct uio *a_uio;
468 		int  a_ioflag;
469 		kauth_cred_t a_cred;
470 	} */ *a = arg;
471 	struct vnode *v = a->a_vp;
472 	struct uio *uio = a->a_uio;
473 	int advice = IO_ADV_DECODE(a->a_ioflag);
474 	struct sysvbfs_node *bnode = v->v_data;
475 	bool extended = false;
476 	vsize_t sz;
477 	int err = 0;
478 
479 	if (a->a_vp->v_type != VREG)
480 		return EISDIR;
481 
482 	if (a->a_ioflag & IO_APPEND)
483 		uio->uio_offset = bnode->size;
484 
485 	if (uio->uio_resid == 0)
486 		return 0;
487 
488 	if (bnode->size < uio->uio_offset + uio->uio_resid) {
489 		sysvbfs_file_setsize(v, uio->uio_offset + uio->uio_resid);
490 		extended = true;
491 	}
492 
493 	while (uio->uio_resid > 0) {
494 		sz = uio->uio_resid;
495 		err = ubc_uiomove(&v->v_uobj, uio, sz, advice,
496 		    UBC_WRITE | UBC_UNMAP_FLAG(v));
497 		if (err)
498 			break;
499 		DPRINTF("%s: write %ldbyte\n", __func__, sz);
500 	}
501 	if (err)
502 		sysvbfs_file_setsize(v, bnode->size - uio->uio_resid);
503 
504 	VN_KNOTE(v, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
505 
506 	return err;
507 }
508 
509 int
510 sysvbfs_remove(void *arg)
511 {
512 	struct vop_remove_args /* {
513 		struct vnodeop_desc *a_desc;
514 		struct vnode * a_dvp;
515 		struct vnode * a_vp;
516 		struct componentname * a_cnp;
517 	} */ *ap = arg;
518 	struct vnode *vp = ap->a_vp;
519 	struct vnode *dvp = ap->a_dvp;
520 	struct sysvbfs_node *bnode = vp->v_data;
521 	struct sysvbfs_mount *bmp = bnode->bmp;
522 	struct bfs *bfs = bmp->bfs;
523 	int err;
524 
525 	DPRINTF("%s: delete %s\n", __func__, ap->a_cnp->cn_nameptr);
526 
527 	if (vp->v_type == VDIR)
528 		return EPERM;
529 
530 	if ((err = bfs_file_delete(bfs, ap->a_cnp->cn_nameptr)) != 0)
531 		DPRINTF("%s: bfs_file_delete failed.\n", __func__);
532 
533 	VN_KNOTE(ap->a_vp, NOTE_DELETE);
534 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
535 	if (dvp == vp)
536 		vrele(vp);
537 	else
538 		vput(vp);
539 	vput(dvp);
540 
541 	if (err == 0) {
542 		bnode->removed = 1;
543 	}
544 
545 	return err;
546 }
547 
548 int
549 sysvbfs_rename(void *arg)
550 {
551 	struct vop_rename_args /* {
552 		struct vnode *a_fdvp;	from parent-directory v-node
553 		struct vnode *a_fvp;	from file v-node
554 		struct componentname *a_fcnp;
555 		struct vnode *a_tdvp;	to parent-directory
556 		struct vnode *a_tvp;	to file v-node
557 		struct componentname *a_tcnp;
558 	} */ *ap = arg;
559 	struct vnode *fvp = ap->a_fvp;
560 	struct vnode *fdvp = ap->a_fdvp;
561 	struct vnode *tvp = ap->a_tvp;
562 	struct vnode *tdvp = ap->a_tdvp;
563 	struct sysvbfs_node *bnode = fvp->v_data;
564 	struct bfs *bfs = bnode->bmp->bfs;
565 	const char *from_name = ap->a_fcnp->cn_nameptr;
566 	const char *to_name = ap->a_tcnp->cn_nameptr;
567 	int error;
568 
569 	DPRINTF("%s: %s->%s\n", __func__, from_name, to_name);
570 	if ((fvp->v_mount != tdvp->v_mount) ||
571 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
572 		error = EXDEV;
573 		printf("cross-device link\n");
574 		goto out;
575 	}
576 
577 	KDASSERT(fvp->v_type == VREG);
578 	KDASSERT(tvp == NULL ? true : tvp->v_type == VREG);
579 	KASSERT(tdvp == fdvp);
580 
581 	/*
582 	 * Make sure the source hasn't been removed between lookup
583 	 * and target directory lock.
584 	 */
585 	if (bnode->removed) {
586 		error = ENOENT;
587 		goto out;
588 	}
589 
590 	error = bfs_file_rename(bfs, from_name, to_name);
591  out:
592 	if (tvp) {
593 		if (error == 0) {
594 			struct sysvbfs_node *tbnode = tvp->v_data;
595 			tbnode->removed = 1;
596 		}
597 		vput(tvp);
598 	}
599 
600 	/* tdvp == tvp probably can't happen with this fs, but safety first */
601 	if (tdvp == tvp)
602 		vrele(tdvp);
603 	else
604 		vput(tdvp);
605 
606 	vrele(fdvp);
607 	vrele(fvp);
608 
609 	return 0;
610 }
611 
612 int
613 sysvbfs_readdir(void *v)
614 {
615 	struct vop_readdir_args /* {
616 		struct vnode *a_vp;
617 		struct uio *a_uio;
618 		kauth_cred_t a_cred;
619 		int *a_eofflag;
620 		off_t **a_cookies;
621 		int *a_ncookies;
622 	} */ *ap = v;
623 	struct uio *uio = ap->a_uio;
624 	struct vnode *vp = ap->a_vp;
625 	struct sysvbfs_node *bnode = vp->v_data;
626 	struct bfs *bfs = bnode->bmp->bfs;
627 	struct dirent *dp;
628 	struct bfs_dirent *file;
629 	int i, n, error;
630 
631 	DPRINTF("%s: offset=%" PRId64 " residue=%zu\n", __func__,
632 	    uio->uio_offset, uio->uio_resid);
633 
634 	KDASSERT(vp->v_type == VDIR);
635 	KDASSERT(uio->uio_offset >= 0);
636 
637 	dp = malloc(sizeof(struct dirent), M_BFS, M_WAITOK | M_ZERO);
638 
639 	i = uio->uio_offset / sizeof(struct dirent);
640 	n = uio->uio_resid / sizeof(struct dirent);
641 	if ((i + n) > bfs->n_dirent)
642 		n = bfs->n_dirent - i;
643 
644 	for (file = &bfs->dirent[i]; i < n; file++) {
645 		if (file->inode == 0)
646 			continue;
647 		if (i == bfs->max_dirent) {
648 			DPRINTF("%s: file system inconsistent.\n",
649 			    __func__);
650 			break;
651 		}
652 		i++;
653 		memset(dp, 0, sizeof(struct dirent));
654 		dp->d_fileno = file->inode;
655 		dp->d_type = file->inode == BFS_ROOT_INODE ? DT_DIR : DT_REG;
656 		dp->d_namlen = strlen(file->name);
657 		strncpy(dp->d_name, file->name, BFS_FILENAME_MAXLEN);
658 		dp->d_reclen = sizeof(struct dirent);
659 		if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) {
660 			DPRINTF("%s: uiomove failed.\n", __func__);
661 			free(dp, M_BFS);
662 			return error;
663 		}
664 	}
665 	DPRINTF("%s: %d %d %d\n", __func__, i, n, bfs->n_dirent);
666 	*ap->a_eofflag = (i == bfs->n_dirent);
667 
668 	free(dp, M_BFS);
669 	return 0;
670 }
671 
672 int
673 sysvbfs_inactive(void *arg)
674 {
675 	struct vop_inactive_args /* {
676 		struct vnode *a_vp;
677 		bool *a_recycle;
678 	} */ *a = arg;
679 	struct vnode *v = a->a_vp;
680 	struct sysvbfs_node *bnode = v->v_data;
681 
682 	DPRINTF("%s:\n", __func__);
683 	if (bnode->removed)
684 		*a->a_recycle = true;
685 	else
686 		*a->a_recycle = false;
687 	VOP_UNLOCK(v);
688 
689 	return 0;
690 }
691 
692 int
693 sysvbfs_reclaim(void *v)
694 {
695 	extern struct pool sysvbfs_node_pool;
696 	struct vop_reclaim_args /* {
697 		struct vnode *a_vp;
698 	} */ *ap = v;
699 	struct vnode *vp = ap->a_vp;
700 	struct sysvbfs_node *bnode = vp->v_data;
701 
702 	DPRINTF("%s:\n", __func__);
703 	mutex_enter(&mntvnode_lock);
704 	LIST_REMOVE(bnode, link);
705 	mutex_exit(&mntvnode_lock);
706 	genfs_node_destroy(vp);
707 	pool_put(&sysvbfs_node_pool, bnode);
708 	vp->v_data = NULL;
709 
710 	return 0;
711 }
712 
713 int
714 sysvbfs_bmap(void *arg)
715 {
716 	struct vop_bmap_args /* {
717 		struct vnode *a_vp;
718 		daddr_t  a_bn;
719 		struct vnode **a_vpp;
720 		daddr_t *a_bnp;
721 		int *a_runp;
722 	} */ *a = arg;
723 	struct vnode *v = a->a_vp;
724 	struct sysvbfs_node *bnode = v->v_data;
725 	struct sysvbfs_mount *bmp = bnode->bmp;
726 	struct bfs_inode *inode = bnode->inode;
727 	daddr_t blk;
728 
729 	DPRINTF("%s:\n", __func__);
730 	/* BFS algorithm is contiguous allocation */
731 	blk = inode->start_sector + a->a_bn;
732 
733 	if (blk * BFS_BSIZE > bmp->bfs->data_end)
734 		return ENOSPC;
735 
736 	*a->a_vpp = bmp->devvp;
737 	*a->a_runp = 0;
738 	DPRINTF("%s: %d + %" PRId64 "\n", __func__, inode->start_sector,
739 	    a->a_bn);
740 
741 	*a->a_bnp = blk;
742 
743 
744 	return 0;
745 }
746 
747 int
748 sysvbfs_strategy(void *arg)
749 {
750 	struct vop_strategy_args /* {
751 		struct vnode *a_vp;
752 		struct buf *a_bp;
753 	} */ *a = arg;
754 	struct buf *b = a->a_bp;
755 	struct vnode *v = a->a_vp;
756 	struct sysvbfs_node *bnode = v->v_data;
757 	struct sysvbfs_mount *bmp = bnode->bmp;
758 	int error;
759 
760 	DPRINTF("%s:\n", __func__);
761 	KDASSERT(v->v_type == VREG);
762 	if (b->b_blkno == b->b_lblkno) {
763 		error = VOP_BMAP(v, b->b_lblkno, NULL, &b->b_blkno, NULL);
764 		if (error) {
765 			b->b_error = error;
766 			biodone(b);
767 			return error;
768 		}
769 		if ((long)b->b_blkno == -1)
770 			clrbuf(b);
771 	}
772 	if ((long)b->b_blkno == -1) {
773 		biodone(b);
774 		return 0;
775 	}
776 
777 	return VOP_STRATEGY(bmp->devvp, b);
778 }
779 
780 int
781 sysvbfs_print(void *v)
782 {
783 	struct vop_print_args /* {
784 		struct vnode *a_vp;
785 	} */ *ap = v;
786 	struct sysvbfs_node *bnode = ap->a_vp->v_data;
787 
788 	DPRINTF("%s:\n", __func__);
789 	bfs_dump(bnode->bmp->bfs);
790 
791 	return 0;
792 }
793 
794 int
795 sysvbfs_advlock(void *v)
796 {
797 	struct vop_advlock_args /* {
798 		struct vnode *a_vp;
799 		void *a_id;
800 		int a_op;
801 		struct flock *a_fl;
802 		int a_flags;
803 	} */ *ap = v;
804 	struct sysvbfs_node *bnode = ap->a_vp->v_data;
805 
806 	DPRINTF("%s: op=%d\n", __func__, ap->a_op);
807 
808 	return lf_advlock(ap, &bnode->lockf, bfs_file_size(bnode->inode));
809 }
810 
811 int
812 sysvbfs_pathconf(void *v)
813 {
814 	struct vop_pathconf_args /* {
815 		struct vnode *a_vp;
816 		int a_name;
817 		register_t *a_retval;
818 	} */ *ap = v;
819 	int err = 0;
820 
821 	DPRINTF("%s:\n", __func__);
822 
823 	switch (ap->a_name) {
824 	case _PC_LINK_MAX:
825 		*ap->a_retval = 1;
826 		break;
827 	case _PC_NAME_MAX:
828 		*ap->a_retval = BFS_FILENAME_MAXLEN;
829 		break;
830 	case _PC_PATH_MAX:
831 		*ap->a_retval = BFS_FILENAME_MAXLEN;
832 		break;
833 	case _PC_CHOWN_RESTRICTED:
834 		*ap->a_retval = 1;
835 		break;
836 	case _PC_NO_TRUNC:
837 		*ap->a_retval = 0;
838 		break;
839 	case _PC_SYNC_IO:
840 		*ap->a_retval = 1;
841 		break;
842 	case _PC_FILESIZEBITS:
843 		*ap->a_retval = 32;
844 		break;
845 	default:
846 		err = EINVAL;
847 		break;
848 	}
849 
850 	return err;
851 }
852 
853 int
854 sysvbfs_fsync(void *v)
855 {
856 	struct vop_fsync_args /* {
857 		struct vnode *a_vp;
858 		kauth_cred_t a_cred;
859 		int a_flags;
860 		off_t offlo;
861 		off_t offhi;
862 	} */ *ap = v;
863 	struct vnode *vp = ap->a_vp;
864 	int error, wait;
865 
866 	if (ap->a_flags & FSYNC_CACHE) {
867 		return EOPNOTSUPP;
868 	}
869 
870 	wait = (ap->a_flags & FSYNC_WAIT) != 0;
871 	error = vflushbuf(vp, ap->a_flags);
872 	if (error == 0 && (ap->a_flags & FSYNC_DATAONLY) == 0)
873 		error = sysvbfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
874 
875 	return error;
876 }
877 
878 int
879 sysvbfs_update(struct vnode *vp, const struct timespec *acc,
880     const struct timespec *mod, int flags)
881 {
882 	struct sysvbfs_node *bnode = vp->v_data;
883 	struct bfs_fileattr attr;
884 
885 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
886 		return 0;
887 
888 	DPRINTF("%s:\n", __func__);
889 	memset(&attr, 0xff, sizeof attr);	/* Set VNOVAL all */
890 	if (bnode->update_atime) {
891 		attr.atime = acc ? acc->tv_sec : time_second;
892 		bnode->update_atime = false;
893 	}
894 	if (bnode->update_ctime) {
895 		attr.ctime = time_second;
896 		bnode->update_ctime = false;
897 	}
898 	if (bnode->update_mtime) {
899 		attr.mtime = mod ? mod->tv_sec : time_second;
900 		bnode->update_mtime = false;
901 	}
902 	bfs_inode_set_attr(bnode->bmp->bfs, bnode->inode, &attr);
903 
904 	return 0;
905 }
906 
907 static void
908 sysvbfs_file_setsize(struct vnode *v, size_t size)
909 {
910 	struct sysvbfs_node *bnode = v->v_data;
911 	struct bfs_inode *inode = bnode->inode;
912 
913 	bnode->size = size;
914 	uvm_vnp_setsize(v, bnode->size);
915 	inode->end_sector = bnode->data_block +
916 	    (ROUND_SECTOR(bnode->size) >> DEV_BSHIFT) - 1;
917 	inode->eof_offset_byte = bnode->data_block * DEV_BSIZE +
918 	    bnode->size - 1;
919 	bnode->update_mtime = true;
920 }
921