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