tmpfs_vnops.c revision 276648
1/*	$NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $	*/
2
3/*-
4 * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9 * 2005 program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * tmpfs vnode interface.
35 */
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: stable/10/sys/fs/tmpfs/tmpfs_vnops.c 276648 2015-01-04 00:46:06Z kib $");
38
39#include <sys/param.h>
40#include <sys/fcntl.h>
41#include <sys/lockf.h>
42#include <sys/lock.h>
43#include <sys/namei.h>
44#include <sys/priv.h>
45#include <sys/proc.h>
46#include <sys/rwlock.h>
47#include <sys/sched.h>
48#include <sys/stat.h>
49#include <sys/systm.h>
50#include <sys/sysctl.h>
51#include <sys/unistd.h>
52#include <sys/vnode.h>
53
54#include <vm/vm.h>
55#include <vm/vm_param.h>
56#include <vm/vm_object.h>
57#include <vm/vm_page.h>
58#include <vm/vm_pager.h>
59
60#include <fs/tmpfs/tmpfs_vnops.h>
61#include <fs/tmpfs/tmpfs.h>
62
63SYSCTL_DECL(_vfs_tmpfs);
64
65static volatile int tmpfs_rename_restarts;
66SYSCTL_INT(_vfs_tmpfs, OID_AUTO, rename_restarts, CTLFLAG_RD,
67    __DEVOLATILE(int *, &tmpfs_rename_restarts), 0,
68    "Times rename had to restart due to lock contention");
69
70static int
71tmpfs_vn_get_ino_alloc(struct mount *mp, void *arg, int lkflags,
72    struct vnode **rvp)
73{
74
75	return (tmpfs_alloc_vp(mp, arg, lkflags, rvp));
76}
77
78static int
79tmpfs_lookup(struct vop_cachedlookup_args *v)
80{
81	struct vnode *dvp = v->a_dvp;
82	struct vnode **vpp = v->a_vpp;
83	struct componentname *cnp = v->a_cnp;
84	struct tmpfs_dirent *de;
85	struct tmpfs_node *dnode;
86	int error;
87
88	dnode = VP_TO_TMPFS_DIR(dvp);
89	*vpp = NULLVP;
90
91	/* Check accessibility of requested node as a first step. */
92	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_thread);
93	if (error != 0)
94		goto out;
95
96	/* We cannot be requesting the parent directory of the root node. */
97	MPASS(IMPLIES(dnode->tn_type == VDIR &&
98	    dnode->tn_dir.tn_parent == dnode,
99	    !(cnp->cn_flags & ISDOTDOT)));
100
101	TMPFS_ASSERT_LOCKED(dnode);
102	if (dnode->tn_dir.tn_parent == NULL) {
103		error = ENOENT;
104		goto out;
105	}
106	if (cnp->cn_flags & ISDOTDOT) {
107		error = vn_vget_ino_gen(dvp, tmpfs_vn_get_ino_alloc,
108		    dnode->tn_dir.tn_parent, cnp->cn_lkflags, vpp);
109		if (error != 0)
110			goto out;
111	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
112		VREF(dvp);
113		*vpp = dvp;
114		error = 0;
115	} else {
116		de = tmpfs_dir_lookup(dnode, NULL, cnp);
117		if (de != NULL && de->td_node == NULL)
118			cnp->cn_flags |= ISWHITEOUT;
119		if (de == NULL || de->td_node == NULL) {
120			/* The entry was not found in the directory.
121			 * This is OK if we are creating or renaming an
122			 * entry and are working on the last component of
123			 * the path name. */
124			if ((cnp->cn_flags & ISLASTCN) &&
125			    (cnp->cn_nameiop == CREATE || \
126			    cnp->cn_nameiop == RENAME ||
127			    (cnp->cn_nameiop == DELETE &&
128			    cnp->cn_flags & DOWHITEOUT &&
129			    cnp->cn_flags & ISWHITEOUT))) {
130				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
131				    cnp->cn_thread);
132				if (error != 0)
133					goto out;
134
135				/* Keep the component name in the buffer for
136				 * future uses. */
137				cnp->cn_flags |= SAVENAME;
138
139				error = EJUSTRETURN;
140			} else
141				error = ENOENT;
142		} else {
143			struct tmpfs_node *tnode;
144
145			/* The entry was found, so get its associated
146			 * tmpfs_node. */
147			tnode = de->td_node;
148
149			/* If we are not at the last path component and
150			 * found a non-directory or non-link entry (which
151			 * may itself be pointing to a directory), raise
152			 * an error. */
153			if ((tnode->tn_type != VDIR &&
154			    tnode->tn_type != VLNK) &&
155			    !(cnp->cn_flags & ISLASTCN)) {
156				error = ENOTDIR;
157				goto out;
158			}
159
160			/* If we are deleting or renaming the entry, keep
161			 * track of its tmpfs_dirent so that it can be
162			 * easily deleted later. */
163			if ((cnp->cn_flags & ISLASTCN) &&
164			    (cnp->cn_nameiop == DELETE ||
165			    cnp->cn_nameiop == RENAME)) {
166				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
167				    cnp->cn_thread);
168				if (error != 0)
169					goto out;
170
171				/* Allocate a new vnode on the matching entry. */
172				error = tmpfs_alloc_vp(dvp->v_mount, tnode,
173				    cnp->cn_lkflags, vpp);
174				if (error != 0)
175					goto out;
176
177				if ((dnode->tn_mode & S_ISTXT) &&
178				  VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) &&
179				  VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) {
180					error = EPERM;
181					vput(*vpp);
182					*vpp = NULL;
183					goto out;
184				}
185				cnp->cn_flags |= SAVENAME;
186			} else {
187				error = tmpfs_alloc_vp(dvp->v_mount, tnode,
188				    cnp->cn_lkflags, vpp);
189				if (error != 0)
190					goto out;
191			}
192		}
193	}
194
195	/* Store the result of this lookup in the cache.  Avoid this if the
196	 * request was for creation, as it does not improve timings on
197	 * emprical tests. */
198	if ((cnp->cn_flags & MAKEENTRY) != 0)
199		cache_enter(dvp, *vpp, cnp);
200
201out:
202	/* If there were no errors, *vpp cannot be null and it must be
203	 * locked. */
204	MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp)));
205
206	return error;
207}
208
209static int
210tmpfs_create(struct vop_create_args *v)
211{
212	struct vnode *dvp = v->a_dvp;
213	struct vnode **vpp = v->a_vpp;
214	struct componentname *cnp = v->a_cnp;
215	struct vattr *vap = v->a_vap;
216	int error;
217
218	MPASS(vap->va_type == VREG || vap->va_type == VSOCK);
219
220	error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
221	if (error == 0 && (cnp->cn_flags & MAKEENTRY) != 0)
222		cache_enter(dvp, *vpp, cnp);
223	return (error);
224}
225
226static int
227tmpfs_mknod(struct vop_mknod_args *v)
228{
229	struct vnode *dvp = v->a_dvp;
230	struct vnode **vpp = v->a_vpp;
231	struct componentname *cnp = v->a_cnp;
232	struct vattr *vap = v->a_vap;
233
234	if (vap->va_type != VBLK && vap->va_type != VCHR &&
235	    vap->va_type != VFIFO)
236		return EINVAL;
237
238	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
239}
240
241static int
242tmpfs_open(struct vop_open_args *v)
243{
244	struct vnode *vp = v->a_vp;
245	int mode = v->a_mode;
246
247	int error;
248	struct tmpfs_node *node;
249
250	MPASS(VOP_ISLOCKED(vp));
251
252	node = VP_TO_TMPFS_NODE(vp);
253
254	/* The file is still active but all its names have been removed
255	 * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
256	 * it is about to die. */
257	if (node->tn_links < 1)
258		return (ENOENT);
259
260	/* If the file is marked append-only, deny write requests. */
261	if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
262		error = EPERM;
263	else {
264		error = 0;
265		/* For regular files, the call below is nop. */
266		KASSERT(vp->v_type != VREG || (node->tn_reg.tn_aobj->flags &
267		    OBJ_DEAD) == 0, ("dead object"));
268		vnode_create_vobject(vp, node->tn_size, v->a_td);
269	}
270
271	MPASS(VOP_ISLOCKED(vp));
272	return error;
273}
274
275static int
276tmpfs_close(struct vop_close_args *v)
277{
278	struct vnode *vp = v->a_vp;
279
280	/* Update node times. */
281	tmpfs_update(vp);
282
283	return (0);
284}
285
286int
287tmpfs_access(struct vop_access_args *v)
288{
289	struct vnode *vp = v->a_vp;
290	accmode_t accmode = v->a_accmode;
291	struct ucred *cred = v->a_cred;
292
293	int error;
294	struct tmpfs_node *node;
295
296	MPASS(VOP_ISLOCKED(vp));
297
298	node = VP_TO_TMPFS_NODE(vp);
299
300	switch (vp->v_type) {
301	case VDIR:
302		/* FALLTHROUGH */
303	case VLNK:
304		/* FALLTHROUGH */
305	case VREG:
306		if (accmode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
307			error = EROFS;
308			goto out;
309		}
310		break;
311
312	case VBLK:
313		/* FALLTHROUGH */
314	case VCHR:
315		/* FALLTHROUGH */
316	case VSOCK:
317		/* FALLTHROUGH */
318	case VFIFO:
319		break;
320
321	default:
322		error = EINVAL;
323		goto out;
324	}
325
326	if (accmode & VWRITE && node->tn_flags & IMMUTABLE) {
327		error = EPERM;
328		goto out;
329	}
330
331	error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
332	    node->tn_gid, accmode, cred, NULL);
333
334out:
335	MPASS(VOP_ISLOCKED(vp));
336
337	return error;
338}
339
340int
341tmpfs_getattr(struct vop_getattr_args *v)
342{
343	struct vnode *vp = v->a_vp;
344	struct vattr *vap = v->a_vap;
345
346	struct tmpfs_node *node;
347
348	node = VP_TO_TMPFS_NODE(vp);
349
350	tmpfs_update(vp);
351
352	vap->va_type = vp->v_type;
353	vap->va_mode = node->tn_mode;
354	vap->va_nlink = node->tn_links;
355	vap->va_uid = node->tn_uid;
356	vap->va_gid = node->tn_gid;
357	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
358	vap->va_fileid = node->tn_id;
359	vap->va_size = node->tn_size;
360	vap->va_blocksize = PAGE_SIZE;
361	vap->va_atime = node->tn_atime;
362	vap->va_mtime = node->tn_mtime;
363	vap->va_ctime = node->tn_ctime;
364	vap->va_birthtime = node->tn_birthtime;
365	vap->va_gen = node->tn_gen;
366	vap->va_flags = node->tn_flags;
367	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
368		node->tn_rdev : NODEV;
369	vap->va_bytes = round_page(node->tn_size);
370	vap->va_filerev = 0;
371
372	return 0;
373}
374
375int
376tmpfs_setattr(struct vop_setattr_args *v)
377{
378	struct vnode *vp = v->a_vp;
379	struct vattr *vap = v->a_vap;
380	struct ucred *cred = v->a_cred;
381	struct thread *td = curthread;
382
383	int error;
384
385	MPASS(VOP_ISLOCKED(vp));
386
387	error = 0;
388
389	/* Abort if any unsettable attribute is given. */
390	if (vap->va_type != VNON ||
391	    vap->va_nlink != VNOVAL ||
392	    vap->va_fsid != VNOVAL ||
393	    vap->va_fileid != VNOVAL ||
394	    vap->va_blocksize != VNOVAL ||
395	    vap->va_gen != VNOVAL ||
396	    vap->va_rdev != VNOVAL ||
397	    vap->va_bytes != VNOVAL)
398		error = EINVAL;
399
400	if (error == 0 && (vap->va_flags != VNOVAL))
401		error = tmpfs_chflags(vp, vap->va_flags, cred, td);
402
403	if (error == 0 && (vap->va_size != VNOVAL))
404		error = tmpfs_chsize(vp, vap->va_size, cred, td);
405
406	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
407		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
408
409	if (error == 0 && (vap->va_mode != (mode_t)VNOVAL))
410		error = tmpfs_chmod(vp, vap->va_mode, cred, td);
411
412	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
413	    vap->va_atime.tv_nsec != VNOVAL) ||
414	    (vap->va_mtime.tv_sec != VNOVAL &&
415	    vap->va_mtime.tv_nsec != VNOVAL) ||
416	    (vap->va_birthtime.tv_sec != VNOVAL &&
417	    vap->va_birthtime.tv_nsec != VNOVAL)))
418		error = tmpfs_chtimes(vp, vap, cred, td);
419
420	/* Update the node times.  We give preference to the error codes
421	 * generated by this function rather than the ones that may arise
422	 * from tmpfs_update. */
423	tmpfs_update(vp);
424
425	MPASS(VOP_ISLOCKED(vp));
426
427	return error;
428}
429
430static int
431tmpfs_read(struct vop_read_args *v)
432{
433	struct vnode *vp;
434	struct uio *uio;
435	struct tmpfs_node *node;
436
437	vp = v->a_vp;
438	if (vp->v_type != VREG)
439		return (EISDIR);
440	uio = v->a_uio;
441	if (uio->uio_offset < 0)
442		return (EINVAL);
443	node = VP_TO_TMPFS_NODE(vp);
444	node->tn_status |= TMPFS_NODE_ACCESSED;
445	return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio));
446}
447
448static int
449tmpfs_write(struct vop_write_args *v)
450{
451	struct vnode *vp;
452	struct uio *uio;
453	struct tmpfs_node *node;
454	off_t oldsize;
455	int error, ioflag;
456	boolean_t extended;
457
458	vp = v->a_vp;
459	uio = v->a_uio;
460	ioflag = v->a_ioflag;
461	error = 0;
462	node = VP_TO_TMPFS_NODE(vp);
463	oldsize = node->tn_size;
464
465	if (uio->uio_offset < 0 || vp->v_type != VREG)
466		return (EINVAL);
467	if (uio->uio_resid == 0)
468		return (0);
469	if (ioflag & IO_APPEND)
470		uio->uio_offset = node->tn_size;
471	if (uio->uio_offset + uio->uio_resid >
472	  VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
473		return (EFBIG);
474	if (vn_rlimit_fsize(vp, uio, uio->uio_td))
475		return (EFBIG);
476	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
477	if (extended) {
478		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid,
479		    FALSE);
480		if (error != 0)
481			goto out;
482	}
483
484	error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio);
485	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
486	    (extended ? TMPFS_NODE_CHANGED : 0);
487	if (node->tn_mode & (S_ISUID | S_ISGID)) {
488		if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
489			node->tn_mode &= ~(S_ISUID | S_ISGID);
490	}
491	if (error != 0)
492		(void)tmpfs_reg_resize(vp, oldsize, TRUE);
493
494out:
495	MPASS(IMPLIES(error == 0, uio->uio_resid == 0));
496	MPASS(IMPLIES(error != 0, oldsize == node->tn_size));
497
498	return (error);
499}
500
501static int
502tmpfs_fsync(struct vop_fsync_args *v)
503{
504	struct vnode *vp = v->a_vp;
505
506	MPASS(VOP_ISLOCKED(vp));
507
508	tmpfs_update(vp);
509
510	return 0;
511}
512
513static int
514tmpfs_remove(struct vop_remove_args *v)
515{
516	struct vnode *dvp = v->a_dvp;
517	struct vnode *vp = v->a_vp;
518
519	int error;
520	struct tmpfs_dirent *de;
521	struct tmpfs_mount *tmp;
522	struct tmpfs_node *dnode;
523	struct tmpfs_node *node;
524
525	MPASS(VOP_ISLOCKED(dvp));
526	MPASS(VOP_ISLOCKED(vp));
527
528	if (vp->v_type == VDIR) {
529		error = EISDIR;
530		goto out;
531	}
532
533	dnode = VP_TO_TMPFS_DIR(dvp);
534	node = VP_TO_TMPFS_NODE(vp);
535	tmp = VFS_TO_TMPFS(vp->v_mount);
536	de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
537	MPASS(de != NULL);
538
539	/* Files marked as immutable or append-only cannot be deleted. */
540	if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
541	    (dnode->tn_flags & APPEND)) {
542		error = EPERM;
543		goto out;
544	}
545
546	/* Remove the entry from the directory; as it is a file, we do not
547	 * have to change the number of hard links of the directory. */
548	tmpfs_dir_detach(dvp, de);
549	if (v->a_cnp->cn_flags & DOWHITEOUT)
550		tmpfs_dir_whiteout_add(dvp, v->a_cnp);
551
552	/* Free the directory entry we just deleted.  Note that the node
553	 * referred by it will not be removed until the vnode is really
554	 * reclaimed. */
555	tmpfs_free_dirent(tmp, de);
556
557	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED;
558	error = 0;
559
560out:
561
562	return error;
563}
564
565static int
566tmpfs_link(struct vop_link_args *v)
567{
568	struct vnode *dvp = v->a_tdvp;
569	struct vnode *vp = v->a_vp;
570	struct componentname *cnp = v->a_cnp;
571
572	int error;
573	struct tmpfs_dirent *de;
574	struct tmpfs_node *node;
575
576	MPASS(VOP_ISLOCKED(dvp));
577	MPASS(cnp->cn_flags & HASBUF);
578	MPASS(dvp != vp); /* XXX When can this be false? */
579	node = VP_TO_TMPFS_NODE(vp);
580
581	/* Ensure that we do not overflow the maximum number of links imposed
582	 * by the system. */
583	MPASS(node->tn_links <= LINK_MAX);
584	if (node->tn_links == LINK_MAX) {
585		error = EMLINK;
586		goto out;
587	}
588
589	/* We cannot create links of files marked immutable or append-only. */
590	if (node->tn_flags & (IMMUTABLE | APPEND)) {
591		error = EPERM;
592		goto out;
593	}
594
595	/* Allocate a new directory entry to represent the node. */
596	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
597	    cnp->cn_nameptr, cnp->cn_namelen, &de);
598	if (error != 0)
599		goto out;
600
601	/* Insert the new directory entry into the appropriate directory. */
602	if (cnp->cn_flags & ISWHITEOUT)
603		tmpfs_dir_whiteout_remove(dvp, cnp);
604	tmpfs_dir_attach(dvp, de);
605
606	/* vp link count has changed, so update node times. */
607	node->tn_status |= TMPFS_NODE_CHANGED;
608	tmpfs_update(vp);
609
610	error = 0;
611
612out:
613	return error;
614}
615
616/*
617 * We acquire all but fdvp locks using non-blocking acquisitions.  If we
618 * fail to acquire any lock in the path we will drop all held locks,
619 * acquire the new lock in a blocking fashion, and then release it and
620 * restart the rename.  This acquire/release step ensures that we do not
621 * spin on a lock waiting for release.  On error release all vnode locks
622 * and decrement references the way tmpfs_rename() would do.
623 */
624static int
625tmpfs_rename_relock(struct vnode *fdvp, struct vnode **fvpp,
626    struct vnode *tdvp, struct vnode **tvpp,
627    struct componentname *fcnp, struct componentname *tcnp)
628{
629	struct vnode *nvp;
630	struct mount *mp;
631	struct tmpfs_dirent *de;
632	int error, restarts = 0;
633
634	VOP_UNLOCK(tdvp, 0);
635	if (*tvpp != NULL && *tvpp != tdvp)
636		VOP_UNLOCK(*tvpp, 0);
637	mp = fdvp->v_mount;
638
639relock:
640	restarts += 1;
641	error = vn_lock(fdvp, LK_EXCLUSIVE);
642	if (error)
643		goto releout;
644	if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
645		VOP_UNLOCK(fdvp, 0);
646		error = vn_lock(tdvp, LK_EXCLUSIVE);
647		if (error)
648			goto releout;
649		VOP_UNLOCK(tdvp, 0);
650		goto relock;
651	}
652	/*
653	 * Re-resolve fvp to be certain it still exists and fetch the
654	 * correct vnode.
655	 */
656	de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(fdvp), NULL, fcnp);
657	if (de == NULL) {
658		VOP_UNLOCK(fdvp, 0);
659		VOP_UNLOCK(tdvp, 0);
660		if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
661		    (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
662			error = EINVAL;
663		else
664			error = ENOENT;
665		goto releout;
666	}
667	error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE | LK_NOWAIT, &nvp);
668	if (error != 0) {
669		VOP_UNLOCK(fdvp, 0);
670		VOP_UNLOCK(tdvp, 0);
671		if (error != EBUSY)
672			goto releout;
673		error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, &nvp);
674		if (error != 0)
675			goto releout;
676		VOP_UNLOCK(nvp, 0);
677		/*
678		 * Concurrent rename race.
679		 */
680		if (nvp == tdvp) {
681			vrele(nvp);
682			error = EINVAL;
683			goto releout;
684		}
685		vrele(*fvpp);
686		*fvpp = nvp;
687		goto relock;
688	}
689	vrele(*fvpp);
690	*fvpp = nvp;
691	VOP_UNLOCK(*fvpp, 0);
692	/*
693	 * Re-resolve tvp and acquire the vnode lock if present.
694	 */
695	de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(tdvp), NULL, tcnp);
696	/*
697	 * If tvp disappeared we just carry on.
698	 */
699	if (de == NULL && *tvpp != NULL) {
700		vrele(*tvpp);
701		*tvpp = NULL;
702	}
703	/*
704	 * Get the tvp ino if the lookup succeeded.  We may have to restart
705	 * if the non-blocking acquire fails.
706	 */
707	if (de != NULL) {
708		nvp = NULL;
709		error = tmpfs_alloc_vp(mp, de->td_node,
710		    LK_EXCLUSIVE | LK_NOWAIT, &nvp);
711		if (*tvpp != NULL)
712			vrele(*tvpp);
713		*tvpp = nvp;
714		if (error != 0) {
715			VOP_UNLOCK(fdvp, 0);
716			VOP_UNLOCK(tdvp, 0);
717			if (error != EBUSY)
718				goto releout;
719			error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE,
720			    &nvp);
721			if (error != 0)
722				goto releout;
723			VOP_UNLOCK(nvp, 0);
724			/*
725			 * fdvp contains fvp, thus tvp (=fdvp) is not empty.
726			 */
727			if (nvp == fdvp) {
728				error = ENOTEMPTY;
729				goto releout;
730			}
731			goto relock;
732		}
733	}
734	tmpfs_rename_restarts += restarts;
735
736	return (0);
737
738releout:
739	vrele(fdvp);
740	vrele(*fvpp);
741	vrele(tdvp);
742	if (*tvpp != NULL)
743		vrele(*tvpp);
744	tmpfs_rename_restarts += restarts;
745
746	return (error);
747}
748
749static int
750tmpfs_rename(struct vop_rename_args *v)
751{
752	struct vnode *fdvp = v->a_fdvp;
753	struct vnode *fvp = v->a_fvp;
754	struct componentname *fcnp = v->a_fcnp;
755	struct vnode *tdvp = v->a_tdvp;
756	struct vnode *tvp = v->a_tvp;
757	struct componentname *tcnp = v->a_tcnp;
758	struct mount *mp = NULL;
759
760	char *newname;
761	int error;
762	struct tmpfs_dirent *de;
763	struct tmpfs_mount *tmp;
764	struct tmpfs_node *fdnode;
765	struct tmpfs_node *fnode;
766	struct tmpfs_node *tnode;
767	struct tmpfs_node *tdnode;
768
769	MPASS(VOP_ISLOCKED(tdvp));
770	MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
771	MPASS(fcnp->cn_flags & HASBUF);
772	MPASS(tcnp->cn_flags & HASBUF);
773
774	/* Disallow cross-device renames.
775	 * XXX Why isn't this done by the caller? */
776	if (fvp->v_mount != tdvp->v_mount ||
777	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
778		error = EXDEV;
779		goto out;
780	}
781
782	/* If source and target are the same file, there is nothing to do. */
783	if (fvp == tvp) {
784		error = 0;
785		goto out;
786	}
787
788	/* If we need to move the directory between entries, lock the
789	 * source so that we can safely operate on it. */
790	if (fdvp != tdvp && fdvp != tvp) {
791		if (vn_lock(fdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
792			mp = tdvp->v_mount;
793			error = vfs_busy(mp, 0);
794			if (error != 0) {
795				mp = NULL;
796				goto out;
797			}
798			error = tmpfs_rename_relock(fdvp, &fvp, tdvp, &tvp,
799			    fcnp, tcnp);
800			if (error != 0) {
801				vfs_unbusy(mp);
802				return (error);
803			}
804			ASSERT_VOP_ELOCKED(fdvp,
805			    "tmpfs_rename: fdvp not locked");
806			ASSERT_VOP_ELOCKED(tdvp,
807			    "tmpfs_rename: tdvp not locked");
808			if (tvp != NULL)
809				ASSERT_VOP_ELOCKED(tvp,
810				    "tmpfs_rename: tvp not locked");
811			if (fvp == tvp) {
812				error = 0;
813				goto out_locked;
814			}
815		}
816	}
817
818	tmp = VFS_TO_TMPFS(tdvp->v_mount);
819	tdnode = VP_TO_TMPFS_DIR(tdvp);
820	tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
821	fdnode = VP_TO_TMPFS_DIR(fdvp);
822	fnode = VP_TO_TMPFS_NODE(fvp);
823	de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
824
825	/* Entry can disappear before we lock fdvp,
826	 * also avoid manipulating '.' and '..' entries. */
827	if (de == NULL) {
828		if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
829		    (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
830			error = EINVAL;
831		else
832			error = ENOENT;
833		goto out_locked;
834	}
835	MPASS(de->td_node == fnode);
836
837	/* If re-naming a directory to another preexisting directory
838	 * ensure that the target directory is empty so that its
839	 * removal causes no side effects.
840	 * Kern_rename gurantees the destination to be a directory
841	 * if the source is one. */
842	if (tvp != NULL) {
843		MPASS(tnode != NULL);
844
845		if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
846		    (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
847			error = EPERM;
848			goto out_locked;
849		}
850
851		if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
852			if (tnode->tn_size > 0) {
853				error = ENOTEMPTY;
854				goto out_locked;
855			}
856		} else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
857			error = ENOTDIR;
858			goto out_locked;
859		} else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
860			error = EISDIR;
861			goto out_locked;
862		} else {
863			MPASS(fnode->tn_type != VDIR &&
864				tnode->tn_type != VDIR);
865		}
866	}
867
868	if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
869	    || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
870		error = EPERM;
871		goto out_locked;
872	}
873
874	/* Ensure that we have enough memory to hold the new name, if it
875	 * has to be changed. */
876	if (fcnp->cn_namelen != tcnp->cn_namelen ||
877	    bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
878		newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
879	} else
880		newname = NULL;
881
882	/* If the node is being moved to another directory, we have to do
883	 * the move. */
884	if (fdnode != tdnode) {
885		/* In case we are moving a directory, we have to adjust its
886		 * parent to point to the new parent. */
887		if (de->td_node->tn_type == VDIR) {
888			struct tmpfs_node *n;
889
890			/* Ensure the target directory is not a child of the
891			 * directory being moved.  Otherwise, we'd end up
892			 * with stale nodes. */
893			n = tdnode;
894			/* TMPFS_LOCK garanties that no nodes are freed while
895			 * traversing the list. Nodes can only be marked as
896			 * removed: tn_parent == NULL. */
897			TMPFS_LOCK(tmp);
898			TMPFS_NODE_LOCK(n);
899			while (n != n->tn_dir.tn_parent) {
900				struct tmpfs_node *parent;
901
902				if (n == fnode) {
903					TMPFS_NODE_UNLOCK(n);
904					TMPFS_UNLOCK(tmp);
905					error = EINVAL;
906					if (newname != NULL)
907						    free(newname, M_TMPFSNAME);
908					goto out_locked;
909				}
910				parent = n->tn_dir.tn_parent;
911				TMPFS_NODE_UNLOCK(n);
912				if (parent == NULL) {
913					n = NULL;
914					break;
915				}
916				TMPFS_NODE_LOCK(parent);
917				if (parent->tn_dir.tn_parent == NULL) {
918					TMPFS_NODE_UNLOCK(parent);
919					n = NULL;
920					break;
921				}
922				n = parent;
923			}
924			TMPFS_UNLOCK(tmp);
925			if (n == NULL) {
926				error = EINVAL;
927				if (newname != NULL)
928					    free(newname, M_TMPFSNAME);
929				goto out_locked;
930			}
931			TMPFS_NODE_UNLOCK(n);
932
933			/* Adjust the parent pointer. */
934			TMPFS_VALIDATE_DIR(fnode);
935			TMPFS_NODE_LOCK(de->td_node);
936			de->td_node->tn_dir.tn_parent = tdnode;
937			TMPFS_NODE_UNLOCK(de->td_node);
938
939			/* As a result of changing the target of the '..'
940			 * entry, the link count of the source and target
941			 * directories has to be adjusted. */
942			TMPFS_NODE_LOCK(tdnode);
943			TMPFS_ASSERT_LOCKED(tdnode);
944			tdnode->tn_links++;
945			TMPFS_NODE_UNLOCK(tdnode);
946
947			TMPFS_NODE_LOCK(fdnode);
948			TMPFS_ASSERT_LOCKED(fdnode);
949			fdnode->tn_links--;
950			TMPFS_NODE_UNLOCK(fdnode);
951		}
952	}
953
954	/* Do the move: just remove the entry from the source directory
955	 * and insert it into the target one. */
956	tmpfs_dir_detach(fdvp, de);
957
958	if (fcnp->cn_flags & DOWHITEOUT)
959		tmpfs_dir_whiteout_add(fdvp, fcnp);
960	if (tcnp->cn_flags & ISWHITEOUT)
961		tmpfs_dir_whiteout_remove(tdvp, tcnp);
962
963	/* If the name has changed, we need to make it effective by changing
964	 * it in the directory entry. */
965	if (newname != NULL) {
966		MPASS(tcnp->cn_namelen <= MAXNAMLEN);
967
968		free(de->ud.td_name, M_TMPFSNAME);
969		de->ud.td_name = newname;
970		tmpfs_dirent_init(de, tcnp->cn_nameptr, tcnp->cn_namelen);
971
972		fnode->tn_status |= TMPFS_NODE_CHANGED;
973		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
974	}
975
976	/* If we are overwriting an entry, we have to remove the old one
977	 * from the target directory. */
978	if (tvp != NULL) {
979		struct tmpfs_dirent *tde;
980
981		/* Remove the old entry from the target directory. */
982		tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
983		tmpfs_dir_detach(tdvp, tde);
984
985		/* Free the directory entry we just deleted.  Note that the
986		 * node referred by it will not be removed until the vnode is
987		 * really reclaimed. */
988		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde);
989	}
990
991	tmpfs_dir_attach(tdvp, de);
992
993	cache_purge(fvp);
994	if (tvp != NULL)
995		cache_purge(tvp);
996	cache_purge_negative(tdvp);
997
998	error = 0;
999
1000out_locked:
1001	if (fdvp != tdvp && fdvp != tvp)
1002		VOP_UNLOCK(fdvp, 0);
1003
1004out:
1005	/* Release target nodes. */
1006	/* XXX: I don't understand when tdvp can be the same as tvp, but
1007	 * other code takes care of this... */
1008	if (tdvp == tvp)
1009		vrele(tdvp);
1010	else
1011		vput(tdvp);
1012	if (tvp != NULL)
1013		vput(tvp);
1014
1015	/* Release source nodes. */
1016	vrele(fdvp);
1017	vrele(fvp);
1018
1019	if (mp != NULL)
1020		vfs_unbusy(mp);
1021
1022	return error;
1023}
1024
1025static int
1026tmpfs_mkdir(struct vop_mkdir_args *v)
1027{
1028	struct vnode *dvp = v->a_dvp;
1029	struct vnode **vpp = v->a_vpp;
1030	struct componentname *cnp = v->a_cnp;
1031	struct vattr *vap = v->a_vap;
1032
1033	MPASS(vap->va_type == VDIR);
1034
1035	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
1036}
1037
1038static int
1039tmpfs_rmdir(struct vop_rmdir_args *v)
1040{
1041	struct vnode *dvp = v->a_dvp;
1042	struct vnode *vp = v->a_vp;
1043
1044	int error;
1045	struct tmpfs_dirent *de;
1046	struct tmpfs_mount *tmp;
1047	struct tmpfs_node *dnode;
1048	struct tmpfs_node *node;
1049
1050	MPASS(VOP_ISLOCKED(dvp));
1051	MPASS(VOP_ISLOCKED(vp));
1052
1053	tmp = VFS_TO_TMPFS(dvp->v_mount);
1054	dnode = VP_TO_TMPFS_DIR(dvp);
1055	node = VP_TO_TMPFS_DIR(vp);
1056
1057	/* Directories with more than two entries ('.' and '..') cannot be
1058	 * removed. */
1059	 if (node->tn_size > 0) {
1060		 error = ENOTEMPTY;
1061		 goto out;
1062	 }
1063
1064	if ((dnode->tn_flags & APPEND)
1065	    || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
1066		error = EPERM;
1067		goto out;
1068	}
1069
1070	/* This invariant holds only if we are not trying to remove "..".
1071	  * We checked for that above so this is safe now. */
1072	MPASS(node->tn_dir.tn_parent == dnode);
1073
1074	/* Get the directory entry associated with node (vp).  This was
1075	 * filled by tmpfs_lookup while looking up the entry. */
1076	de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
1077	MPASS(TMPFS_DIRENT_MATCHES(de,
1078	    v->a_cnp->cn_nameptr,
1079	    v->a_cnp->cn_namelen));
1080
1081	/* Check flags to see if we are allowed to remove the directory. */
1082	if (dnode->tn_flags & APPEND
1083		|| node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
1084		error = EPERM;
1085		goto out;
1086	}
1087
1088
1089	/* Detach the directory entry from the directory (dnode). */
1090	tmpfs_dir_detach(dvp, de);
1091	if (v->a_cnp->cn_flags & DOWHITEOUT)
1092		tmpfs_dir_whiteout_add(dvp, v->a_cnp);
1093
1094	/* No vnode should be allocated for this entry from this point */
1095	TMPFS_NODE_LOCK(node);
1096	TMPFS_ASSERT_ELOCKED(node);
1097	node->tn_links--;
1098	node->tn_dir.tn_parent = NULL;
1099	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
1100	    TMPFS_NODE_MODIFIED;
1101
1102	TMPFS_NODE_UNLOCK(node);
1103
1104	TMPFS_NODE_LOCK(dnode);
1105	TMPFS_ASSERT_ELOCKED(dnode);
1106	dnode->tn_links--;
1107	dnode->tn_status |= TMPFS_NODE_ACCESSED | \
1108	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1109	TMPFS_NODE_UNLOCK(dnode);
1110
1111	cache_purge(dvp);
1112	cache_purge(vp);
1113
1114	/* Free the directory entry we just deleted.  Note that the node
1115	 * referred by it will not be removed until the vnode is really
1116	 * reclaimed. */
1117	tmpfs_free_dirent(tmp, de);
1118
1119	/* Release the deleted vnode (will destroy the node, notify
1120	 * interested parties and clean it from the cache). */
1121
1122	dnode->tn_status |= TMPFS_NODE_CHANGED;
1123	tmpfs_update(dvp);
1124
1125	error = 0;
1126
1127out:
1128	return error;
1129}
1130
1131static int
1132tmpfs_symlink(struct vop_symlink_args *v)
1133{
1134	struct vnode *dvp = v->a_dvp;
1135	struct vnode **vpp = v->a_vpp;
1136	struct componentname *cnp = v->a_cnp;
1137	struct vattr *vap = v->a_vap;
1138	char *target = v->a_target;
1139
1140#ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */
1141	MPASS(vap->va_type == VLNK);
1142#else
1143	vap->va_type = VLNK;
1144#endif
1145
1146	return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1147}
1148
1149static int
1150tmpfs_readdir(struct vop_readdir_args *v)
1151{
1152	struct vnode *vp = v->a_vp;
1153	struct uio *uio = v->a_uio;
1154	int *eofflag = v->a_eofflag;
1155	u_long **cookies = v->a_cookies;
1156	int *ncookies = v->a_ncookies;
1157
1158	int error;
1159	ssize_t startresid;
1160	int maxcookies;
1161	struct tmpfs_node *node;
1162
1163	/* This operation only makes sense on directory nodes. */
1164	if (vp->v_type != VDIR)
1165		return ENOTDIR;
1166
1167	maxcookies = 0;
1168	node = VP_TO_TMPFS_DIR(vp);
1169
1170	startresid = uio->uio_resid;
1171
1172	/* Allocate cookies for NFS and compat modules. */
1173	if (cookies != NULL && ncookies != NULL) {
1174		maxcookies = howmany(node->tn_size,
1175		    sizeof(struct tmpfs_dirent)) + 2;
1176		*cookies = malloc(maxcookies * sizeof(**cookies), M_TEMP,
1177		    M_WAITOK);
1178		*ncookies = 0;
1179	}
1180
1181	if (cookies == NULL)
1182		error = tmpfs_dir_getdents(node, uio, 0, NULL, NULL);
1183	else
1184		error = tmpfs_dir_getdents(node, uio, maxcookies, *cookies,
1185		    ncookies);
1186
1187	/* Buffer was filled without hitting EOF. */
1188	if (error == EJUSTRETURN)
1189		error = (uio->uio_resid != startresid) ? 0 : EINVAL;
1190
1191	if (error != 0 && cookies != NULL)
1192		free(*cookies, M_TEMP);
1193
1194	if (eofflag != NULL)
1195		*eofflag =
1196		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1197
1198	return error;
1199}
1200
1201static int
1202tmpfs_readlink(struct vop_readlink_args *v)
1203{
1204	struct vnode *vp = v->a_vp;
1205	struct uio *uio = v->a_uio;
1206
1207	int error;
1208	struct tmpfs_node *node;
1209
1210	MPASS(uio->uio_offset == 0);
1211	MPASS(vp->v_type == VLNK);
1212
1213	node = VP_TO_TMPFS_NODE(vp);
1214
1215	error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
1216	    uio);
1217	node->tn_status |= TMPFS_NODE_ACCESSED;
1218
1219	return error;
1220}
1221
1222static int
1223tmpfs_inactive(struct vop_inactive_args *v)
1224{
1225	struct vnode *vp = v->a_vp;
1226
1227	struct tmpfs_node *node;
1228
1229	node = VP_TO_TMPFS_NODE(vp);
1230
1231	if (node->tn_links == 0)
1232		vrecycle(vp);
1233
1234	return 0;
1235}
1236
1237int
1238tmpfs_reclaim(struct vop_reclaim_args *v)
1239{
1240	struct vnode *vp = v->a_vp;
1241
1242	struct tmpfs_mount *tmp;
1243	struct tmpfs_node *node;
1244
1245	node = VP_TO_TMPFS_NODE(vp);
1246	tmp = VFS_TO_TMPFS(vp->v_mount);
1247
1248	if (vp->v_type == VREG)
1249		tmpfs_destroy_vobject(vp, node->tn_reg.tn_aobj);
1250	else
1251		vnode_destroy_vobject(vp);
1252	vp->v_object = NULL;
1253	cache_purge(vp);
1254
1255	TMPFS_NODE_LOCK(node);
1256	TMPFS_ASSERT_ELOCKED(node);
1257	tmpfs_free_vp(vp);
1258
1259	/* If the node referenced by this vnode was deleted by the user,
1260	 * we must free its associated data structures (now that the vnode
1261	 * is being reclaimed). */
1262	if (node->tn_links == 0 &&
1263	    (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
1264		node->tn_vpstate = TMPFS_VNODE_DOOMED;
1265		TMPFS_NODE_UNLOCK(node);
1266		tmpfs_free_node(tmp, node);
1267	} else
1268		TMPFS_NODE_UNLOCK(node);
1269
1270	MPASS(vp->v_data == NULL);
1271	return 0;
1272}
1273
1274static int
1275tmpfs_print(struct vop_print_args *v)
1276{
1277	struct vnode *vp = v->a_vp;
1278
1279	struct tmpfs_node *node;
1280
1281	node = VP_TO_TMPFS_NODE(vp);
1282
1283	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%lx, links %d\n",
1284	    node, node->tn_flags, node->tn_links);
1285	printf("\tmode 0%o, owner %d, group %d, size %jd, status 0x%x\n",
1286	    node->tn_mode, node->tn_uid, node->tn_gid,
1287	    (intmax_t)node->tn_size, node->tn_status);
1288
1289	if (vp->v_type == VFIFO)
1290		fifo_printinfo(vp);
1291
1292	printf("\n");
1293
1294	return 0;
1295}
1296
1297static int
1298tmpfs_pathconf(struct vop_pathconf_args *v)
1299{
1300	int name = v->a_name;
1301	register_t *retval = v->a_retval;
1302
1303	int error;
1304
1305	error = 0;
1306
1307	switch (name) {
1308	case _PC_LINK_MAX:
1309		*retval = LINK_MAX;
1310		break;
1311
1312	case _PC_NAME_MAX:
1313		*retval = NAME_MAX;
1314		break;
1315
1316	case _PC_PATH_MAX:
1317		*retval = PATH_MAX;
1318		break;
1319
1320	case _PC_PIPE_BUF:
1321		*retval = PIPE_BUF;
1322		break;
1323
1324	case _PC_CHOWN_RESTRICTED:
1325		*retval = 1;
1326		break;
1327
1328	case _PC_NO_TRUNC:
1329		*retval = 1;
1330		break;
1331
1332	case _PC_SYNC_IO:
1333		*retval = 1;
1334		break;
1335
1336	case _PC_FILESIZEBITS:
1337		*retval = 0; /* XXX Don't know which value should I return. */
1338		break;
1339
1340	default:
1341		error = EINVAL;
1342	}
1343
1344	return error;
1345}
1346
1347static int
1348tmpfs_vptofh(struct vop_vptofh_args *ap)
1349{
1350	struct tmpfs_fid *tfhp;
1351	struct tmpfs_node *node;
1352
1353	tfhp = (struct tmpfs_fid *)ap->a_fhp;
1354	node = VP_TO_TMPFS_NODE(ap->a_vp);
1355
1356	tfhp->tf_len = sizeof(struct tmpfs_fid);
1357	tfhp->tf_id = node->tn_id;
1358	tfhp->tf_gen = node->tn_gen;
1359
1360	return (0);
1361}
1362
1363static int
1364tmpfs_whiteout(struct vop_whiteout_args *ap)
1365{
1366	struct vnode *dvp = ap->a_dvp;
1367	struct componentname *cnp = ap->a_cnp;
1368	struct tmpfs_dirent *de;
1369
1370	switch (ap->a_flags) {
1371	case LOOKUP:
1372		return (0);
1373	case CREATE:
1374		de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
1375		if (de != NULL)
1376			return (de->td_node == NULL ? 0 : EEXIST);
1377		return (tmpfs_dir_whiteout_add(dvp, cnp));
1378	case DELETE:
1379		tmpfs_dir_whiteout_remove(dvp, cnp);
1380		return (0);
1381	default:
1382		panic("tmpfs_whiteout: unknown op");
1383	}
1384}
1385
1386/*
1387 * vnode operations vector used for files stored in a tmpfs file system.
1388 */
1389struct vop_vector tmpfs_vnodeop_entries = {
1390	.vop_default =			&default_vnodeops,
1391	.vop_lookup =			vfs_cache_lookup,
1392	.vop_cachedlookup =		tmpfs_lookup,
1393	.vop_create =			tmpfs_create,
1394	.vop_mknod =			tmpfs_mknod,
1395	.vop_open =			tmpfs_open,
1396	.vop_close =			tmpfs_close,
1397	.vop_access =			tmpfs_access,
1398	.vop_getattr =			tmpfs_getattr,
1399	.vop_setattr =			tmpfs_setattr,
1400	.vop_read =			tmpfs_read,
1401	.vop_write =			tmpfs_write,
1402	.vop_fsync =			tmpfs_fsync,
1403	.vop_remove =			tmpfs_remove,
1404	.vop_link =			tmpfs_link,
1405	.vop_rename =			tmpfs_rename,
1406	.vop_mkdir =			tmpfs_mkdir,
1407	.vop_rmdir =			tmpfs_rmdir,
1408	.vop_symlink =			tmpfs_symlink,
1409	.vop_readdir =			tmpfs_readdir,
1410	.vop_readlink =			tmpfs_readlink,
1411	.vop_inactive =			tmpfs_inactive,
1412	.vop_reclaim =			tmpfs_reclaim,
1413	.vop_print =			tmpfs_print,
1414	.vop_pathconf =			tmpfs_pathconf,
1415	.vop_vptofh =			tmpfs_vptofh,
1416	.vop_whiteout =			tmpfs_whiteout,
1417	.vop_bmap =			VOP_EOPNOTSUPP,
1418};
1419
1420