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