1235537Sgber/*-
2235537Sgber * Copyright (c) 2010-2012 Semihalf
3235537Sgber * Copyright (c) 2008, 2009 Reinoud Zandijk
4235537Sgber * All rights reserved.
5235537Sgber *
6235537Sgber * Redistribution and use in source and binary forms, with or without
7235537Sgber * modification, are permitted provided that the following conditions
8235537Sgber * are met:
9235537Sgber * 1. Redistributions of source code must retain the above copyright
10235537Sgber *    notice, this list of conditions and the following disclaimer.
11235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
12235537Sgber *    notice, this list of conditions and the following disclaimer in the
13235537Sgber *    documentation and/or other materials provided with the distribution.
14235537Sgber *
15235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25235537Sgber *
26235537Sgber * From: NetBSD: nilfs_vnops.c,v 1.2 2009/08/26 03:40:48 elad
27235537Sgber */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber__FBSDID("$FreeBSD: stable/11/sys/fs/nandfs/nandfs_vnops.c 346032 2019-04-08 15:52:13Z sjg $");
31235537Sgber
32235537Sgber#include <sys/param.h>
33235537Sgber#include <sys/systm.h>
34235537Sgber#include <sys/conf.h>
35235537Sgber#include <sys/kernel.h>
36235537Sgber#include <sys/lock.h>
37235537Sgber#include <sys/lockf.h>
38235537Sgber#include <sys/malloc.h>
39235537Sgber#include <sys/mount.h>
40235537Sgber#include <sys/mutex.h>
41235537Sgber#include <sys/namei.h>
42235537Sgber#include <sys/sysctl.h>
43235537Sgber#include <sys/unistd.h>
44235537Sgber#include <sys/vnode.h>
45235537Sgber#include <sys/buf.h>
46235537Sgber#include <sys/bio.h>
47235537Sgber#include <sys/fcntl.h>
48235537Sgber#include <sys/dirent.h>
49251171Sjeff#include <sys/rwlock.h>
50235537Sgber#include <sys/stat.h>
51235537Sgber#include <sys/priv.h>
52235537Sgber
53235537Sgber#include <vm/vm.h>
54235537Sgber#include <vm/vm_extern.h>
55235537Sgber#include <vm/vm_object.h>
56235537Sgber#include <vm/vnode_pager.h>
57235537Sgber
58235537Sgber#include <machine/_inttypes.h>
59235537Sgber
60235537Sgber#include <fs/nandfs/nandfs_mount.h>
61235537Sgber#include <fs/nandfs/nandfs.h>
62235537Sgber#include <fs/nandfs/nandfs_subr.h>
63235537Sgber
64235537Sgberextern uma_zone_t nandfs_node_zone;
65235537Sgberstatic void nandfs_read_filebuf(struct nandfs_node *, struct buf *);
66235537Sgberstatic void nandfs_itimes_locked(struct vnode *);
67235537Sgberstatic int nandfs_truncate(struct vnode *, uint64_t);
68235537Sgber
69235537Sgberstatic vop_pathconf_t	nandfs_pathconf;
70235537Sgber
71235537Sgber#define UPDATE_CLOSE 0
72235537Sgber#define UPDATE_WAIT 0
73235537Sgber
74235537Sgberstatic int
75235537Sgbernandfs_inactive(struct vop_inactive_args *ap)
76235537Sgber{
77235537Sgber	struct vnode *vp = ap->a_vp;
78235537Sgber	struct nandfs_node *node = VTON(vp);
79235537Sgber	int error = 0;
80235537Sgber
81235537Sgber	DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, node));
82235537Sgber
83235537Sgber	if (node == NULL) {
84235537Sgber		DPRINTF(NODE, ("%s: inactive NULL node\n", __func__));
85235537Sgber		return (0);
86235537Sgber	}
87235537Sgber
88235537Sgber	if (node->nn_inode.i_mode != 0 && !(node->nn_inode.i_links_count)) {
89235537Sgber		nandfs_truncate(vp, 0);
90235537Sgber		error = nandfs_node_destroy(node);
91235537Sgber		if (error)
92235537Sgber			nandfs_error("%s: destroy node: %p\n", __func__, node);
93235537Sgber		node->nn_flags = 0;
94235537Sgber		vrecycle(vp);
95235537Sgber	}
96235537Sgber
97235537Sgber	return (error);
98235537Sgber}
99235537Sgber
100235537Sgberstatic int
101235537Sgbernandfs_reclaim(struct vop_reclaim_args *ap)
102235537Sgber{
103235537Sgber	struct vnode *vp = ap->a_vp;
104235537Sgber	struct nandfs_node *nandfs_node = VTON(vp);
105235537Sgber	struct nandfs_device *fsdev = nandfs_node->nn_nandfsdev;
106235537Sgber	uint64_t ino = nandfs_node->nn_ino;
107235537Sgber
108235537Sgber	DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, nandfs_node));
109235537Sgber
110235537Sgber	/* Invalidate all entries to a particular vnode. */
111235537Sgber	cache_purge(vp);
112235537Sgber
113235537Sgber	/* Destroy the vm object and flush associated pages. */
114235537Sgber	vnode_destroy_vobject(vp);
115235537Sgber
116235537Sgber	/* Remove from vfs hash if not system vnode */
117235537Sgber	if (!NANDFS_SYS_NODE(nandfs_node->nn_ino))
118235537Sgber		vfs_hash_remove(vp);
119235537Sgber
120235537Sgber	/* Dispose all node knowledge */
121235537Sgber	nandfs_dispose_node(&nandfs_node);
122235537Sgber
123235537Sgber	if (!NANDFS_SYS_NODE(ino))
124235537Sgber		NANDFS_WRITEUNLOCK(fsdev);
125235537Sgber
126235537Sgber	return (0);
127235537Sgber}
128235537Sgber
129235537Sgberstatic int
130235537Sgbernandfs_read(struct vop_read_args *ap)
131235537Sgber{
132331643Sdim	struct vnode *vp = ap->a_vp;
133331643Sdim	struct nandfs_node *node = VTON(vp);
134235537Sgber	struct nandfs_device *nandfsdev = node->nn_nandfsdev;
135235537Sgber	struct uio *uio = ap->a_uio;
136235537Sgber	struct buf *bp;
137235537Sgber	uint64_t size;
138235537Sgber	uint32_t blocksize;
139235537Sgber	off_t bytesinfile;
140235537Sgber	ssize_t toread, off;
141235537Sgber	daddr_t lbn;
142235537Sgber	ssize_t resid;
143235537Sgber	int error = 0;
144235537Sgber
145235537Sgber	if (uio->uio_resid == 0)
146235537Sgber		return (0);
147235537Sgber
148235537Sgber	size = node->nn_inode.i_size;
149235537Sgber	if (uio->uio_offset >= size)
150235537Sgber		return (0);
151235537Sgber
152235537Sgber	blocksize = nandfsdev->nd_blocksize;
153235537Sgber	bytesinfile = size - uio->uio_offset;
154235537Sgber
155235537Sgber	resid = omin(uio->uio_resid, bytesinfile);
156235537Sgber
157235537Sgber	while (resid) {
158235537Sgber		lbn = uio->uio_offset / blocksize;
159235537Sgber		off = uio->uio_offset & (blocksize - 1);
160235537Sgber
161235537Sgber		toread = omin(resid, blocksize - off);
162235537Sgber
163235537Sgber		DPRINTF(READ, ("nandfs_read bn: 0x%jx toread: 0x%zx (0x%x)\n",
164235537Sgber		    (uintmax_t)lbn, toread, blocksize));
165235537Sgber
166235537Sgber		error = nandfs_bread(node, lbn, NOCRED, 0, &bp);
167235537Sgber		if (error) {
168235537Sgber			brelse(bp);
169235537Sgber			break;
170235537Sgber		}
171235537Sgber
172235537Sgber		error = uiomove(bp->b_data + off, toread, uio);
173235537Sgber		if (error) {
174235537Sgber			brelse(bp);
175235537Sgber			break;
176235537Sgber		}
177235537Sgber
178235537Sgber		brelse(bp);
179235537Sgber		resid -= toread;
180235537Sgber	}
181235537Sgber
182235537Sgber	return (error);
183235537Sgber}
184235537Sgber
185235537Sgberstatic int
186235537Sgbernandfs_write(struct vop_write_args *ap)
187235537Sgber{
188235537Sgber	struct nandfs_device *fsdev;
189235537Sgber	struct nandfs_node *node;
190235537Sgber	struct vnode *vp;
191235537Sgber	struct uio *uio;
192235537Sgber	struct buf *bp;
193235537Sgber	uint64_t file_size, vblk;
194235537Sgber	uint32_t blocksize;
195235537Sgber	ssize_t towrite, off;
196235537Sgber	daddr_t lbn;
197235537Sgber	ssize_t resid;
198235537Sgber	int error, ioflag, modified;
199235537Sgber
200235537Sgber	vp = ap->a_vp;
201235537Sgber	uio = ap->a_uio;
202235537Sgber	ioflag = ap->a_ioflag;
203235537Sgber	node = VTON(vp);
204235537Sgber	fsdev = node->nn_nandfsdev;
205235537Sgber
206235537Sgber	if (nandfs_fs_full(fsdev))
207235537Sgber		return (ENOSPC);
208235537Sgber
209235537Sgber	DPRINTF(WRITE, ("nandfs_write called %#zx at %#jx\n",
210235537Sgber	    uio->uio_resid, (uintmax_t)uio->uio_offset));
211235537Sgber
212235537Sgber	if (uio->uio_offset < 0)
213235537Sgber		return (EINVAL);
214235537Sgber	if (uio->uio_resid == 0)
215235537Sgber		return (0);
216235537Sgber
217235537Sgber	blocksize = fsdev->nd_blocksize;
218235537Sgber	file_size = node->nn_inode.i_size;
219235537Sgber
220235537Sgber	switch (vp->v_type) {
221235537Sgber	case VREG:
222235537Sgber		if (ioflag & IO_APPEND)
223235537Sgber			uio->uio_offset = file_size;
224235537Sgber		break;
225235537Sgber	case VDIR:
226235537Sgber		return (EISDIR);
227235537Sgber	case VLNK:
228235537Sgber		break;
229235537Sgber	default:
230235537Sgber		panic("%s: bad file type vp: %p", __func__, vp);
231235537Sgber	}
232235537Sgber
233235537Sgber	/* If explicitly asked to append, uio_offset can be wrong? */
234235537Sgber	if (ioflag & IO_APPEND)
235235537Sgber		uio->uio_offset = file_size;
236235537Sgber
237235537Sgber	resid = uio->uio_resid;
238235537Sgber	modified = error = 0;
239235537Sgber
240235537Sgber	while (uio->uio_resid) {
241235537Sgber		lbn = uio->uio_offset / blocksize;
242235537Sgber		off = uio->uio_offset & (blocksize - 1);
243235537Sgber
244235537Sgber		towrite = omin(uio->uio_resid, blocksize - off);
245235537Sgber
246235537Sgber		DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x)\n",
247235537Sgber		    __func__, (uintmax_t)lbn, towrite, blocksize));
248235537Sgber
249235537Sgber		error = nandfs_bmap_lookup(node, lbn, &vblk);
250235537Sgber		if (error)
251235537Sgber			break;
252235537Sgber
253235537Sgber		DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x) "
254235537Sgber		    "vblk=%jx\n", __func__, (uintmax_t)lbn, towrite, blocksize,
255235537Sgber		    vblk));
256235537Sgber
257235537Sgber		if (vblk != 0)
258235537Sgber			error = nandfs_bread(node, lbn, NOCRED, 0, &bp);
259235537Sgber		else
260235537Sgber			error = nandfs_bcreate(node, lbn, NOCRED, 0, &bp);
261235537Sgber
262235537Sgber		DPRINTF(WRITE, ("%s: vp %p bread bp %p lbn %#jx\n", __func__,
263235537Sgber		    vp, bp, (uintmax_t)lbn));
264235537Sgber		if (error) {
265235537Sgber			if (bp)
266235537Sgber				brelse(bp);
267235537Sgber			break;
268235537Sgber		}
269235537Sgber
270235537Sgber		error = uiomove((char *)bp->b_data + off, (int)towrite, uio);
271235537Sgber		if (error)
272235537Sgber			break;
273235537Sgber
274235537Sgber		error = nandfs_dirty_buf(bp, 0);
275235537Sgber		if (error)
276235537Sgber			break;
277235537Sgber
278235537Sgber		modified++;
279235537Sgber	}
280235537Sgber
281235537Sgber	/* XXX proper handling when only part of file was properly written */
282235537Sgber	if (modified) {
283235537Sgber		if (resid > uio->uio_resid && ap->a_cred &&
284235537Sgber		    ap->a_cred->cr_uid != 0)
285235537Sgber			node->nn_inode.i_mode &= ~(ISUID | ISGID);
286235537Sgber
287235537Sgber		if (file_size < uio->uio_offset + uio->uio_resid) {
288235537Sgber			node->nn_inode.i_size = uio->uio_offset +
289235537Sgber			    uio->uio_resid;
290235537Sgber			node->nn_flags |= IN_CHANGE | IN_UPDATE;
291235537Sgber			vnode_pager_setsize(vp, uio->uio_offset +
292235537Sgber			    uio->uio_resid);
293235537Sgber			nandfs_itimes(vp);
294235537Sgber		}
295235537Sgber	}
296235537Sgber
297235537Sgber	DPRINTF(WRITE, ("%s: return:%d\n", __func__, error));
298235537Sgber
299235537Sgber	return (error);
300235537Sgber}
301235537Sgber
302235537Sgberstatic int
303235537Sgbernandfs_lookup(struct vop_cachedlookup_args *ap)
304235537Sgber{
305235537Sgber	struct vnode *dvp, **vpp;
306235537Sgber	struct componentname *cnp;
307235537Sgber	struct ucred *cred;
308235537Sgber	struct thread *td;
309235537Sgber	struct nandfs_node *dir_node, *node;
310235537Sgber	struct nandfsmount *nmp;
311235537Sgber	uint64_t ino, off;
312235537Sgber	const char *name;
313235537Sgber	int namelen, nameiop, islastcn, mounted_ro;
314235537Sgber	int error, found;
315235537Sgber
316235537Sgber	DPRINTF(VNCALL, ("%s\n", __func__));
317235537Sgber
318235537Sgber	dvp = ap->a_dvp;
319235537Sgber	vpp = ap->a_vpp;
320235537Sgber	*vpp = NULL;
321235537Sgber
322235537Sgber	cnp = ap->a_cnp;
323235537Sgber	cred = cnp->cn_cred;
324235537Sgber	td = cnp->cn_thread;
325235537Sgber
326235537Sgber	dir_node = VTON(dvp);
327235537Sgber	nmp = dir_node->nn_nmp;
328235537Sgber
329235537Sgber	/* Simplify/clarification flags */
330235537Sgber	nameiop = cnp->cn_nameiop;
331235537Sgber	islastcn = cnp->cn_flags & ISLASTCN;
332235537Sgber	mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY;
333235537Sgber
334235537Sgber	/*
335235537Sgber	 * If requesting a modify on the last path element on a read-only
336235537Sgber	 * filingsystem, reject lookup;
337235537Sgber	 */
338235537Sgber	if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME))
339235537Sgber		return (EROFS);
340235537Sgber
341235537Sgber	if (dir_node->nn_inode.i_links_count == 0)
342235537Sgber		return (ENOENT);
343235537Sgber
344235537Sgber	/*
345235537Sgber	 * Obviously, the file is not (anymore) in the namecache, we have to
346235537Sgber	 * search for it. There are three basic cases: '.', '..' and others.
347235537Sgber	 *
348235537Sgber	 * Following the guidelines of VOP_LOOKUP manpage and tmpfs.
349235537Sgber	 */
350235537Sgber	error = 0;
351235537Sgber	if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) {
352235537Sgber		DPRINTF(LOOKUP, ("\tlookup '.'\n"));
353235537Sgber		/* Special case 1 '.' */
354235537Sgber		VREF(dvp);
355235537Sgber		*vpp = dvp;
356235537Sgber		/* Done */
357235537Sgber	} else if (cnp->cn_flags & ISDOTDOT) {
358235537Sgber		/* Special case 2 '..' */
359235537Sgber		DPRINTF(LOOKUP, ("\tlookup '..'\n"));
360235537Sgber
361235537Sgber		/* Get our node */
362235537Sgber		name = "..";
363235537Sgber		namelen = 2;
364235537Sgber		error = nandfs_lookup_name_in_dir(dvp, name, namelen, &ino,
365235537Sgber		    &found, &off);
366235537Sgber		if (error)
367235537Sgber			goto out;
368235537Sgber		if (!found)
369235537Sgber			error = ENOENT;
370235537Sgber
371235537Sgber		/* First unlock parent */
372235537Sgber		VOP_UNLOCK(dvp, 0);
373235537Sgber
374235537Sgber		if (error == 0) {
375235537Sgber			DPRINTF(LOOKUP, ("\tfound '..'\n"));
376235537Sgber			/* Try to create/reuse the node */
377235537Sgber			error = nandfs_get_node(nmp, ino, &node);
378235537Sgber
379235537Sgber			if (!error) {
380235537Sgber				DPRINTF(LOOKUP,
381235537Sgber				    ("\tnode retrieved/created OK\n"));
382235537Sgber				*vpp = NTOV(node);
383235537Sgber			}
384235537Sgber		}
385235537Sgber
386235537Sgber		/* Try to relock parent */
387235537Sgber		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
388235537Sgber	} else {
389235537Sgber		DPRINTF(LOOKUP, ("\tlookup file\n"));
390235537Sgber		/* All other files */
391235537Sgber		/* Look up filename in the directory returning its inode */
392235537Sgber		name = cnp->cn_nameptr;
393235537Sgber		namelen = cnp->cn_namelen;
394235537Sgber		error = nandfs_lookup_name_in_dir(dvp, name, namelen,
395235537Sgber		    &ino, &found, &off);
396235537Sgber		if (error)
397235537Sgber			goto out;
398235537Sgber		if (!found) {
399235537Sgber			DPRINTF(LOOKUP, ("\tNOT found\n"));
400235537Sgber			/*
401235537Sgber			 * UGH, didn't find name. If we're creating or
402235537Sgber			 * renaming on the last name this is OK and we ought
403235537Sgber			 * to return EJUSTRETURN if its allowed to be created.
404235537Sgber			 */
405235537Sgber			error = ENOENT;
406235537Sgber			if ((nameiop == CREATE || nameiop == RENAME) &&
407235537Sgber			    islastcn) {
408269018Simp				error = VOP_ACCESS(dvp, VWRITE, cred, td);
409235537Sgber				if (!error) {
410235537Sgber					/* keep the component name */
411235537Sgber					cnp->cn_flags |= SAVENAME;
412235537Sgber					error = EJUSTRETURN;
413235537Sgber				}
414235537Sgber			}
415235537Sgber			/* Done */
416235537Sgber		} else {
417235537Sgber			if (ino == NANDFS_WHT_INO)
418235537Sgber				cnp->cn_flags |= ISWHITEOUT;
419235537Sgber
420235537Sgber			if ((cnp->cn_flags & ISWHITEOUT) &&
421235537Sgber			    (nameiop == LOOKUP))
422235537Sgber				return (ENOENT);
423235537Sgber
424235537Sgber			if ((nameiop == DELETE) && islastcn) {
425235537Sgber				if ((cnp->cn_flags & ISWHITEOUT) &&
426235537Sgber				    (cnp->cn_flags & DOWHITEOUT)) {
427235537Sgber					cnp->cn_flags |= SAVENAME;
428235537Sgber					dir_node->nn_diroff = off;
429235537Sgber					return (EJUSTRETURN);
430235537Sgber				}
431235537Sgber
432235537Sgber				error = VOP_ACCESS(dvp, VWRITE, cred,
433235537Sgber				    cnp->cn_thread);
434235537Sgber				if (error)
435235537Sgber					return (error);
436235537Sgber
437235537Sgber				/* Try to create/reuse the node */
438235537Sgber				error = nandfs_get_node(nmp, ino, &node);
439235537Sgber				if (!error) {
440235537Sgber					*vpp = NTOV(node);
441235537Sgber					node->nn_diroff = off;
442235537Sgber				}
443235537Sgber
444235537Sgber				if ((dir_node->nn_inode.i_mode & ISVTX) &&
445235537Sgber				    cred->cr_uid != 0 &&
446235537Sgber				    cred->cr_uid != dir_node->nn_inode.i_uid &&
447235537Sgber				    node->nn_inode.i_uid != cred->cr_uid) {
448235537Sgber					vput(*vpp);
449235537Sgber					*vpp = NULL;
450235537Sgber					return (EPERM);
451235537Sgber				}
452235537Sgber			} else if ((nameiop == RENAME) && islastcn) {
453235537Sgber				error = VOP_ACCESS(dvp, VWRITE, cred,
454235537Sgber				    cnp->cn_thread);
455235537Sgber				if (error)
456235537Sgber					return (error);
457235537Sgber
458235537Sgber				/* Try to create/reuse the node */
459235537Sgber				error = nandfs_get_node(nmp, ino, &node);
460235537Sgber				if (!error) {
461235537Sgber					*vpp = NTOV(node);
462235537Sgber					node->nn_diroff = off;
463235537Sgber				}
464235537Sgber			} else {
465235537Sgber				/* Try to create/reuse the node */
466235537Sgber				error = nandfs_get_node(nmp, ino, &node);
467235537Sgber				if (!error) {
468235537Sgber					*vpp = NTOV(node);
469235537Sgber					node->nn_diroff = off;
470235537Sgber				}
471235537Sgber			}
472235537Sgber		}
473235537Sgber	}
474235537Sgber
475235537Sgberout:
476235537Sgber	/*
477235537Sgber	 * Store result in the cache if requested. If we are creating a file,
478235537Sgber	 * the file might not be found and thus putting it into the namecache
479235537Sgber	 * might be seen as negative caching.
480235537Sgber	 */
481275897Skib	if ((cnp->cn_flags & MAKEENTRY) != 0)
482235537Sgber		cache_enter(dvp, *vpp, cnp);
483235537Sgber
484235537Sgber	return (error);
485235537Sgber
486235537Sgber}
487235537Sgber
488235537Sgberstatic int
489235537Sgbernandfs_getattr(struct vop_getattr_args *ap)
490235537Sgber{
491235537Sgber	struct vnode *vp = ap->a_vp;
492235537Sgber	struct vattr *vap = ap->a_vap;
493235537Sgber	struct nandfs_node *node = VTON(vp);
494235537Sgber	struct nandfs_inode *inode = &node->nn_inode;
495235537Sgber
496235537Sgber	DPRINTF(VNCALL, ("%s: vp: %p\n", __func__, vp));
497235537Sgber	nandfs_itimes(vp);
498235537Sgber
499235537Sgber	/* Basic info */
500235537Sgber	VATTR_NULL(vap);
501235537Sgber	vap->va_atime.tv_sec = inode->i_mtime;
502235537Sgber	vap->va_atime.tv_nsec = inode->i_mtime_nsec;
503235537Sgber	vap->va_mtime.tv_sec = inode->i_mtime;
504235537Sgber	vap->va_mtime.tv_nsec = inode->i_mtime_nsec;
505235537Sgber	vap->va_ctime.tv_sec = inode->i_ctime;
506235537Sgber	vap->va_ctime.tv_nsec = inode->i_ctime_nsec;
507235537Sgber	vap->va_type = IFTOVT(inode->i_mode);
508235537Sgber	vap->va_mode = inode->i_mode & ~S_IFMT;
509235537Sgber	vap->va_nlink = inode->i_links_count;
510235537Sgber	vap->va_uid = inode->i_uid;
511235537Sgber	vap->va_gid = inode->i_gid;
512235537Sgber	vap->va_rdev = inode->i_special;
513235537Sgber	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
514235537Sgber	vap->va_fileid = node->nn_ino;
515235537Sgber	vap->va_size = inode->i_size;
516235537Sgber	vap->va_blocksize = node->nn_nandfsdev->nd_blocksize;
517235537Sgber	vap->va_gen = 0;
518235537Sgber	vap->va_flags = inode->i_flags;
519235537Sgber	vap->va_bytes = inode->i_blocks * vap->va_blocksize;
520235537Sgber	vap->va_filerev = 0;
521235537Sgber	vap->va_vaflags = 0;
522235537Sgber
523235537Sgber	return (0);
524235537Sgber}
525235537Sgber
526235537Sgberstatic int
527235537Sgbernandfs_vtruncbuf(struct vnode *vp, uint64_t nblks)
528235537Sgber{
529235537Sgber	struct nandfs_device *nffsdev;
530235537Sgber	struct bufobj *bo;
531235537Sgber	struct buf *bp, *nbp;
532235537Sgber
533235537Sgber	bo = &vp->v_bufobj;
534235537Sgber	nffsdev = VTON(vp)->nn_nandfsdev;
535235537Sgber
536235537Sgber	ASSERT_VOP_LOCKED(vp, "nandfs_truncate");
537235537Sgberrestart:
538235537Sgber	BO_LOCK(bo);
539235537Sgberrestart_locked:
540235537Sgber	TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, nbp) {
541235537Sgber		if (bp->b_lblkno < nblks)
542235537Sgber			continue;
543235537Sgber		if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL))
544235537Sgber			goto restart_locked;
545235537Sgber
546235537Sgber		bremfree(bp);
547235537Sgber		bp->b_flags |= (B_INVAL | B_RELBUF);
548235537Sgber		bp->b_flags &= ~(B_ASYNC | B_MANAGED);
549235537Sgber		BO_UNLOCK(bo);
550235537Sgber		brelse(bp);
551235537Sgber		BO_LOCK(bo);
552235537Sgber	}
553235537Sgber
554235537Sgber	TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
555235537Sgber		if (bp->b_lblkno < nblks)
556235537Sgber			continue;
557235537Sgber		if (BUF_LOCK(bp,
558235537Sgber		    LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK,
559251171Sjeff		    BO_LOCKPTR(bo)) == ENOLCK)
560235537Sgber			goto restart;
561235537Sgber		bp->b_flags |= (B_INVAL | B_RELBUF);
562235537Sgber		bp->b_flags &= ~(B_ASYNC | B_MANAGED);
563235537Sgber		brelse(bp);
564235537Sgber		nandfs_dirty_bufs_decrement(nffsdev);
565235537Sgber		BO_LOCK(bo);
566235537Sgber	}
567235537Sgber
568235537Sgber	BO_UNLOCK(bo);
569235537Sgber
570235537Sgber	return (0);
571235537Sgber}
572235537Sgber
573235537Sgberstatic int
574235537Sgbernandfs_truncate(struct vnode *vp, uint64_t newsize)
575235537Sgber{
576235537Sgber	struct nandfs_device *nffsdev;
577235537Sgber	struct nandfs_node *node;
578235537Sgber	struct nandfs_inode *inode;
579235537Sgber	struct buf *bp = NULL;
580235537Sgber	uint64_t oblks, nblks, vblk, size, rest;
581235537Sgber	int error;
582235537Sgber
583235537Sgber	node = VTON(vp);
584235537Sgber	nffsdev = node->nn_nandfsdev;
585235537Sgber	inode = &node->nn_inode;
586235537Sgber
587235537Sgber	/* Calculate end of file */
588235537Sgber	size = inode->i_size;
589235537Sgber
590235537Sgber	if (newsize == size) {
591235537Sgber		node->nn_flags |= IN_CHANGE | IN_UPDATE;
592235537Sgber		nandfs_itimes(vp);
593235537Sgber		return (0);
594235537Sgber	}
595235537Sgber
596235537Sgber	if (newsize > size) {
597235537Sgber		inode->i_size = newsize;
598235537Sgber		vnode_pager_setsize(vp, newsize);
599235537Sgber		node->nn_flags |= IN_CHANGE | IN_UPDATE;
600235537Sgber		nandfs_itimes(vp);
601235537Sgber		return (0);
602235537Sgber	}
603235537Sgber
604235537Sgber	nblks = howmany(newsize, nffsdev->nd_blocksize);
605235537Sgber	oblks = howmany(size, nffsdev->nd_blocksize);
606235537Sgber	rest = newsize % nffsdev->nd_blocksize;
607235537Sgber
608235537Sgber	if (rest) {
609235537Sgber		error = nandfs_bmap_lookup(node, nblks - 1, &vblk);
610235537Sgber		if (error)
611235537Sgber			return (error);
612235537Sgber
613235537Sgber		if (vblk != 0)
614235537Sgber			error = nandfs_bread(node, nblks - 1, NOCRED, 0, &bp);
615235537Sgber		else
616235537Sgber			error = nandfs_bcreate(node, nblks - 1, NOCRED, 0, &bp);
617235537Sgber
618235537Sgber		if (error) {
619235537Sgber			if (bp)
620235537Sgber				brelse(bp);
621235537Sgber			return (error);
622235537Sgber		}
623235537Sgber
624235537Sgber		bzero((char *)bp->b_data + rest,
625235537Sgber		    (u_int)(nffsdev->nd_blocksize - rest));
626235537Sgber		error = nandfs_dirty_buf(bp, 0);
627235537Sgber		if (error)
628235537Sgber			return (error);
629235537Sgber	}
630235537Sgber
631235537Sgber	DPRINTF(VNCALL, ("%s: vp %p oblks %jx nblks %jx\n", __func__, vp, oblks,
632235537Sgber	    nblks));
633235537Sgber
634235537Sgber	error = nandfs_bmap_truncate_mapping(node, oblks - 1, nblks - 1);
635235537Sgber	if (error) {
636235537Sgber		if (bp)
637235537Sgber			nandfs_undirty_buf(bp);
638235537Sgber		return (error);
639235537Sgber	}
640235537Sgber
641235537Sgber	error = nandfs_vtruncbuf(vp, nblks);
642235537Sgber	if (error) {
643235537Sgber		if (bp)
644235537Sgber			nandfs_undirty_buf(bp);
645235537Sgber		return (error);
646235537Sgber	}
647235537Sgber
648235537Sgber	inode->i_size = newsize;
649235537Sgber	vnode_pager_setsize(vp, newsize);
650235537Sgber	node->nn_flags |= IN_CHANGE | IN_UPDATE;
651235537Sgber	nandfs_itimes(vp);
652235537Sgber
653235537Sgber	return (error);
654235537Sgber}
655235537Sgber
656235537Sgberstatic void
657235537Sgbernandfs_itimes_locked(struct vnode *vp)
658235537Sgber{
659235537Sgber	struct nandfs_node *node;
660235537Sgber	struct nandfs_inode *inode;
661235537Sgber	struct timespec ts;
662235537Sgber
663235537Sgber	ASSERT_VI_LOCKED(vp, __func__);
664235537Sgber
665235537Sgber	node = VTON(vp);
666235537Sgber	inode = &node->nn_inode;
667235537Sgber
668235537Sgber	if ((node->nn_flags & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
669235537Sgber		return;
670235537Sgber
671235537Sgber	if (((vp->v_mount->mnt_kern_flag &
672235537Sgber	    (MNTK_SUSPENDED | MNTK_SUSPEND)) == 0) ||
673235537Sgber	    (node->nn_flags & (IN_CHANGE | IN_UPDATE)))
674235537Sgber		node->nn_flags |= IN_MODIFIED;
675235537Sgber
676235537Sgber	vfs_timestamp(&ts);
677235537Sgber	if (node->nn_flags & IN_UPDATE) {
678235537Sgber		inode->i_mtime = ts.tv_sec;
679235537Sgber		inode->i_mtime_nsec = ts.tv_nsec;
680235537Sgber	}
681235537Sgber	if (node->nn_flags & IN_CHANGE) {
682235537Sgber		inode->i_ctime = ts.tv_sec;
683235537Sgber		inode->i_ctime_nsec = ts.tv_nsec;
684235537Sgber	}
685235537Sgber
686235537Sgber	node->nn_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
687235537Sgber}
688235537Sgber
689235537Sgbervoid
690235537Sgbernandfs_itimes(struct vnode *vp)
691235537Sgber{
692235537Sgber
693235537Sgber	VI_LOCK(vp);
694235537Sgber	nandfs_itimes_locked(vp);
695235537Sgber	VI_UNLOCK(vp);
696235537Sgber}
697235537Sgber
698235537Sgberstatic int
699235537Sgbernandfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td)
700235537Sgber{
701235537Sgber	struct nandfs_node *node = VTON(vp);
702235537Sgber	struct nandfs_inode *inode = &node->nn_inode;
703235537Sgber	uint16_t nmode;
704235537Sgber	int error = 0;
705235537Sgber
706235537Sgber	DPRINTF(VNCALL, ("%s: vp %p, mode %x, cred %p, td %p\n", __func__, vp,
707235537Sgber	    mode, cred, td));
708235537Sgber	/*
709235537Sgber	 * To modify the permissions on a file, must possess VADMIN
710235537Sgber	 * for that file.
711235537Sgber	 */
712235537Sgber	if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
713235537Sgber		return (error);
714235537Sgber
715235537Sgber	/*
716235537Sgber	 * Privileged processes may set the sticky bit on non-directories,
717235537Sgber	 * as well as set the setgid bit on a file with a group that the
718235537Sgber	 * process is not a member of. Both of these are allowed in
719235537Sgber	 * jail(8).
720235537Sgber	 */
721235537Sgber	if (vp->v_type != VDIR && (mode & S_ISTXT)) {
722235537Sgber		if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
723235537Sgber			return (EFTYPE);
724235537Sgber	}
725235537Sgber	if (!groupmember(inode->i_gid, cred) && (mode & ISGID)) {
726235537Sgber		error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
727235537Sgber		if (error)
728235537Sgber			return (error);
729235537Sgber	}
730235537Sgber
731235537Sgber	/*
732235537Sgber	 * Deny setting setuid if we are not the file owner.
733235537Sgber	 */
734235537Sgber	if ((mode & ISUID) && inode->i_uid != cred->cr_uid) {
735235537Sgber		error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
736235537Sgber		if (error)
737235537Sgber			return (error);
738235537Sgber	}
739235537Sgber
740235537Sgber	nmode = inode->i_mode;
741235537Sgber	nmode &= ~ALLPERMS;
742235537Sgber	nmode |= (mode & ALLPERMS);
743235537Sgber	inode->i_mode = nmode;
744235537Sgber	node->nn_flags |= IN_CHANGE;
745235537Sgber
746235537Sgber	DPRINTF(VNCALL, ("%s: to mode %x\n", __func__, nmode));
747235537Sgber
748235537Sgber	return (error);
749235537Sgber}
750235537Sgber
751235537Sgberstatic int
752235537Sgbernandfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
753235537Sgber    struct thread *td)
754235537Sgber{
755235537Sgber	struct nandfs_node *node = VTON(vp);
756235537Sgber	struct nandfs_inode *inode = &node->nn_inode;
757235537Sgber	uid_t ouid;
758235537Sgber	gid_t ogid;
759235537Sgber	int error = 0;
760235537Sgber
761235537Sgber	if (uid == (uid_t)VNOVAL)
762235537Sgber		uid = inode->i_uid;
763235537Sgber	if (gid == (gid_t)VNOVAL)
764235537Sgber		gid = inode->i_gid;
765235537Sgber	/*
766235537Sgber	 * To modify the ownership of a file, must possess VADMIN for that
767235537Sgber	 * file.
768235537Sgber	 */
769235537Sgber	if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td)))
770235537Sgber		return (error);
771235537Sgber	/*
772235537Sgber	 * To change the owner of a file, or change the group of a file to a
773235537Sgber	 * group of which we are not a member, the caller must have
774235537Sgber	 * privilege.
775235537Sgber	 */
776235537Sgber	if (((uid != inode->i_uid && uid != cred->cr_uid) ||
777235537Sgber	    (gid != inode->i_gid && !groupmember(gid, cred))) &&
778235537Sgber	    (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
779235537Sgber		return (error);
780235537Sgber	ogid = inode->i_gid;
781235537Sgber	ouid = inode->i_uid;
782235537Sgber
783235537Sgber	inode->i_gid = gid;
784235537Sgber	inode->i_uid = uid;
785235537Sgber
786235537Sgber	node->nn_flags |= IN_CHANGE;
787235537Sgber	if ((inode->i_mode & (ISUID | ISGID)) &&
788235537Sgber	    (ouid != uid || ogid != gid)) {
789269018Simp		if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0))
790235537Sgber			inode->i_mode &= ~(ISUID | ISGID);
791235537Sgber	}
792235537Sgber	DPRINTF(VNCALL, ("%s: vp %p, cred %p, td %p - ret OK\n", __func__, vp,
793235537Sgber	    cred, td));
794235537Sgber	return (0);
795235537Sgber}
796235537Sgber
797235537Sgberstatic int
798235537Sgbernandfs_setattr(struct vop_setattr_args *ap)
799235537Sgber{
800235537Sgber	struct vnode *vp = ap->a_vp;
801235537Sgber	struct nandfs_node *node = VTON(vp);
802235537Sgber	struct nandfs_inode *inode = &node->nn_inode;
803235537Sgber	struct vattr *vap = ap->a_vap;
804235537Sgber	struct ucred *cred = ap->a_cred;
805235537Sgber	struct thread *td = curthread;
806235537Sgber	uint32_t flags;
807235537Sgber	int error = 0;
808235537Sgber
809235537Sgber	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
810235537Sgber	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
811235537Sgber	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
812235537Sgber	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
813235537Sgber		DPRINTF(VNCALL, ("%s: unsettable attribute\n", __func__));
814235537Sgber		return (EINVAL);
815235537Sgber	}
816235537Sgber
817235537Sgber	if (vap->va_flags != VNOVAL) {
818235537Sgber		DPRINTF(VNCALL, ("%s: vp:%p td:%p flags:%lx\n", __func__, vp,
819235537Sgber		    td, vap->va_flags));
820235537Sgber
821235537Sgber		if (vp->v_mount->mnt_flag & MNT_RDONLY)
822235537Sgber			return (EROFS);
823235537Sgber		/*
824235537Sgber		 * Callers may only modify the file flags on objects they
825235537Sgber		 * have VADMIN rights for.
826235537Sgber		 */
827235537Sgber		if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
828235537Sgber			return (error);
829235537Sgber		/*
830235537Sgber		 * Unprivileged processes are not permitted to unset system
831235537Sgber		 * flags, or modify flags if any system flags are set.
832235537Sgber		 * Privileged non-jail processes may not modify system flags
833235537Sgber		 * if securelevel > 0 and any existing system flags are set.
834235537Sgber		 * Privileged jail processes behave like privileged non-jail
835235537Sgber		 * processes if the security.jail.chflags_allowed sysctl is
836235537Sgber		 * is non-zero; otherwise, they behave like unprivileged
837235537Sgber		 * processes.
838235537Sgber		 */
839235537Sgber
840235537Sgber		flags = inode->i_flags;
841235537Sgber		if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
842235537Sgber			if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
843235537Sgber				error = securelevel_gt(cred, 0);
844235537Sgber				if (error)
845235537Sgber					return (error);
846235537Sgber			}
847235537Sgber			/* Snapshot flag cannot be set or cleared */
848235537Sgber			if (((vap->va_flags & SF_SNAPSHOT) != 0 &&
849235537Sgber			    (flags & SF_SNAPSHOT) == 0) ||
850235537Sgber			    ((vap->va_flags & SF_SNAPSHOT) == 0 &&
851235537Sgber			    (flags & SF_SNAPSHOT) != 0))
852235537Sgber				return (EPERM);
853235537Sgber
854235537Sgber			inode->i_flags = vap->va_flags;
855235537Sgber		} else {
856235537Sgber			if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
857235537Sgber			    (vap->va_flags & UF_SETTABLE) != vap->va_flags)
858235537Sgber				return (EPERM);
859235537Sgber
860235537Sgber			flags &= SF_SETTABLE;
861235537Sgber			flags |= (vap->va_flags & UF_SETTABLE);
862235537Sgber			inode->i_flags = flags;
863235537Sgber		}
864235537Sgber		node->nn_flags |= IN_CHANGE;
865235537Sgber		if (vap->va_flags & (IMMUTABLE | APPEND))
866235537Sgber			return (0);
867235537Sgber	}
868235537Sgber	if (inode->i_flags & (IMMUTABLE | APPEND))
869235537Sgber		return (EPERM);
870235537Sgber
871235537Sgber	if (vap->va_size != (u_quad_t)VNOVAL) {
872235537Sgber		DPRINTF(VNCALL, ("%s: vp:%p td:%p size:%jx\n", __func__, vp, td,
873235537Sgber		    (uintmax_t)vap->va_size));
874235537Sgber
875235537Sgber		switch (vp->v_type) {
876235537Sgber		case VDIR:
877235537Sgber			return (EISDIR);
878235537Sgber		case VLNK:
879235537Sgber		case VREG:
880235537Sgber			if (vp->v_mount->mnt_flag & MNT_RDONLY)
881235537Sgber				return (EROFS);
882235537Sgber			if ((inode->i_flags & SF_SNAPSHOT) != 0)
883235537Sgber				return (EPERM);
884235537Sgber			break;
885235537Sgber		default:
886235537Sgber			return (0);
887235537Sgber		}
888235537Sgber
889235537Sgber		if (vap->va_size > node->nn_nandfsdev->nd_maxfilesize)
890235537Sgber			return (EFBIG);
891235537Sgber
892235537Sgber		KASSERT((vp->v_type == VREG), ("Set size %d", vp->v_type));
893235537Sgber		nandfs_truncate(vp, vap->va_size);
894235537Sgber		node->nn_flags |= IN_CHANGE;
895235537Sgber
896235537Sgber		return (0);
897235537Sgber	}
898235537Sgber
899235537Sgber	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
900235537Sgber		if (vp->v_mount->mnt_flag & MNT_RDONLY)
901235537Sgber			return (EROFS);
902235537Sgber		DPRINTF(VNCALL, ("%s: vp:%p td:%p uid/gid %x/%x\n", __func__,
903235537Sgber		    vp, td, vap->va_uid, vap->va_gid));
904235537Sgber		error = nandfs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
905235537Sgber		if (error)
906235537Sgber			return (error);
907235537Sgber	}
908235537Sgber
909235537Sgber	if (vap->va_mode != (mode_t)VNOVAL) {
910235537Sgber		if (vp->v_mount->mnt_flag & MNT_RDONLY)
911235537Sgber			return (EROFS);
912235537Sgber		DPRINTF(VNCALL, ("%s: vp:%p td:%p mode %x\n", __func__, vp, td,
913235537Sgber		    vap->va_mode));
914235537Sgber
915235537Sgber		error = nandfs_chmod(vp, (int)vap->va_mode, cred, td);
916235537Sgber		if (error)
917235537Sgber			return (error);
918235537Sgber	}
919235537Sgber	if (vap->va_atime.tv_sec != VNOVAL ||
920235537Sgber	    vap->va_mtime.tv_sec != VNOVAL ||
921235537Sgber	    vap->va_birthtime.tv_sec != VNOVAL) {
922235537Sgber		DPRINTF(VNCALL, ("%s: vp:%p td:%p time a/m/b %jx/%jx/%jx\n",
923235537Sgber		    __func__, vp, td, (uintmax_t)vap->va_atime.tv_sec,
924235537Sgber		    (uintmax_t)vap->va_mtime.tv_sec,
925235537Sgber		    (uintmax_t)vap->va_birthtime.tv_sec));
926235537Sgber
927235537Sgber		if (vap->va_atime.tv_sec != VNOVAL)
928235537Sgber			node->nn_flags |= IN_ACCESS;
929235537Sgber		if (vap->va_mtime.tv_sec != VNOVAL)
930235537Sgber			node->nn_flags |= IN_CHANGE | IN_UPDATE;
931235537Sgber		if (vap->va_birthtime.tv_sec != VNOVAL)
932235537Sgber			node->nn_flags |= IN_MODIFIED;
933235537Sgber		nandfs_itimes(vp);
934235537Sgber		return (0);
935235537Sgber	}
936235537Sgber
937235537Sgber	return (0);
938235537Sgber}
939235537Sgber
940235537Sgberstatic int
941235537Sgbernandfs_open(struct vop_open_args *ap)
942235537Sgber{
943235537Sgber	struct nandfs_node *node = VTON(ap->a_vp);
944235537Sgber	uint64_t filesize;
945235537Sgber
946235537Sgber	DPRINTF(VNCALL, ("nandfs_open called ap->a_mode %x\n", ap->a_mode));
947235537Sgber
948235537Sgber	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
949235537Sgber		return (EOPNOTSUPP);
950235537Sgber
951235537Sgber	if ((node->nn_inode.i_flags & APPEND) &&
952235537Sgber	    (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
953235537Sgber		return (EPERM);
954235537Sgber
955235537Sgber	filesize = node->nn_inode.i_size;
956235537Sgber	vnode_create_vobject(ap->a_vp, filesize, ap->a_td);
957235537Sgber
958235537Sgber	return (0);
959235537Sgber}
960235537Sgber
961235537Sgberstatic int
962235537Sgbernandfs_close(struct vop_close_args *ap)
963235537Sgber{
964235537Sgber	struct vnode *vp = ap->a_vp;
965235537Sgber	struct nandfs_node *node = VTON(vp);
966235537Sgber
967235537Sgber	DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node));
968235537Sgber
969235537Sgber	mtx_lock(&vp->v_interlock);
970235537Sgber	if (vp->v_usecount > 1)
971235537Sgber		nandfs_itimes_locked(vp);
972235537Sgber	mtx_unlock(&vp->v_interlock);
973235537Sgber
974235537Sgber	return (0);
975235537Sgber}
976235537Sgber
977235537Sgberstatic int
978235537Sgbernandfs_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode)
979235537Sgber{
980235537Sgber
981235537Sgber	/* Check if we are allowed to write */
982235537Sgber	switch (vap->va_type) {
983235537Sgber	case VDIR:
984235537Sgber	case VLNK:
985235537Sgber	case VREG:
986235537Sgber		/*
987235537Sgber		 * Normal nodes: check if we're on a read-only mounted
988235537Sgber		 * filingsystem and bomb out if we're trying to write.
989235537Sgber		 */
990269421Simp		if ((mode & VMODIFY_PERMS) && (vp->v_mount->mnt_flag & MNT_RDONLY))
991235537Sgber			return (EROFS);
992235537Sgber		break;
993235537Sgber	case VBLK:
994235537Sgber	case VCHR:
995235537Sgber	case VSOCK:
996235537Sgber	case VFIFO:
997235537Sgber		/*
998235537Sgber		 * Special nodes: even on read-only mounted filingsystems
999235537Sgber		 * these are allowed to be written to if permissions allow.
1000235537Sgber		 */
1001235537Sgber		break;
1002235537Sgber	default:
1003235537Sgber		/* No idea what this is */
1004235537Sgber		return (EINVAL);
1005235537Sgber	}
1006235537Sgber
1007269018Simp	/* No one may write immutable files */
1008235537Sgber	if ((mode & VWRITE) && (VTON(vp)->nn_inode.i_flags & IMMUTABLE))
1009235537Sgber		return (EPERM);
1010235537Sgber
1011235537Sgber	return (0);
1012235537Sgber}
1013235537Sgber
1014235537Sgberstatic int
1015235537Sgbernandfs_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode,
1016235537Sgber    struct ucred *cred)
1017235537Sgber{
1018235537Sgber
1019235537Sgber	return (vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid, mode,
1020235537Sgber	    cred, NULL));
1021235537Sgber}
1022235537Sgber
1023235537Sgberstatic int
1024235537Sgbernandfs_advlock(struct vop_advlock_args *ap)
1025235537Sgber{
1026235537Sgber	struct nandfs_node *nvp;
1027235537Sgber	quad_t size;
1028235537Sgber
1029235537Sgber	nvp = VTON(ap->a_vp);
1030235537Sgber	size = nvp->nn_inode.i_size;
1031235537Sgber	return (lf_advlock(ap, &(nvp->nn_lockf), size));
1032235537Sgber}
1033235537Sgber
1034235537Sgberstatic int
1035235537Sgbernandfs_access(struct vop_access_args *ap)
1036235537Sgber{
1037235537Sgber	struct vnode *vp = ap->a_vp;
1038235537Sgber	accmode_t accmode = ap->a_accmode;
1039235537Sgber	struct ucred *cred = ap->a_cred;
1040235537Sgber	struct vattr vap;
1041235537Sgber	int error;
1042235537Sgber
1043235537Sgber	DPRINTF(VNCALL, ("%s: vp:%p mode: %x\n", __func__, vp, accmode));
1044235537Sgber
1045235537Sgber	error = VOP_GETATTR(vp, &vap, NULL);
1046235537Sgber	if (error)
1047235537Sgber		return (error);
1048235537Sgber
1049235537Sgber	error = nandfs_check_possible(vp, &vap, accmode);
1050269018Simp	if (error)
1051235537Sgber		return (error);
1052235537Sgber
1053235537Sgber	error = nandfs_check_permitted(vp, &vap, accmode, cred);
1054235537Sgber
1055235537Sgber	return (error);
1056235537Sgber}
1057235537Sgber
1058235537Sgberstatic int
1059235537Sgbernandfs_print(struct vop_print_args *ap)
1060235537Sgber{
1061235537Sgber	struct vnode *vp = ap->a_vp;
1062235537Sgber	struct nandfs_node *nvp = VTON(vp);
1063235537Sgber
1064235537Sgber	printf("\tvp=%p, nandfs_node=%p\n", vp, nvp);
1065235537Sgber	printf("nandfs inode %#jx\n", (uintmax_t)nvp->nn_ino);
1066235537Sgber	printf("flags = 0x%b\n", (u_int)nvp->nn_flags, PRINT_NODE_FLAGS);
1067235537Sgber
1068235537Sgber	return (0);
1069235537Sgber}
1070235537Sgber
1071235537Sgberstatic void
1072235537Sgbernandfs_read_filebuf(struct nandfs_node *node, struct buf *bp)
1073235537Sgber{
1074235537Sgber	struct nandfs_device *nandfsdev = node->nn_nandfsdev;
1075235537Sgber	struct buf *nbp;
1076235537Sgber	nandfs_daddr_t vblk, pblk;
1077235537Sgber	nandfs_lbn_t from;
1078235537Sgber	uint32_t blocksize;
1079235537Sgber	int error = 0;
1080235537Sgber	int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
1081235537Sgber
1082235537Sgber	/*
1083235537Sgber	 * Translate all the block sectors into a series of buffers to read
1084235537Sgber	 * asynchronously from the nandfs device. Note that this lookup may
1085235537Sgber	 * induce readin's too.
1086235537Sgber	 */
1087235537Sgber
1088235537Sgber	blocksize = nandfsdev->nd_blocksize;
1089235537Sgber	if (bp->b_bcount / blocksize != 1)
1090235537Sgber		panic("invalid b_count in bp %p\n", bp);
1091235537Sgber
1092235537Sgber	from = bp->b_blkno;
1093235537Sgber
1094235537Sgber	DPRINTF(READ, ("\tread in from inode %#jx blkno %#jx"
1095235537Sgber	    " count %#lx\n", (uintmax_t)node->nn_ino, from,
1096235537Sgber	    bp->b_bcount));
1097235537Sgber
1098235537Sgber	/* Get virtual block numbers for the vnode's buffer span */
1099235537Sgber	error = nandfs_bmap_lookup(node, from, &vblk);
1100235537Sgber	if (error) {
1101235537Sgber		bp->b_error = EINVAL;
1102235537Sgber		bp->b_ioflags |= BIO_ERROR;
1103235537Sgber		bufdone(bp);
1104235537Sgber		return;
1105235537Sgber	}
1106235537Sgber
1107235537Sgber	/* Translate virtual block numbers to physical block numbers */
1108235537Sgber	error = nandfs_vtop(node, vblk, &pblk);
1109235537Sgber	if (error) {
1110235537Sgber		bp->b_error = EINVAL;
1111235537Sgber		bp->b_ioflags |= BIO_ERROR;
1112235537Sgber		bufdone(bp);
1113235537Sgber		return;
1114235537Sgber	}
1115235537Sgber
1116235537Sgber	/* Issue translated blocks */
1117235537Sgber	bp->b_resid = bp->b_bcount;
1118235537Sgber
1119235537Sgber	/* Note virtual block 0 marks not mapped */
1120235537Sgber	if (vblk == 0) {
1121235537Sgber		vfs_bio_clrbuf(bp);
1122235537Sgber		bufdone(bp);
1123235537Sgber		return;
1124235537Sgber	}
1125235537Sgber
1126235537Sgber	nbp = bp;
1127235537Sgber	nbp->b_blkno = pblk * blk2dev;
1128235537Sgber	bp->b_iooffset = dbtob(nbp->b_blkno);
1129235537Sgber	MPASS(bp->b_iooffset >= 0);
1130235537Sgber	BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, nbp);
1131235537Sgber	nandfs_vblk_set(bp, vblk);
1132235537Sgber	DPRINTF(READ, ("read_filebuf : ino %#jx blk %#jx -> "
1133235537Sgber	    "%#jx -> %#jx [bp %p]\n", (uintmax_t)node->nn_ino,
1134235537Sgber	    (uintmax_t)(from), (uintmax_t)vblk,
1135235537Sgber	    (uintmax_t)pblk, nbp));
1136235537Sgber}
1137235537Sgber
1138235537Sgberstatic void
1139235537Sgbernandfs_write_filebuf(struct nandfs_node *node, struct buf *bp)
1140235537Sgber{
1141235537Sgber	struct nandfs_device *nandfsdev = node->nn_nandfsdev;
1142235537Sgber
1143235537Sgber	bp->b_iooffset = dbtob(bp->b_blkno);
1144235537Sgber	MPASS(bp->b_iooffset >= 0);
1145235537Sgber	BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, bp);
1146235537Sgber}
1147235537Sgber
1148235537Sgberstatic int
1149235537Sgbernandfs_strategy(struct vop_strategy_args *ap)
1150235537Sgber{
1151235537Sgber	struct vnode *vp = ap->a_vp;
1152235537Sgber	struct buf *bp = ap->a_bp;
1153235537Sgber	struct nandfs_node *node = VTON(vp);
1154235537Sgber
1155235537Sgber
1156235537Sgber	/* check if we ought to be here */
1157235537Sgber	KASSERT((vp->v_type != VBLK && vp->v_type != VCHR),
1158235537Sgber	    ("nandfs_strategy on type %d", vp->v_type));
1159235537Sgber
1160235537Sgber	/* Translate if needed and pass on */
1161235537Sgber	if (bp->b_iocmd == BIO_READ) {
1162235537Sgber		nandfs_read_filebuf(node, bp);
1163235537Sgber		return (0);
1164235537Sgber	}
1165235537Sgber
1166235537Sgber	/* Send to segment collector */
1167235537Sgber	nandfs_write_filebuf(node, bp);
1168235537Sgber	return (0);
1169235537Sgber}
1170235537Sgber
1171235537Sgberstatic int
1172235537Sgbernandfs_readdir(struct vop_readdir_args *ap)
1173235537Sgber{
1174235537Sgber	struct uio *uio = ap->a_uio;
1175235537Sgber	struct vnode *vp = ap->a_vp;
1176235537Sgber	struct nandfs_node *node = VTON(vp);
1177235537Sgber	struct nandfs_dir_entry *ndirent;
1178235537Sgber	struct dirent dirent;
1179235537Sgber	struct buf *bp;
1180235537Sgber	uint64_t file_size, diroffset, transoffset, blkoff;
1181235537Sgber	uint64_t blocknr;
1182235537Sgber	uint32_t blocksize = node->nn_nandfsdev->nd_blocksize;
1183235537Sgber	uint8_t *pos, name_len;
1184235537Sgber	int error;
1185235537Sgber
1186235537Sgber	DPRINTF(READDIR, ("nandfs_readdir called\n"));
1187235537Sgber
1188235537Sgber	if (vp->v_type != VDIR)
1189235537Sgber		return (ENOTDIR);
1190235537Sgber
1191235537Sgber	file_size = node->nn_inode.i_size;
1192235537Sgber	DPRINTF(READDIR, ("nandfs_readdir filesize %jd resid %zd\n",
1193235537Sgber	    (uintmax_t)file_size, uio->uio_resid ));
1194235537Sgber
1195235537Sgber	/* We are called just as long as we keep on pushing data in */
1196235537Sgber	error = 0;
1197235537Sgber	if ((uio->uio_offset < file_size) &&
1198235537Sgber	    (uio->uio_resid >= sizeof(struct dirent))) {
1199235537Sgber		diroffset = uio->uio_offset;
1200235537Sgber		transoffset = diroffset;
1201235537Sgber
1202235537Sgber		blocknr = diroffset / blocksize;
1203235537Sgber		blkoff = diroffset % blocksize;
1204235537Sgber		error = nandfs_bread(node, blocknr, NOCRED, 0, &bp);
1205235537Sgber		if (error) {
1206235537Sgber			brelse(bp);
1207235537Sgber			return (EIO);
1208235537Sgber		}
1209235537Sgber		while (diroffset < file_size) {
1210235537Sgber			DPRINTF(READDIR, ("readdir : offset = %"PRIu64"\n",
1211235537Sgber			    diroffset));
1212235537Sgber			if (blkoff >= blocksize) {
1213235537Sgber				blkoff = 0; blocknr++;
1214235537Sgber				brelse(bp);
1215235537Sgber				error = nandfs_bread(node, blocknr, NOCRED, 0,
1216235537Sgber				    &bp);
1217235537Sgber				if (error) {
1218235537Sgber					brelse(bp);
1219235537Sgber					return (EIO);
1220235537Sgber				}
1221235537Sgber			}
1222235537Sgber
1223235537Sgber			/* Read in one dirent */
1224235537Sgber			pos = (uint8_t *)bp->b_data + blkoff;
1225235537Sgber			ndirent = (struct nandfs_dir_entry *)pos;
1226235537Sgber
1227235537Sgber			name_len = ndirent->name_len;
1228341074Smarkj			memset(&dirent, 0, sizeof(dirent));
1229235537Sgber			dirent.d_fileno = ndirent->inode;
1230235537Sgber			if (dirent.d_fileno) {
1231235537Sgber				dirent.d_type = ndirent->file_type;
1232235537Sgber				dirent.d_namlen = name_len;
1233235537Sgber				strncpy(dirent.d_name, ndirent->name, name_len);
1234235537Sgber				dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
1235341074Smarkj				dirent_terminate(&dirent);
1236235537Sgber				DPRINTF(READDIR, ("copying `%*.*s`\n", name_len,
1237235537Sgber				    name_len, dirent.d_name));
1238235537Sgber			}
1239235537Sgber
1240235537Sgber			/*
1241235537Sgber			 * If there isn't enough space in the uio to return a
1242235537Sgber			 * whole dirent, break off read
1243235537Sgber			 */
1244235537Sgber			if (uio->uio_resid < GENERIC_DIRSIZ(&dirent))
1245235537Sgber				break;
1246235537Sgber
1247235537Sgber			/* Transfer */
1248235537Sgber			if (dirent.d_fileno)
1249341074Smarkj				uiomove(&dirent, dirent.d_reclen, uio);
1250235537Sgber
1251235537Sgber			/* Advance */
1252235537Sgber			diroffset += ndirent->rec_len;
1253235537Sgber			blkoff += ndirent->rec_len;
1254235537Sgber
1255298806Spfg			/* Remember the last entry we transferred */
1256235537Sgber			transoffset = diroffset;
1257235537Sgber		}
1258235537Sgber		brelse(bp);
1259235537Sgber
1260298806Spfg		/* Pass on last transferred offset */
1261235537Sgber		uio->uio_offset = transoffset;
1262235537Sgber	}
1263235537Sgber
1264235537Sgber	if (ap->a_eofflag)
1265235537Sgber		*ap->a_eofflag = (uio->uio_offset >= file_size);
1266235537Sgber
1267235537Sgber	return (error);
1268235537Sgber}
1269235537Sgber
1270235537Sgberstatic int
1271235537Sgbernandfs_dirempty(struct vnode *dvp, uint64_t parentino, struct ucred *cred)
1272235537Sgber{
1273235537Sgber	struct nandfs_node *dnode = VTON(dvp);
1274235537Sgber	struct nandfs_dir_entry *dirent;
1275235537Sgber	uint64_t file_size = dnode->nn_inode.i_size;
1276235537Sgber	uint64_t blockcount = dnode->nn_inode.i_blocks;
1277235537Sgber	uint64_t blocknr;
1278235537Sgber	uint32_t blocksize = dnode->nn_nandfsdev->nd_blocksize;
1279235537Sgber	uint32_t limit;
1280235537Sgber	uint32_t off;
1281235537Sgber	uint8_t	*pos;
1282235537Sgber	struct buf *bp;
1283235537Sgber	int error;
1284235537Sgber
1285235537Sgber	DPRINTF(LOOKUP, ("%s: dvp %p parentino %#jx cred %p\n", __func__, dvp,
1286235537Sgber	    (uintmax_t)parentino, cred));
1287235537Sgber
1288235537Sgber	KASSERT((file_size != 0), ("nandfs_dirempty for NULL dir %p", dvp));
1289235537Sgber
1290235537Sgber	blocknr = 0;
1291235537Sgber	while (blocknr < blockcount) {
1292235537Sgber		error = nandfs_bread(dnode, blocknr, NOCRED, 0, &bp);
1293235537Sgber		if (error) {
1294235537Sgber			brelse(bp);
1295235537Sgber			return (0);
1296235537Sgber		}
1297235537Sgber
1298235537Sgber		pos = (uint8_t *)bp->b_data;
1299235537Sgber		off = 0;
1300235537Sgber
1301235537Sgber		if (blocknr == (blockcount - 1))
1302235537Sgber			limit = file_size % blocksize;
1303235537Sgber		else
1304235537Sgber			limit = blocksize;
1305235537Sgber
1306235537Sgber		while (off < limit) {
1307235537Sgber			dirent = (struct nandfs_dir_entry *)(pos + off);
1308235537Sgber			off += dirent->rec_len;
1309235537Sgber
1310235537Sgber			if (dirent->inode == 0)
1311235537Sgber				continue;
1312235537Sgber
1313235537Sgber			switch (dirent->name_len) {
1314235537Sgber			case 0:
1315235537Sgber				break;
1316235537Sgber			case 1:
1317235537Sgber				if (dirent->name[0] != '.')
1318235537Sgber					goto notempty;
1319235537Sgber
1320235537Sgber				KASSERT(dirent->inode == dnode->nn_ino,
1321235537Sgber				    (".'s inode does not match dir"));
1322235537Sgber				break;
1323235537Sgber			case 2:
1324235537Sgber				if (dirent->name[0] != '.' &&
1325235537Sgber				    dirent->name[1] != '.')
1326235537Sgber					goto notempty;
1327235537Sgber
1328235537Sgber				KASSERT(dirent->inode == parentino,
1329235537Sgber				    ("..'s inode does not match parent"));
1330235537Sgber				break;
1331235537Sgber			default:
1332235537Sgber				goto notempty;
1333235537Sgber			}
1334235537Sgber		}
1335235537Sgber
1336235537Sgber		brelse(bp);
1337235537Sgber		blocknr++;
1338235537Sgber	}
1339235537Sgber
1340235537Sgber	return (1);
1341235537Sgbernotempty:
1342235537Sgber	brelse(bp);
1343235537Sgber	return (0);
1344235537Sgber}
1345235537Sgber
1346235537Sgberstatic int
1347235537Sgbernandfs_link(struct vop_link_args *ap)
1348235537Sgber{
1349235537Sgber	struct vnode *tdvp = ap->a_tdvp;
1350235537Sgber	struct vnode *vp = ap->a_vp;
1351235537Sgber	struct componentname *cnp = ap->a_cnp;
1352235537Sgber	struct nandfs_node *node = VTON(vp);
1353235537Sgber	struct nandfs_inode *inode = &node->nn_inode;
1354235537Sgber	int error;
1355235537Sgber
1356235537Sgber	if (inode->i_links_count >= LINK_MAX)
1357235537Sgber		return (EMLINK);
1358235537Sgber
1359235537Sgber	if (inode->i_flags & (IMMUTABLE | APPEND))
1360235537Sgber		return (EPERM);
1361235537Sgber
1362235537Sgber	/* Update link count */
1363235537Sgber	inode->i_links_count++;
1364235537Sgber
1365235537Sgber	/* Add dir entry */
1366235537Sgber	error = nandfs_add_dirent(tdvp, node->nn_ino, cnp->cn_nameptr,
1367235537Sgber	    cnp->cn_namelen, IFTODT(inode->i_mode));
1368235537Sgber	if (error) {
1369235537Sgber		inode->i_links_count--;
1370235537Sgber	}
1371235537Sgber
1372235537Sgber	node->nn_flags |= IN_CHANGE;
1373235537Sgber	nandfs_itimes(vp);
1374235537Sgber	DPRINTF(VNCALL, ("%s: tdvp %p vp %p cnp %p\n",
1375235537Sgber	    __func__, tdvp, vp, cnp));
1376235537Sgber
1377235537Sgber	return (0);
1378235537Sgber}
1379235537Sgber
1380235537Sgberstatic int
1381235537Sgbernandfs_create(struct vop_create_args *ap)
1382235537Sgber{
1383235537Sgber	struct vnode *dvp = ap->a_dvp;
1384235537Sgber	struct vnode **vpp = ap->a_vpp;
1385235537Sgber	struct componentname *cnp = ap->a_cnp;
1386235537Sgber	uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
1387235537Sgber	struct nandfs_node *dir_node = VTON(dvp);
1388235537Sgber	struct nandfsmount *nmp = dir_node->nn_nmp;
1389235537Sgber	struct nandfs_node *node;
1390235537Sgber	int error;
1391235537Sgber
1392235537Sgber	DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp));
1393235537Sgber
1394235537Sgber	if (nandfs_fs_full(dir_node->nn_nandfsdev))
1395235537Sgber		return (ENOSPC);
1396235537Sgber
1397235537Sgber	/* Create new vnode/inode */
1398235537Sgber	error = nandfs_node_create(nmp, &node, mode);
1399235537Sgber	if (error)
1400235537Sgber		return (error);
1401235537Sgber	node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
1402235537Sgber	node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
1403235537Sgber
1404235537Sgber	/* Add new dir entry */
1405235537Sgber	error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
1406235537Sgber	    cnp->cn_namelen, IFTODT(mode));
1407235537Sgber	if (error) {
1408235537Sgber		if (nandfs_node_destroy(node)) {
1409235537Sgber			nandfs_error("%s: error destroying node %p\n",
1410235537Sgber			    __func__, node);
1411235537Sgber		}
1412235537Sgber		return (error);
1413235537Sgber	}
1414235537Sgber	*vpp = NTOV(node);
1415276007Skib	if ((cnp->cn_flags & MAKEENTRY) != 0)
1416276007Skib		cache_enter(dvp, *vpp, cnp);
1417235537Sgber
1418235537Sgber	DPRINTF(VNCALL, ("created file vp %p nandnode %p ino %jx\n", *vpp, node,
1419235537Sgber	    (uintmax_t)node->nn_ino));
1420235537Sgber	return (0);
1421235537Sgber}
1422235537Sgber
1423235537Sgberstatic int
1424235537Sgbernandfs_remove(struct vop_remove_args *ap)
1425235537Sgber{
1426235537Sgber	struct vnode *vp = ap->a_vp;
1427235537Sgber	struct vnode *dvp = ap->a_dvp;
1428235537Sgber	struct nandfs_node *node = VTON(vp);
1429235537Sgber	struct nandfs_node *dnode = VTON(dvp);
1430235537Sgber	struct componentname *cnp = ap->a_cnp;
1431235537Sgber
1432235537Sgber	DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx link %d\n",
1433235537Sgber	    __func__, dvp, vp, node, (uintmax_t)node->nn_ino,
1434235537Sgber	    node->nn_inode.i_links_count));
1435235537Sgber
1436235537Sgber	if (vp->v_type == VDIR)
1437235537Sgber		return (EISDIR);
1438235537Sgber
1439235537Sgber	/* Files marked as immutable or append-only cannot be deleted. */
1440235537Sgber	if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
1441235537Sgber	    (dnode->nn_inode.i_flags & APPEND))
1442235537Sgber		return (EPERM);
1443235537Sgber
1444235537Sgber	nandfs_remove_dirent(dvp, node, cnp);
1445235537Sgber	node->nn_inode.i_links_count--;
1446235537Sgber	node->nn_flags |= IN_CHANGE;
1447235537Sgber
1448235537Sgber	return (0);
1449235537Sgber}
1450235537Sgber
1451235537Sgber/*
1452235537Sgber * Check if source directory is in the path of the target directory.
1453235537Sgber * Target is supplied locked, source is unlocked.
1454235537Sgber * The target is always vput before returning.
1455235537Sgber */
1456235537Sgberstatic int
1457235537Sgbernandfs_checkpath(struct nandfs_node *src, struct nandfs_node *dest,
1458235537Sgber    struct ucred *cred)
1459235537Sgber{
1460235537Sgber	struct vnode *vp;
1461235537Sgber	int error, rootino;
1462235537Sgber	struct nandfs_dir_entry dirent;
1463235537Sgber
1464235537Sgber	vp = NTOV(dest);
1465235537Sgber	if (src->nn_ino == dest->nn_ino) {
1466235537Sgber		error = EEXIST;
1467235537Sgber		goto out;
1468235537Sgber	}
1469235537Sgber	rootino = NANDFS_ROOT_INO;
1470235537Sgber	error = 0;
1471235537Sgber	if (dest->nn_ino == rootino)
1472235537Sgber		goto out;
1473235537Sgber
1474235537Sgber	for (;;) {
1475235537Sgber		if (vp->v_type != VDIR) {
1476235537Sgber			error = ENOTDIR;
1477235537Sgber			break;
1478235537Sgber		}
1479235537Sgber
1480235537Sgber		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirent,
1481235537Sgber		    NANDFS_DIR_REC_LEN(2), (off_t)0, UIO_SYSSPACE,
1482235537Sgber		    IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED,
1483235537Sgber		    NULL, NULL);
1484235537Sgber		if (error != 0)
1485235537Sgber			break;
1486235537Sgber		if (dirent.name_len != 2 ||
1487235537Sgber		    dirent.name[0] != '.' ||
1488235537Sgber		    dirent.name[1] != '.') {
1489235537Sgber			error = ENOTDIR;
1490235537Sgber			break;
1491235537Sgber		}
1492235537Sgber		if (dirent.inode == src->nn_ino) {
1493235537Sgber			error = EINVAL;
1494235537Sgber			break;
1495235537Sgber		}
1496235537Sgber		if (dirent.inode == rootino)
1497235537Sgber			break;
1498235537Sgber		vput(vp);
1499235537Sgber		if ((error = VFS_VGET(vp->v_mount, dirent.inode,
1500235537Sgber		    LK_EXCLUSIVE, &vp)) != 0) {
1501235537Sgber			vp = NULL;
1502235537Sgber			break;
1503235537Sgber		}
1504235537Sgber	}
1505235537Sgber
1506235537Sgberout:
1507235537Sgber	if (error == ENOTDIR)
1508235537Sgber		printf("checkpath: .. not a directory\n");
1509235537Sgber	if (vp != NULL)
1510235537Sgber		vput(vp);
1511235537Sgber	return (error);
1512235537Sgber}
1513235537Sgber
1514235537Sgberstatic int
1515235537Sgbernandfs_rename(struct vop_rename_args *ap)
1516235537Sgber{
1517235537Sgber	struct vnode *tvp = ap->a_tvp;
1518235537Sgber	struct vnode *tdvp = ap->a_tdvp;
1519235537Sgber	struct vnode *fvp = ap->a_fvp;
1520235537Sgber	struct vnode *fdvp = ap->a_fdvp;
1521235537Sgber	struct componentname *tcnp = ap->a_tcnp;
1522235537Sgber	struct componentname *fcnp = ap->a_fcnp;
1523235537Sgber	int doingdirectory = 0, oldparent = 0, newparent = 0;
1524235537Sgber	int error = 0;
1525235537Sgber
1526235537Sgber	struct nandfs_node *fdnode, *fnode, *fnode1;
1527235537Sgber	struct nandfs_node *tdnode = VTON(tdvp);
1528235537Sgber	struct nandfs_node *tnode;
1529235537Sgber
1530235537Sgber	uint32_t tdflags, fflags, fdflags;
1531235537Sgber	uint16_t mode;
1532235537Sgber
1533235537Sgber	DPRINTF(VNCALL, ("%s: fdvp:%p fvp:%p tdvp:%p tdp:%p\n", __func__, fdvp,
1534235537Sgber	    fvp, tdvp, tvp));
1535235537Sgber
1536235537Sgber	/*
1537235537Sgber	 * Check for cross-device rename.
1538235537Sgber	 */
1539235537Sgber	if ((fvp->v_mount != tdvp->v_mount) ||
1540235537Sgber	    (tvp && (fvp->v_mount != tvp->v_mount))) {
1541235537Sgber		error = EXDEV;
1542235537Sgberabortit:
1543235537Sgber		if (tdvp == tvp)
1544235537Sgber			vrele(tdvp);
1545235537Sgber		else
1546235537Sgber			vput(tdvp);
1547235537Sgber		if (tvp)
1548235537Sgber			vput(tvp);
1549235537Sgber		vrele(fdvp);
1550235537Sgber		vrele(fvp);
1551235537Sgber		return (error);
1552235537Sgber	}
1553235537Sgber
1554235537Sgber	tdflags = tdnode->nn_inode.i_flags;
1555235537Sgber	if (tvp &&
1556235537Sgber	    ((VTON(tvp)->nn_inode.i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
1557235537Sgber	    (tdflags & APPEND))) {
1558235537Sgber		error = EPERM;
1559235537Sgber		goto abortit;
1560235537Sgber	}
1561235537Sgber
1562235537Sgber	/*
1563235537Sgber	 * Renaming a file to itself has no effect.  The upper layers should
1564235537Sgber	 * not call us in that case.  Temporarily just warn if they do.
1565235537Sgber	 */
1566235537Sgber	if (fvp == tvp) {
1567235537Sgber		printf("nandfs_rename: fvp == tvp (can't happen)\n");
1568235537Sgber		error = 0;
1569235537Sgber		goto abortit;
1570235537Sgber	}
1571235537Sgber
1572235537Sgber	if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
1573235537Sgber		goto abortit;
1574235537Sgber
1575235537Sgber	fdnode = VTON(fdvp);
1576235537Sgber	fnode = VTON(fvp);
1577235537Sgber
1578235537Sgber	if (fnode->nn_inode.i_links_count >= LINK_MAX) {
1579235537Sgber		VOP_UNLOCK(fvp, 0);
1580235537Sgber		error = EMLINK;
1581235537Sgber		goto abortit;
1582235537Sgber	}
1583235537Sgber
1584235537Sgber	fflags = fnode->nn_inode.i_flags;
1585235537Sgber	fdflags = fdnode->nn_inode.i_flags;
1586235537Sgber
1587235537Sgber	if ((fflags & (NOUNLINK | IMMUTABLE | APPEND)) ||
1588235537Sgber	    (fdflags & APPEND)) {
1589235537Sgber		VOP_UNLOCK(fvp, 0);
1590235537Sgber		error = EPERM;
1591235537Sgber		goto abortit;
1592235537Sgber	}
1593235537Sgber
1594235537Sgber	mode = fnode->nn_inode.i_mode;
1595235537Sgber	if ((mode & S_IFMT) == S_IFDIR) {
1596235537Sgber		/*
1597235537Sgber		 * Avoid ".", "..", and aliases of "." for obvious reasons.
1598235537Sgber		 */
1599235537Sgber
1600235537Sgber		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
1601235537Sgber		    (fdvp == fvp) ||
1602235537Sgber		    ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT) ||
1603235537Sgber		    (fnode->nn_flags & IN_RENAME)) {
1604235537Sgber			VOP_UNLOCK(fvp, 0);
1605235537Sgber			error = EINVAL;
1606235537Sgber			goto abortit;
1607235537Sgber		}
1608235537Sgber		fnode->nn_flags |= IN_RENAME;
1609235537Sgber		doingdirectory = 1;
1610235537Sgber		DPRINTF(VNCALL, ("%s: doingdirectory dvp %p\n", __func__,
1611235537Sgber		    tdvp));
1612235537Sgber		oldparent = fdnode->nn_ino;
1613235537Sgber	}
1614235537Sgber
1615235537Sgber	vrele(fdvp);
1616235537Sgber
1617235537Sgber	tnode = NULL;
1618235537Sgber	if (tvp)
1619235537Sgber		tnode = VTON(tvp);
1620235537Sgber
1621235537Sgber	/*
1622235537Sgber	 * Bump link count on fvp while we are moving stuff around. If we
1623235537Sgber	 * crash before completing the work, the link count may be wrong
1624235537Sgber	 * but correctable.
1625235537Sgber	 */
1626235537Sgber	fnode->nn_inode.i_links_count++;
1627235537Sgber
1628235537Sgber	/* Check for in path moving XXX */
1629235537Sgber	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
1630235537Sgber	VOP_UNLOCK(fvp, 0);
1631235537Sgber	if (oldparent != tdnode->nn_ino)
1632235537Sgber		newparent = tdnode->nn_ino;
1633235537Sgber	if (doingdirectory && newparent) {
1634235537Sgber		if (error)	/* write access check above */
1635235537Sgber			goto bad;
1636235537Sgber		if (tnode != NULL)
1637235537Sgber			vput(tvp);
1638235537Sgber
1639235537Sgber		error = nandfs_checkpath(fnode, tdnode, tcnp->cn_cred);
1640235537Sgber		if (error)
1641235537Sgber			goto out;
1642235537Sgber
1643235537Sgber		VREF(tdvp);
1644235537Sgber		error = relookup(tdvp, &tvp, tcnp);
1645235537Sgber		if (error)
1646235537Sgber			goto out;
1647235537Sgber		vrele(tdvp);
1648235537Sgber		tdnode = VTON(tdvp);
1649235537Sgber		tnode = NULL;
1650235537Sgber		if (tvp)
1651235537Sgber			tnode = VTON(tvp);
1652235537Sgber	}
1653235537Sgber
1654235537Sgber	/*
1655235537Sgber	 * If the target doesn't exist, link the target to the source and
1656235537Sgber	 * unlink the source. Otherwise, rewrite the target directory to
1657235537Sgber	 * reference the source and remove the original entry.
1658235537Sgber	 */
1659235537Sgber
1660235537Sgber	if (tvp == NULL) {
1661235537Sgber		/*
1662235537Sgber		 * Account for ".." in new directory.
1663235537Sgber		 */
1664235537Sgber		if (doingdirectory && fdvp != tdvp)
1665235537Sgber			tdnode->nn_inode.i_links_count++;
1666235537Sgber
1667235537Sgber		DPRINTF(VNCALL, ("%s: new entry in dvp:%p\n", __func__, tdvp));
1668235537Sgber		/*
1669235537Sgber		 * Add name in new directory.
1670235537Sgber		 */
1671235537Sgber		error = nandfs_add_dirent(tdvp, fnode->nn_ino, tcnp->cn_nameptr,
1672235537Sgber		    tcnp->cn_namelen, IFTODT(fnode->nn_inode.i_mode));
1673235537Sgber		if (error) {
1674235537Sgber			if (doingdirectory && fdvp != tdvp)
1675235537Sgber				tdnode->nn_inode.i_links_count--;
1676235537Sgber			goto bad;
1677235537Sgber		}
1678235537Sgber
1679235537Sgber		vput(tdvp);
1680235537Sgber	} else {
1681235537Sgber		/*
1682235537Sgber		 * If the parent directory is "sticky", then the user must
1683235537Sgber		 * own the parent directory, or the destination of the rename,
1684235537Sgber		 * otherwise the destination may not be changed (except by
1685235537Sgber		 * root). This implements append-only directories.
1686235537Sgber		 */
1687235537Sgber		if ((tdnode->nn_inode.i_mode & S_ISTXT) &&
1688235537Sgber		    tcnp->cn_cred->cr_uid != 0 &&
1689235537Sgber		    tcnp->cn_cred->cr_uid != tdnode->nn_inode.i_uid &&
1690235537Sgber		    tnode->nn_inode.i_uid != tcnp->cn_cred->cr_uid) {
1691235537Sgber			error = EPERM;
1692235537Sgber			goto bad;
1693235537Sgber		}
1694235537Sgber		/*
1695235537Sgber		 * Target must be empty if a directory and have no links
1696235537Sgber		 * to it. Also, ensure source and target are compatible
1697235537Sgber		 * (both directories, or both not directories).
1698235537Sgber		 */
1699235537Sgber		mode = tnode->nn_inode.i_mode;
1700235537Sgber		if ((mode & S_IFMT) == S_IFDIR) {
1701235537Sgber			if (!nandfs_dirempty(tvp, tdnode->nn_ino,
1702235537Sgber			    tcnp->cn_cred)) {
1703235537Sgber				error = ENOTEMPTY;
1704235537Sgber				goto bad;
1705235537Sgber			}
1706235537Sgber			if (!doingdirectory) {
1707235537Sgber				error = ENOTDIR;
1708235537Sgber				goto bad;
1709235537Sgber			}
1710235537Sgber			/*
1711235537Sgber			 * Update name cache since directory is going away.
1712235537Sgber			 */
1713235537Sgber			cache_purge(tdvp);
1714235537Sgber		} else if (doingdirectory) {
1715235537Sgber			error = EISDIR;
1716235537Sgber			goto bad;
1717235537Sgber		}
1718235537Sgber
1719235537Sgber		DPRINTF(VNCALL, ("%s: update entry dvp:%p\n", __func__, tdvp));
1720235537Sgber		/*
1721235537Sgber		 * Change name tcnp in tdvp to point at fvp.
1722235537Sgber		 */
1723235537Sgber		error = nandfs_update_dirent(tdvp, fnode, tnode);
1724235537Sgber		if (error)
1725235537Sgber			goto bad;
1726235537Sgber
1727235537Sgber		if (doingdirectory && !newparent)
1728235537Sgber			tdnode->nn_inode.i_links_count--;
1729235537Sgber
1730235537Sgber		vput(tdvp);
1731235537Sgber
1732235537Sgber		tnode->nn_inode.i_links_count--;
1733235537Sgber		vput(tvp);
1734235537Sgber		tnode = NULL;
1735235537Sgber	}
1736235537Sgber
1737235537Sgber	/*
1738235537Sgber	 * Unlink the source.
1739235537Sgber	 */
1740235537Sgber	fcnp->cn_flags &= ~MODMASK;
1741235537Sgber	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1742235537Sgber	VREF(fdvp);
1743235537Sgber	error = relookup(fdvp, &fvp, fcnp);
1744235537Sgber	if (error == 0)
1745235537Sgber		vrele(fdvp);
1746235537Sgber	if (fvp != NULL) {
1747235537Sgber		fnode1 = VTON(fvp);
1748235537Sgber		fdnode = VTON(fdvp);
1749235537Sgber	} else {
1750235537Sgber		/*
1751235537Sgber		 * From name has disappeared.
1752235537Sgber		 */
1753235537Sgber		if (doingdirectory)
1754235537Sgber			panic("nandfs_rename: lost dir entry");
1755235537Sgber		vrele(ap->a_fvp);
1756235537Sgber		return (0);
1757235537Sgber	}
1758235537Sgber
1759235537Sgber	DPRINTF(VNCALL, ("%s: unlink source fnode:%p\n", __func__, fnode));
1760235537Sgber
1761235537Sgber	/*
1762235537Sgber	 * Ensure that the directory entry still exists and has not
1763235537Sgber	 * changed while the new name has been entered. If the source is
1764235537Sgber	 * a file then the entry may have been unlinked or renamed. In
1765235537Sgber	 * either case there is no further work to be done. If the source
1766235537Sgber	 * is a directory then it cannot have been rmdir'ed; its link
1767235537Sgber	 * count of three would cause a rmdir to fail with ENOTEMPTY.
1768235537Sgber	 * The IN_RENAME flag ensures that it cannot be moved by another
1769235537Sgber	 * rename.
1770235537Sgber	 */
1771235537Sgber	if (fnode != fnode1) {
1772235537Sgber		if (doingdirectory)
1773235537Sgber			panic("nandfs: lost dir entry");
1774235537Sgber	} else {
1775235537Sgber		/*
1776235537Sgber		 * If the source is a directory with a
1777235537Sgber		 * new parent, the link count of the old
1778235537Sgber		 * parent directory must be decremented
1779235537Sgber		 * and ".." set to point to the new parent.
1780235537Sgber		 */
1781235537Sgber		if (doingdirectory && newparent) {
1782235537Sgber			DPRINTF(VNCALL, ("%s: new parent %#jx -> %#jx\n",
1783235537Sgber			    __func__, (uintmax_t) oldparent,
1784235537Sgber			    (uintmax_t) newparent));
1785235537Sgber			error = nandfs_update_parent_dir(fvp, newparent);
1786235537Sgber			if (!error) {
1787235537Sgber				fdnode->nn_inode.i_links_count--;
1788235537Sgber				fdnode->nn_flags |= IN_CHANGE;
1789235537Sgber			}
1790235537Sgber		}
1791235537Sgber		error = nandfs_remove_dirent(fdvp, fnode, fcnp);
1792235537Sgber		if (!error) {
1793235537Sgber			fnode->nn_inode.i_links_count--;
1794235537Sgber			fnode->nn_flags |= IN_CHANGE;
1795235537Sgber		}
1796235537Sgber		fnode->nn_flags &= ~IN_RENAME;
1797235537Sgber	}
1798235537Sgber	if (fdnode)
1799235537Sgber		vput(fdvp);
1800235537Sgber	if (fnode)
1801235537Sgber		vput(fvp);
1802235537Sgber	vrele(ap->a_fvp);
1803235537Sgber	return (error);
1804235537Sgber
1805235537Sgberbad:
1806235537Sgber	DPRINTF(VNCALL, ("%s: error:%d\n", __func__, error));
1807235537Sgber	if (tnode)
1808235537Sgber		vput(NTOV(tnode));
1809235537Sgber	vput(NTOV(tdnode));
1810235537Sgberout:
1811235537Sgber	if (doingdirectory)
1812235537Sgber		fnode->nn_flags &= ~IN_RENAME;
1813235537Sgber	if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
1814235537Sgber		fnode->nn_inode.i_links_count--;
1815235537Sgber		fnode->nn_flags |= IN_CHANGE;
1816235537Sgber		fnode->nn_flags &= ~IN_RENAME;
1817235537Sgber		vput(fvp);
1818235537Sgber	} else
1819235537Sgber		vrele(fvp);
1820235537Sgber	return (error);
1821235537Sgber}
1822235537Sgber
1823235537Sgberstatic int
1824235537Sgbernandfs_mkdir(struct vop_mkdir_args *ap)
1825235537Sgber{
1826235537Sgber	struct vnode *dvp = ap->a_dvp;
1827235537Sgber	struct vnode **vpp = ap->a_vpp;
1828235537Sgber	struct componentname *cnp = ap->a_cnp;
1829235537Sgber	struct nandfs_node *dir_node = VTON(dvp);
1830235537Sgber	struct nandfs_inode *dir_inode = &dir_node->nn_inode;
1831235537Sgber	struct nandfs_node *node;
1832235537Sgber	struct nandfsmount *nmp = dir_node->nn_nmp;
1833235537Sgber	uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
1834235537Sgber	int error;
1835235537Sgber
1836235537Sgber	DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp));
1837235537Sgber
1838235537Sgber	if (nandfs_fs_full(dir_node->nn_nandfsdev))
1839235537Sgber		return (ENOSPC);
1840235537Sgber
1841235537Sgber	if (dir_inode->i_links_count >= LINK_MAX)
1842235537Sgber		return (EMLINK);
1843235537Sgber
1844235537Sgber	error = nandfs_node_create(nmp, &node, mode);
1845235537Sgber	if (error)
1846235537Sgber		return (error);
1847235537Sgber
1848235537Sgber	node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
1849235537Sgber	node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
1850235537Sgber
1851235537Sgber	*vpp = NTOV(node);
1852235537Sgber
1853235537Sgber	error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
1854235537Sgber	    cnp->cn_namelen, IFTODT(mode));
1855235537Sgber	if (error) {
1856235537Sgber		vput(*vpp);
1857235537Sgber		return (error);
1858235537Sgber	}
1859235537Sgber
1860235537Sgber	dir_node->nn_inode.i_links_count++;
1861235537Sgber	dir_node->nn_flags |= IN_CHANGE;
1862235537Sgber
1863235537Sgber	error = nandfs_init_dir(NTOV(node), node->nn_ino, dir_node->nn_ino);
1864235537Sgber	if (error) {
1865235537Sgber		vput(NTOV(node));
1866235537Sgber		return (error);
1867235537Sgber	}
1868235537Sgber
1869235537Sgber	DPRINTF(VNCALL, ("created dir vp %p nandnode %p ino %jx\n", *vpp, node,
1870235537Sgber	    (uintmax_t)node->nn_ino));
1871235537Sgber	return (0);
1872235537Sgber}
1873235537Sgber
1874235537Sgberstatic int
1875235537Sgbernandfs_mknod(struct vop_mknod_args *ap)
1876235537Sgber{
1877235537Sgber	struct vnode *dvp = ap->a_dvp;
1878235537Sgber	struct vnode **vpp = ap->a_vpp;
1879235537Sgber	struct vattr *vap = ap->a_vap;
1880235537Sgber	uint16_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
1881235537Sgber	struct componentname *cnp = ap->a_cnp;
1882235537Sgber	struct nandfs_node *dir_node = VTON(dvp);
1883235537Sgber	struct nandfsmount *nmp = dir_node->nn_nmp;
1884235537Sgber	struct nandfs_node *node;
1885235537Sgber	int error;
1886235537Sgber
1887235537Sgber	if (nandfs_fs_full(dir_node->nn_nandfsdev))
1888235537Sgber		return (ENOSPC);
1889235537Sgber
1890235537Sgber	error = nandfs_node_create(nmp, &node, mode);
1891235537Sgber	if (error)
1892235537Sgber		return (error);
1893235537Sgber	node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
1894235537Sgber	node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
1895235537Sgber	if (vap->va_rdev != VNOVAL)
1896235537Sgber		node->nn_inode.i_special = vap->va_rdev;
1897235537Sgber
1898235537Sgber	*vpp = NTOV(node);
1899235537Sgber
1900235537Sgber	if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
1901235537Sgber	    cnp->cn_namelen, IFTODT(mode))) {
1902235537Sgber		vput(*vpp);
1903235537Sgber		return (ENOTDIR);
1904235537Sgber	}
1905235537Sgber
1906235537Sgber	node->nn_flags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1907235537Sgber
1908235537Sgber	return (0);
1909235537Sgber}
1910235537Sgber
1911235537Sgberstatic int
1912235537Sgbernandfs_symlink(struct vop_symlink_args *ap)
1913235537Sgber{
1914235537Sgber	struct vnode **vpp = ap->a_vpp;
1915235537Sgber	struct vnode *dvp = ap->a_dvp;
1916235537Sgber	uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
1917235537Sgber	struct componentname *cnp = ap->a_cnp;
1918235537Sgber	struct nandfs_node *dir_node = VTON(dvp);
1919235537Sgber	struct nandfsmount *nmp = dir_node->nn_nmp;
1920235537Sgber	struct nandfs_node *node;
1921235537Sgber	int len, error;
1922235537Sgber
1923235537Sgber	if (nandfs_fs_full(dir_node->nn_nandfsdev))
1924235537Sgber		return (ENOSPC);
1925235537Sgber
1926235537Sgber	error = nandfs_node_create(nmp, &node, S_IFLNK | mode);
1927235537Sgber	if (error)
1928235537Sgber		return (error);
1929235537Sgber	node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
1930235537Sgber	node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
1931235537Sgber
1932235537Sgber	*vpp = NTOV(node);
1933235537Sgber
1934235537Sgber	if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
1935235537Sgber	    cnp->cn_namelen, IFTODT(mode))) {
1936235537Sgber		vput(*vpp);
1937235537Sgber		return (ENOTDIR);
1938235537Sgber	}
1939235537Sgber
1940235537Sgber
1941235537Sgber	len = strlen(ap->a_target);
1942235537Sgber	error = vn_rdwr(UIO_WRITE, *vpp, ap->a_target, len, (off_t)0,
1943235537Sgber	    UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
1944235537Sgber	    cnp->cn_cred, NOCRED, NULL, NULL);
1945235537Sgber	if (error)
1946235537Sgber		vput(*vpp);
1947235537Sgber
1948235537Sgber	return (error);
1949235537Sgber}
1950235537Sgber
1951235537Sgberstatic int
1952235537Sgbernandfs_readlink(struct vop_readlink_args *ap)
1953235537Sgber{
1954235537Sgber	struct vnode *vp = ap->a_vp;
1955235537Sgber
1956235537Sgber	return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
1957235537Sgber}
1958235537Sgber
1959235537Sgberstatic int
1960235537Sgbernandfs_rmdir(struct vop_rmdir_args *ap)
1961235537Sgber{
1962235537Sgber	struct vnode *vp = ap->a_vp;
1963235537Sgber	struct vnode *dvp = ap->a_dvp;
1964235537Sgber	struct componentname *cnp = ap->a_cnp;
1965235537Sgber	struct nandfs_node *node, *dnode;
1966235537Sgber	uint32_t dflag, flag;
1967235537Sgber	int error = 0;
1968235537Sgber
1969235537Sgber	node = VTON(vp);
1970235537Sgber	dnode = VTON(dvp);
1971235537Sgber
1972235537Sgber	/* Files marked as immutable or append-only cannot be deleted. */
1973235537Sgber	if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
1974235537Sgber	    (dnode->nn_inode.i_flags & APPEND))
1975235537Sgber		return (EPERM);
1976235537Sgber
1977235537Sgber	DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx\n", __func__,
1978235537Sgber	    dvp, vp, node, (uintmax_t)node->nn_ino));
1979235537Sgber
1980235537Sgber	if (node->nn_inode.i_links_count < 2)
1981235537Sgber		return (EINVAL);
1982235537Sgber
1983235537Sgber	if (!nandfs_dirempty(vp, dnode->nn_ino, cnp->cn_cred))
1984235537Sgber		return (ENOTEMPTY);
1985235537Sgber
1986235537Sgber	/* Files marked as immutable or append-only cannot be deleted. */
1987235537Sgber	dflag = dnode->nn_inode.i_flags;
1988235537Sgber	flag = node->nn_inode.i_flags;
1989235537Sgber	if ((dflag & APPEND) ||
1990235537Sgber	    (flag & (NOUNLINK | IMMUTABLE | APPEND))) {
1991235537Sgber		return (EPERM);
1992235537Sgber	}
1993235537Sgber
1994235537Sgber	if (vp->v_mountedhere != 0)
1995235537Sgber		return (EINVAL);
1996235537Sgber
1997235537Sgber	nandfs_remove_dirent(dvp, node, cnp);
1998235537Sgber	dnode->nn_inode.i_links_count -= 1;
1999235537Sgber	dnode->nn_flags |= IN_CHANGE;
2000235537Sgber
2001235537Sgber	cache_purge(dvp);
2002235537Sgber
2003235537Sgber	error = nandfs_truncate(vp, (uint64_t)0);
2004235537Sgber	if (error)
2005235537Sgber		return (error);
2006235537Sgber
2007235537Sgber	node->nn_inode.i_links_count -= 2;
2008235537Sgber	node->nn_flags |= IN_CHANGE;
2009235537Sgber
2010235537Sgber	cache_purge(vp);
2011235537Sgber
2012235537Sgber	return (error);
2013235537Sgber}
2014235537Sgber
2015235537Sgberstatic int
2016235537Sgbernandfs_fsync(struct vop_fsync_args *ap)
2017235537Sgber{
2018235537Sgber	struct vnode *vp = ap->a_vp;
2019235537Sgber	struct nandfs_node *node = VTON(vp);
2020235537Sgber	int locked;
2021235537Sgber
2022235537Sgber	DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp,
2023235537Sgber	    node, (uintmax_t)node->nn_ino));
2024235537Sgber
2025235537Sgber	/*
2026235537Sgber	 * Start syncing vnode only if inode was modified or
2027235537Sgber	 * there are some dirty buffers
2028235537Sgber	 */
2029235537Sgber	if (VTON(vp)->nn_flags & IN_MODIFIED ||
2030235537Sgber	    vp->v_bufobj.bo_dirty.bv_cnt) {
2031235537Sgber		locked = VOP_ISLOCKED(vp);
2032235537Sgber		VOP_UNLOCK(vp, 0);
2033235537Sgber		nandfs_wakeup_wait_sync(node->nn_nandfsdev, SYNCER_FSYNC);
2034235537Sgber		VOP_LOCK(vp, locked | LK_RETRY);
2035235537Sgber	}
2036235537Sgber
2037235537Sgber	return (0);
2038235537Sgber}
2039235537Sgber
2040235537Sgberstatic int
2041235537Sgbernandfs_bmap(struct vop_bmap_args *ap)
2042235537Sgber{
2043235537Sgber	struct vnode *vp = ap->a_vp;
2044235537Sgber	struct nandfs_node *nnode = VTON(vp);
2045235537Sgber	struct nandfs_device *nandfsdev = nnode->nn_nandfsdev;
2046235537Sgber	nandfs_daddr_t l2vmap, v2pmap;
2047235537Sgber	int error;
2048235537Sgber	int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
2049235537Sgber
2050235537Sgber	DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp,
2051235537Sgber	    nnode, (uintmax_t)nnode->nn_ino));
2052235537Sgber
2053235537Sgber	if (ap->a_bop != NULL)
2054235537Sgber		*ap->a_bop = &nandfsdev->nd_devvp->v_bufobj;
2055235537Sgber	if (ap->a_bnp == NULL)
2056235537Sgber		return (0);
2057235537Sgber	if (ap->a_runp != NULL)
2058235537Sgber		*ap->a_runp = 0;
2059235537Sgber	if (ap->a_runb != NULL)
2060235537Sgber		*ap->a_runb = 0;
2061235537Sgber
2062235537Sgber	/*
2063235537Sgber	 * Translate all the block sectors into a series of buffers to read
2064235537Sgber	 * asynchronously from the nandfs device. Note that this lookup may
2065235537Sgber	 * induce readin's too.
2066235537Sgber	 */
2067235537Sgber
2068235537Sgber	/* Get virtual block numbers for the vnode's buffer span */
2069235537Sgber	error = nandfs_bmap_lookup(nnode, ap->a_bn, &l2vmap);
2070235537Sgber	if (error)
2071235537Sgber		return (-1);
2072235537Sgber
2073235537Sgber	/* Translate virtual block numbers to physical block numbers */
2074235537Sgber	error = nandfs_vtop(nnode, l2vmap, &v2pmap);
2075235537Sgber	if (error)
2076235537Sgber		return (-1);
2077235537Sgber
2078235537Sgber	/* Note virtual block 0 marks not mapped */
2079235537Sgber	if (l2vmap == 0)
2080235537Sgber		*ap->a_bnp = -1;
2081235537Sgber	else
2082235537Sgber		*ap->a_bnp = v2pmap * blk2dev;	/* in DEV_BSIZE */
2083235537Sgber
2084235537Sgber	DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx lblk %jx -> blk %jx\n",
2085235537Sgber	    __func__, vp, nnode, (uintmax_t)nnode->nn_ino, (uintmax_t)ap->a_bn,
2086235537Sgber	    (uintmax_t)*ap->a_bnp ));
2087235537Sgber
2088235537Sgber	return (0);
2089235537Sgber}
2090235537Sgber
2091235537Sgberstatic void
2092235537Sgbernandfs_force_syncer(struct nandfsmount *nmp)
2093235537Sgber{
2094235537Sgber
2095235537Sgber	nmp->nm_flags |= NANDFS_FORCE_SYNCER;
2096235537Sgber	nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_FFORCE);
2097235537Sgber}
2098235537Sgber
2099235537Sgberstatic int
2100235537Sgbernandfs_ioctl(struct vop_ioctl_args *ap)
2101235537Sgber{
2102235537Sgber	struct vnode *vp = ap->a_vp;
2103235537Sgber	u_long command = ap->a_command;
2104235537Sgber	caddr_t data = ap->a_data;
2105235537Sgber	struct nandfs_node *node = VTON(vp);
2106235537Sgber	struct nandfs_device *nandfsdev = node->nn_nandfsdev;
2107235537Sgber	struct nandfsmount *nmp = node->nn_nmp;
2108235537Sgber	uint64_t *tab, *cno;
2109235537Sgber	struct nandfs_seg_stat *nss;
2110235537Sgber	struct nandfs_cpmode *ncpm;
2111235537Sgber	struct nandfs_argv *nargv;
2112235537Sgber	struct nandfs_cpstat *ncp;
2113235537Sgber	int error;
2114235537Sgber
2115235537Sgber	DPRINTF(VNCALL, ("%s: %x\n", __func__, (uint32_t)command));
2116235537Sgber
2117235537Sgber	error = priv_check(ap->a_td, PRIV_VFS_MOUNT);
2118235537Sgber	if (error)
2119235537Sgber		return (error);
2120235537Sgber
2121235537Sgber	if (nmp->nm_ronly) {
2122235537Sgber		switch (command) {
2123235537Sgber		case NANDFS_IOCTL_GET_FSINFO:
2124235537Sgber		case NANDFS_IOCTL_GET_SUSTAT:
2125235537Sgber		case NANDFS_IOCTL_GET_CPINFO:
2126235537Sgber		case NANDFS_IOCTL_GET_CPSTAT:
2127235537Sgber		case NANDFS_IOCTL_GET_SUINFO:
2128235537Sgber		case NANDFS_IOCTL_GET_VINFO:
2129235537Sgber		case NANDFS_IOCTL_GET_BDESCS:
2130235537Sgber			break;
2131235537Sgber		default:
2132235537Sgber			return (EROFS);
2133235537Sgber		}
2134235537Sgber	}
2135235537Sgber
2136235537Sgber	switch (command) {
2137235537Sgber	case NANDFS_IOCTL_GET_FSINFO:
2138235537Sgber		error = nandfs_get_fsinfo(nmp, (struct nandfs_fsinfo *)data);
2139235537Sgber		break;
2140235537Sgber	case NANDFS_IOCTL_GET_SUSTAT:
2141235537Sgber		nss = (struct nandfs_seg_stat *)data;
2142235537Sgber		error = nandfs_get_seg_stat(nandfsdev, nss);
2143235537Sgber		break;
2144235537Sgber	case NANDFS_IOCTL_CHANGE_CPMODE:
2145235537Sgber		ncpm = (struct nandfs_cpmode *)data;
2146235537Sgber		error = nandfs_chng_cpmode(nandfsdev->nd_cp_node, ncpm);
2147235537Sgber		nandfs_force_syncer(nmp);
2148235537Sgber		break;
2149235537Sgber	case NANDFS_IOCTL_GET_CPINFO:
2150235537Sgber		nargv = (struct nandfs_argv *)data;
2151235537Sgber		error = nandfs_get_cpinfo_ioctl(nandfsdev->nd_cp_node, nargv);
2152235537Sgber		break;
2153235537Sgber	case NANDFS_IOCTL_DELETE_CP:
2154235537Sgber		tab = (uint64_t *)data;
2155235537Sgber		error = nandfs_delete_cp(nandfsdev->nd_cp_node, tab[0], tab[1]);
2156235537Sgber		nandfs_force_syncer(nmp);
2157235537Sgber		break;
2158235537Sgber	case NANDFS_IOCTL_GET_CPSTAT:
2159235537Sgber		ncp = (struct nandfs_cpstat *)data;
2160235537Sgber		error = nandfs_get_cpstat(nandfsdev->nd_cp_node, ncp);
2161235537Sgber		break;
2162235537Sgber	case NANDFS_IOCTL_GET_SUINFO:
2163235537Sgber		nargv = (struct nandfs_argv *)data;
2164235537Sgber		error = nandfs_get_segment_info_ioctl(nandfsdev, nargv);
2165235537Sgber		break;
2166235537Sgber	case NANDFS_IOCTL_GET_VINFO:
2167235537Sgber		nargv = (struct nandfs_argv *)data;
2168235537Sgber		error = nandfs_get_dat_vinfo_ioctl(nandfsdev, nargv);
2169235537Sgber		break;
2170235537Sgber	case NANDFS_IOCTL_GET_BDESCS:
2171235537Sgber		nargv = (struct nandfs_argv *)data;
2172235537Sgber		error = nandfs_get_dat_bdescs_ioctl(nandfsdev, nargv);
2173235537Sgber		break;
2174235537Sgber	case NANDFS_IOCTL_SYNC:
2175235537Sgber		cno = (uint64_t *)data;
2176235537Sgber		nandfs_force_syncer(nmp);
2177235537Sgber		*cno = nandfsdev->nd_last_cno;
2178235537Sgber		error = 0;
2179235537Sgber		break;
2180235537Sgber	case NANDFS_IOCTL_MAKE_SNAP:
2181235537Sgber		cno = (uint64_t *)data;
2182235537Sgber		error = nandfs_make_snap(nandfsdev, cno);
2183235537Sgber		nandfs_force_syncer(nmp);
2184235537Sgber		break;
2185235537Sgber	case NANDFS_IOCTL_DELETE_SNAP:
2186235537Sgber		cno = (uint64_t *)data;
2187235537Sgber		error = nandfs_delete_snap(nandfsdev, *cno);
2188235537Sgber		nandfs_force_syncer(nmp);
2189235537Sgber		break;
2190235537Sgber	default:
2191235537Sgber		error = ENOTTY;
2192235537Sgber		break;
2193235537Sgber	}
2194235537Sgber
2195235537Sgber	return (error);
2196235537Sgber}
2197235537Sgber
2198235537Sgber/*
2199235537Sgber * Whiteout vnode call
2200235537Sgber */
2201235537Sgberstatic int
2202235537Sgbernandfs_whiteout(struct vop_whiteout_args *ap)
2203235537Sgber{
2204235537Sgber	struct vnode *dvp = ap->a_dvp;
2205235537Sgber	struct componentname *cnp = ap->a_cnp;
2206235537Sgber	int error = 0;
2207235537Sgber
2208235537Sgber	switch (ap->a_flags) {
2209235537Sgber	case LOOKUP:
2210235537Sgber		return (0);
2211235537Sgber	case CREATE:
2212235537Sgber		/* Create a new directory whiteout */
2213235537Sgber#ifdef INVARIANTS
2214235537Sgber		if ((cnp->cn_flags & SAVENAME) == 0)
2215235537Sgber			panic("ufs_whiteout: missing name");
2216235537Sgber#endif
2217235537Sgber		error = nandfs_add_dirent(dvp, NANDFS_WHT_INO, cnp->cn_nameptr,
2218235537Sgber		    cnp->cn_namelen, DT_WHT);
2219235537Sgber		break;
2220235537Sgber
2221235537Sgber	case DELETE:
2222235537Sgber		/* Remove an existing directory whiteout */
2223235537Sgber		cnp->cn_flags &= ~DOWHITEOUT;
2224235537Sgber		error = nandfs_remove_dirent(dvp, NULL, cnp);
2225235537Sgber		break;
2226235537Sgber	default:
2227235537Sgber		panic("nandf_whiteout: unknown op: %d", ap->a_flags);
2228235537Sgber	}
2229235537Sgber
2230235537Sgber	return (error);
2231235537Sgber}
2232235537Sgber
2233235537Sgberstatic int
2234235537Sgbernandfs_pathconf(struct vop_pathconf_args *ap)
2235235537Sgber{
2236235537Sgber	int error;
2237235537Sgber
2238235537Sgber	error = 0;
2239235537Sgber	switch (ap->a_name) {
2240235537Sgber	case _PC_LINK_MAX:
2241235537Sgber		*ap->a_retval = LINK_MAX;
2242235537Sgber		break;
2243235537Sgber	case _PC_NAME_MAX:
2244328298Sjhb		*ap->a_retval = NANDFS_NAME_LEN;
2245235537Sgber		break;
2246235537Sgber	case _PC_PIPE_BUF:
2247328298Sjhb		if (ap->a_vp->v_type == VDIR || ap->a_vp->v_type == VFIFO)
2248328298Sjhb			*ap->a_retval = PIPE_BUF;
2249328298Sjhb		else
2250328298Sjhb			error = EINVAL;
2251235537Sgber		break;
2252235537Sgber	case _PC_CHOWN_RESTRICTED:
2253235537Sgber		*ap->a_retval = 1;
2254235537Sgber		break;
2255235537Sgber	case _PC_NO_TRUNC:
2256235537Sgber		*ap->a_retval = 1;
2257235537Sgber		break;
2258235537Sgber	case _PC_ALLOC_SIZE_MIN:
2259235537Sgber		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize;
2260235537Sgber		break;
2261235537Sgber	case _PC_FILESIZEBITS:
2262235537Sgber		*ap->a_retval = 64;
2263235537Sgber		break;
2264235537Sgber	case _PC_REC_INCR_XFER_SIZE:
2265235537Sgber		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
2266235537Sgber		break;
2267235537Sgber	case _PC_REC_MAX_XFER_SIZE:
2268235537Sgber		*ap->a_retval = -1; /* means ``unlimited'' */
2269235537Sgber		break;
2270235537Sgber	case _PC_REC_MIN_XFER_SIZE:
2271235537Sgber		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
2272235537Sgber		break;
2273235537Sgber	default:
2274328298Sjhb		error = vop_stdpathconf(ap);
2275235537Sgber		break;
2276235537Sgber	}
2277235537Sgber	return (error);
2278235537Sgber}
2279235537Sgber
2280235537Sgberstatic int
2281235537Sgbernandfs_vnlock1(struct vop_lock1_args *ap)
2282235537Sgber{
2283235537Sgber	struct vnode *vp = ap->a_vp;
2284235537Sgber	struct nandfs_node *node = VTON(vp);
2285235537Sgber	int error, vi_locked;
2286235537Sgber
2287235537Sgber	/*
2288235537Sgber	 * XXX can vnode go away while we are sleeping?
2289235537Sgber	 */
2290235537Sgber	vi_locked = mtx_owned(&vp->v_interlock);
2291235537Sgber	if (vi_locked)
2292235537Sgber		VI_UNLOCK(vp);
2293235537Sgber	error = NANDFS_WRITELOCKFLAGS(node->nn_nandfsdev,
2294235537Sgber	    ap->a_flags & LK_NOWAIT);
2295235537Sgber	if (vi_locked && !error)
2296235537Sgber		VI_LOCK(vp);
2297235537Sgber	if (error)
2298235537Sgber		return (error);
2299235537Sgber
2300235537Sgber	error = vop_stdlock(ap);
2301235537Sgber	if (error) {
2302235537Sgber		NANDFS_WRITEUNLOCK(node->nn_nandfsdev);
2303235537Sgber		return (error);
2304235537Sgber	}
2305235537Sgber
2306235537Sgber	return (0);
2307235537Sgber}
2308235537Sgber
2309235537Sgberstatic int
2310235537Sgbernandfs_vnunlock(struct vop_unlock_args *ap)
2311235537Sgber{
2312235537Sgber	struct vnode *vp = ap->a_vp;
2313235537Sgber	struct nandfs_node *node = VTON(vp);
2314235537Sgber	int error;
2315235537Sgber
2316235537Sgber	error = vop_stdunlock(ap);
2317235537Sgber	if (error)
2318235537Sgber		return (error);
2319235537Sgber
2320235537Sgber	NANDFS_WRITEUNLOCK(node->nn_nandfsdev);
2321235537Sgber
2322235537Sgber	return (0);
2323235537Sgber}
2324235537Sgber
2325235537Sgber/*
2326235537Sgber * Global vfs data structures
2327235537Sgber */
2328235537Sgberstruct vop_vector nandfs_vnodeops = {
2329235537Sgber	.vop_default =		&default_vnodeops,
2330235537Sgber	.vop_access =		nandfs_access,
2331235537Sgber	.vop_advlock =		nandfs_advlock,
2332235537Sgber	.vop_bmap =		nandfs_bmap,
2333235537Sgber	.vop_close =		nandfs_close,
2334235537Sgber	.vop_create =		nandfs_create,
2335235537Sgber	.vop_fsync =		nandfs_fsync,
2336235537Sgber	.vop_getattr =		nandfs_getattr,
2337235537Sgber	.vop_inactive =		nandfs_inactive,
2338235537Sgber	.vop_cachedlookup =	nandfs_lookup,
2339235537Sgber	.vop_ioctl =		nandfs_ioctl,
2340235537Sgber	.vop_link =		nandfs_link,
2341235537Sgber	.vop_lookup =		vfs_cache_lookup,
2342235537Sgber	.vop_mkdir =		nandfs_mkdir,
2343235537Sgber	.vop_mknod =		nandfs_mknod,
2344235537Sgber	.vop_open =		nandfs_open,
2345235537Sgber	.vop_pathconf =		nandfs_pathconf,
2346235537Sgber	.vop_print =		nandfs_print,
2347235537Sgber	.vop_read =		nandfs_read,
2348235537Sgber	.vop_readdir =		nandfs_readdir,
2349235537Sgber	.vop_readlink =		nandfs_readlink,
2350235537Sgber	.vop_reclaim =		nandfs_reclaim,
2351235537Sgber	.vop_remove =		nandfs_remove,
2352235537Sgber	.vop_rename =		nandfs_rename,
2353235537Sgber	.vop_rmdir =		nandfs_rmdir,
2354235537Sgber	.vop_whiteout =		nandfs_whiteout,
2355235537Sgber	.vop_write =		nandfs_write,
2356235537Sgber	.vop_setattr =		nandfs_setattr,
2357235537Sgber	.vop_strategy =		nandfs_strategy,
2358235537Sgber	.vop_symlink =		nandfs_symlink,
2359235537Sgber	.vop_lock1 =		nandfs_vnlock1,
2360235537Sgber	.vop_unlock =		nandfs_vnunlock,
2361235537Sgber};
2362235537Sgber
2363235537Sgberstruct vop_vector nandfs_system_vnodeops = {
2364235537Sgber	.vop_default =		&default_vnodeops,
2365235537Sgber	.vop_close =		nandfs_close,
2366235537Sgber	.vop_inactive =		nandfs_inactive,
2367235537Sgber	.vop_reclaim =		nandfs_reclaim,
2368235537Sgber	.vop_strategy =		nandfs_strategy,
2369235537Sgber	.vop_fsync =		nandfs_fsync,
2370235537Sgber	.vop_bmap =		nandfs_bmap,
2371235537Sgber	.vop_access =		VOP_PANIC,
2372235537Sgber	.vop_advlock =		VOP_PANIC,
2373235537Sgber	.vop_create =		VOP_PANIC,
2374235537Sgber	.vop_getattr =		VOP_PANIC,
2375235537Sgber	.vop_cachedlookup =	VOP_PANIC,
2376235537Sgber	.vop_ioctl =		VOP_PANIC,
2377235537Sgber	.vop_link =		VOP_PANIC,
2378235537Sgber	.vop_lookup =		VOP_PANIC,
2379235537Sgber	.vop_mkdir =		VOP_PANIC,
2380235537Sgber	.vop_mknod =		VOP_PANIC,
2381235537Sgber	.vop_open =		VOP_PANIC,
2382235537Sgber	.vop_pathconf =		VOP_PANIC,
2383235537Sgber	.vop_print =		VOP_PANIC,
2384235537Sgber	.vop_read =		VOP_PANIC,
2385235537Sgber	.vop_readdir =		VOP_PANIC,
2386235537Sgber	.vop_readlink =		VOP_PANIC,
2387235537Sgber	.vop_remove =		VOP_PANIC,
2388235537Sgber	.vop_rename =		VOP_PANIC,
2389235537Sgber	.vop_rmdir =		VOP_PANIC,
2390235537Sgber	.vop_whiteout =		VOP_PANIC,
2391235537Sgber	.vop_write =		VOP_PANIC,
2392235537Sgber	.vop_setattr =		VOP_PANIC,
2393235537Sgber	.vop_symlink =		VOP_PANIC,
2394235537Sgber};
2395235537Sgber
2396235537Sgberstatic int
2397235537Sgbernandfsfifo_close(struct vop_close_args *ap)
2398235537Sgber{
2399235537Sgber	struct vnode *vp = ap->a_vp;
2400235537Sgber	struct nandfs_node *node = VTON(vp);
2401235537Sgber
2402235537Sgber	DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node));
2403235537Sgber
2404235537Sgber	mtx_lock(&vp->v_interlock);
2405235537Sgber	if (vp->v_usecount > 1)
2406235537Sgber		nandfs_itimes_locked(vp);
2407235537Sgber	mtx_unlock(&vp->v_interlock);
2408235537Sgber
2409235537Sgber	return (fifo_specops.vop_close(ap));
2410235537Sgber}
2411235537Sgber
2412235537Sgberstruct vop_vector nandfs_fifoops = {
2413235537Sgber	.vop_default =		&fifo_specops,
2414235537Sgber	.vop_fsync =		VOP_PANIC,
2415235537Sgber	.vop_access =		nandfs_access,
2416235537Sgber	.vop_close =		nandfsfifo_close,
2417235537Sgber	.vop_getattr =		nandfs_getattr,
2418235537Sgber	.vop_inactive =		nandfs_inactive,
2419328298Sjhb	.vop_pathconf =		nandfs_pathconf,
2420235537Sgber	.vop_print =		nandfs_print,
2421235537Sgber	.vop_read =		VOP_PANIC,
2422235537Sgber	.vop_reclaim =		nandfs_reclaim,
2423235537Sgber	.vop_setattr =		nandfs_setattr,
2424235537Sgber	.vop_write =		VOP_PANIC,
2425235537Sgber	.vop_lock1 =		nandfs_vnlock1,
2426235537Sgber	.vop_unlock =		nandfs_vnunlock,
2427235537Sgber};
2428235537Sgber
2429235537Sgberint
2430235537Sgbernandfs_vinit(struct vnode *vp, uint64_t ino)
2431235537Sgber{
2432235537Sgber	struct nandfs_node *node;
2433235537Sgber
2434235537Sgber	ASSERT_VOP_LOCKED(vp, __func__);
2435235537Sgber
2436235537Sgber	node = VTON(vp);
2437235537Sgber
2438235537Sgber	/* Check if we're fetching the root */
2439235537Sgber	if (ino == NANDFS_ROOT_INO)
2440235537Sgber		vp->v_vflag |= VV_ROOT;
2441235537Sgber
2442235537Sgber	if (ino != NANDFS_GC_INO)
2443235537Sgber		vp->v_type = IFTOVT(node->nn_inode.i_mode);
2444235537Sgber	else
2445235537Sgber		vp->v_type = VREG;
2446235537Sgber
2447235537Sgber	if (vp->v_type == VFIFO)
2448235537Sgber		vp->v_op = &nandfs_fifoops;
2449235537Sgber
2450235537Sgber	return (0);
2451235537Sgber}
2452