tmpfs_vnops.c revision 175294
1228940Sdelphij/*	$NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $	*/
2228940Sdelphij
3228940Sdelphij/*
4228940Sdelphij * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
5228940Sdelphij * All rights reserved.
6228940Sdelphij *
7228940Sdelphij * This code is derived from software contributed to The NetBSD Foundation
8228940Sdelphij * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9228940Sdelphij * 2005 program.
10228940Sdelphij *
11228940Sdelphij * Redistribution and use in source and binary forms, with or without
12228940Sdelphij * modification, are permitted provided that the following conditions
13228940Sdelphij * are met:
14228940Sdelphij * 1. Redistributions of source code must retain the above copyright
15228940Sdelphij *    notice, this list of conditions and the following disclaimer.
16228940Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
17228940Sdelphij *    notice, this list of conditions and the following disclaimer in the
18228940Sdelphij *    documentation and/or other materials provided with the distribution.
19228940Sdelphij * 3. All advertising materials mentioning features or use of this software
20228940Sdelphij *    must display the following acknowledgement:
21228940Sdelphij *        This product includes software developed by the NetBSD
22228940Sdelphij *        Foundation, Inc. and its contributors.
23228940Sdelphij * 4. Neither the name of The NetBSD Foundation nor the names of its
24228940Sdelphij *    contributors may be used to endorse or promote products derived
25228940Sdelphij *    from this software without specific prior written permission.
26228940Sdelphij *
27228940Sdelphij * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28228940Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29228940Sdelphij * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30228940Sdelphij * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31228940Sdelphij * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32228940Sdelphij * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33228940Sdelphij * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34228940Sdelphij * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35228940Sdelphij * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36228940Sdelphij * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37228940Sdelphij * POSSIBILITY OF SUCH DAMAGE.
38228940Sdelphij */
39228940Sdelphij
40228940Sdelphij/*
41228940Sdelphij * tmpfs vnode interface.
42228940Sdelphij */
43228940Sdelphij#include <sys/cdefs.h>
44228940Sdelphij__FBSDID("$FreeBSD: head/sys/fs/tmpfs/tmpfs_vnops.c 175294 2008-01-13 14:44:15Z attilio $");
45228940Sdelphij
46228940Sdelphij#include <sys/param.h>
47228940Sdelphij#include <sys/fcntl.h>
48228940Sdelphij#include <sys/lockf.h>
49228940Sdelphij#include <sys/namei.h>
50228940Sdelphij#include <sys/priv.h>
51228940Sdelphij#include <sys/proc.h>
52228940Sdelphij#include <sys/resourcevar.h>
53228940Sdelphij#include <sys/stat.h>
54228940Sdelphij#include <sys/systm.h>
55255320Sdelphij#include <sys/unistd.h>
56228940Sdelphij#include <sys/vnode.h>
57228940Sdelphij
58228940Sdelphij#include <vm/vm.h>
59228940Sdelphij#include <vm/vm_object.h>
60228940Sdelphij#include <vm/vm_page.h>
61228940Sdelphij#include <vm/vm_pager.h>
62228940Sdelphij#include <sys/sched.h>
63228940Sdelphij#include <sys/sf_buf.h>
64228940Sdelphij#include <machine/_inttypes.h>
65228940Sdelphij
66228940Sdelphij#include <fs/fifofs/fifo.h>
67228940Sdelphij#include <fs/tmpfs/tmpfs_vnops.h>
68228940Sdelphij#include <fs/tmpfs/tmpfs.h>
69228940Sdelphij
70228940Sdelphij/* --------------------------------------------------------------------- */
71228940Sdelphij
72228940Sdelphijstatic int
73228940Sdelphijtmpfs_lookup(struct vop_cachedlookup_args *v)
74228940Sdelphij{
75228940Sdelphij	struct vnode *dvp = v->a_dvp;
76228940Sdelphij	struct vnode **vpp = v->a_vpp;
77228940Sdelphij	struct componentname *cnp = v->a_cnp;
78228940Sdelphij	struct thread *td = cnp->cn_thread;
79228940Sdelphij
80228940Sdelphij	int error;
81228940Sdelphij	struct tmpfs_dirent *de;
82228940Sdelphij	struct tmpfs_node *dnode;
83228940Sdelphij
84228940Sdelphij	dnode = VP_TO_TMPFS_DIR(dvp);
85228940Sdelphij	*vpp = NULLVP;
86228940Sdelphij
87228940Sdelphij	/* Check accessibility of requested node as a first step. */
88228940Sdelphij	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td);
89228940Sdelphij	if (error != 0)
90228940Sdelphij		goto out;
91228940Sdelphij
92228940Sdelphij	/* We cannot be requesting the parent directory of the root node. */
93228940Sdelphij	MPASS(IMPLIES(dnode->tn_type == VDIR &&
94228940Sdelphij	    dnode->tn_dir.tn_parent == dnode,
95228940Sdelphij	    !(cnp->cn_flags & ISDOTDOT)));
96228940Sdelphij
97228940Sdelphij	if (cnp->cn_flags & ISDOTDOT) {
98228940Sdelphij		int ltype = 0;
99228940Sdelphij
100228940Sdelphij		ltype = VOP_ISLOCKED(dvp, td);
101228940Sdelphij		vhold(dvp);
102228940Sdelphij		VOP_UNLOCK(dvp, 0);
103228940Sdelphij		/* Allocate a new vnode on the matching entry. */
104228940Sdelphij		error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent,
105228940Sdelphij		    cnp->cn_lkflags, vpp, td);
106228940Sdelphij
107228940Sdelphij		vn_lock(dvp, ltype | LK_RETRY);
108228940Sdelphij		vdrop(dvp);
109228940Sdelphij	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
110228940Sdelphij		VREF(dvp);
111228940Sdelphij		*vpp = dvp;
112228940Sdelphij		error = 0;
113228940Sdelphij	} else {
114228940Sdelphij		de = tmpfs_dir_lookup(dnode, cnp);
115228940Sdelphij		if (de == NULL) {
116228940Sdelphij			/* The entry was not found in the directory.
117228940Sdelphij			 * This is OK if we are creating or renaming an
118228940Sdelphij			 * entry and are working on the last component of
119228940Sdelphij			 * the path name. */
120228940Sdelphij			if ((cnp->cn_flags & ISLASTCN) &&
121228940Sdelphij			    (cnp->cn_nameiop == CREATE || \
122228940Sdelphij			    cnp->cn_nameiop == RENAME)) {
123228940Sdelphij				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
124228940Sdelphij				    cnp->cn_thread);
125228940Sdelphij				if (error != 0)
126228940Sdelphij					goto out;
127228940Sdelphij
128228940Sdelphij				/* Keep the component name in the buffer for
129228940Sdelphij				 * future uses. */
130228940Sdelphij				cnp->cn_flags |= SAVENAME;
131228940Sdelphij
132228940Sdelphij				error = EJUSTRETURN;
133228940Sdelphij			} else
134228940Sdelphij				error = ENOENT;
135228940Sdelphij		} else {
136228940Sdelphij			struct tmpfs_node *tnode;
137228940Sdelphij
138228940Sdelphij			/* The entry was found, so get its associated
139228940Sdelphij			 * tmpfs_node. */
140228940Sdelphij			tnode = de->td_node;
141228940Sdelphij
142228940Sdelphij			/* If we are not at the last path component and
143228940Sdelphij			 * found a non-directory or non-link entry (which
144228940Sdelphij			 * may itself be pointing to a directory), raise
145228940Sdelphij			 * an error. */
146228940Sdelphij			if ((tnode->tn_type != VDIR &&
147228940Sdelphij			    tnode->tn_type != VLNK) &&
148228940Sdelphij			    !(cnp->cn_flags & ISLASTCN)) {
149228940Sdelphij				error = ENOTDIR;
150228940Sdelphij				goto out;
151228940Sdelphij			}
152228940Sdelphij
153228940Sdelphij			/* If we are deleting or renaming the entry, keep
154228940Sdelphij			 * track of its tmpfs_dirent so that it can be
155228940Sdelphij			 * easily deleted later. */
156228940Sdelphij			if ((cnp->cn_flags & ISLASTCN) &&
157228940Sdelphij			    (cnp->cn_nameiop == DELETE ||
158228940Sdelphij			    cnp->cn_nameiop == RENAME)) {
159228940Sdelphij				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
160228940Sdelphij				    cnp->cn_thread);
161228940Sdelphij				if (error != 0)
162228940Sdelphij					goto out;
163228940Sdelphij
164228940Sdelphij				/* Allocate a new vnode on the matching entry. */
165228940Sdelphij				error = tmpfs_alloc_vp(dvp->v_mount, tnode,
166228940Sdelphij						cnp->cn_lkflags, vpp, td);
167228940Sdelphij				if (error != 0)
168228940Sdelphij					goto out;
169228940Sdelphij
170245768Sdelphij				if ((dnode->tn_mode & S_ISTXT) &&
171245768Sdelphij				  VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) &&
172228940Sdelphij				  VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) {
173228940Sdelphij					error = EPERM;
174228940Sdelphij					vput(*vpp);
175228940Sdelphij					*vpp = NULL;
176228940Sdelphij					goto out;
177228940Sdelphij				}
178228940Sdelphij				cnp->cn_flags |= SAVENAME;
179228940Sdelphij			} else {
180228940Sdelphij				error = tmpfs_alloc_vp(dvp->v_mount, tnode,
181228940Sdelphij						cnp->cn_lkflags, vpp, td);
182228940Sdelphij			}
183228940Sdelphij		}
184228940Sdelphij	}
185228940Sdelphij
186228940Sdelphij	/* Store the result of this lookup in the cache.  Avoid this if the
187228940Sdelphij	 * request was for creation, as it does not improve timings on
188228940Sdelphij	 * emprical tests. */
189228940Sdelphij	if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
190228940Sdelphij		cache_enter(dvp, *vpp, cnp);
191228940Sdelphij
192228940Sdelphijout:
193228940Sdelphij	/* If there were no errors, *vpp cannot be null and it must be
194228940Sdelphij	 * locked. */
195228940Sdelphij	MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp, td)));
196228940Sdelphij
197228940Sdelphij	return error;
198228940Sdelphij}
199228940Sdelphij
200228940Sdelphij/* --------------------------------------------------------------------- */
201228940Sdelphij
202228940Sdelphijstatic int
203228940Sdelphijtmpfs_create(struct vop_create_args *v)
204228940Sdelphij{
205228940Sdelphij	struct vnode *dvp = v->a_dvp;
206228940Sdelphij	struct vnode **vpp = v->a_vpp;
207228940Sdelphij	struct componentname *cnp = v->a_cnp;
208228940Sdelphij	struct vattr *vap = v->a_vap;
209228940Sdelphij
210228940Sdelphij	MPASS(vap->va_type == VREG || vap->va_type == VSOCK);
211228940Sdelphij
212228940Sdelphij	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
213228940Sdelphij}
214228940Sdelphij/* --------------------------------------------------------------------- */
215228940Sdelphij
216228940Sdelphijstatic int
217228940Sdelphijtmpfs_mknod(struct vop_mknod_args *v)
218228940Sdelphij{
219228940Sdelphij	struct vnode *dvp = v->a_dvp;
220228940Sdelphij	struct vnode **vpp = v->a_vpp;
221228940Sdelphij	struct componentname *cnp = v->a_cnp;
222228940Sdelphij	struct vattr *vap = v->a_vap;
223228940Sdelphij
224228940Sdelphij	if (vap->va_type != VBLK && vap->va_type != VCHR &&
225228940Sdelphij	    vap->va_type != VFIFO)
226228940Sdelphij		return EINVAL;
227228940Sdelphij
228228940Sdelphij	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
229228940Sdelphij}
230228940Sdelphij
231228940Sdelphij/* --------------------------------------------------------------------- */
232228940Sdelphij
233228940Sdelphijstatic int
234228940Sdelphijtmpfs_open(struct vop_open_args *v)
235228940Sdelphij{
236228940Sdelphij	struct vnode *vp = v->a_vp;
237228940Sdelphij	int mode = v->a_mode;
238228940Sdelphij
239228940Sdelphij	int error;
240228940Sdelphij	struct tmpfs_node *node;
241228940Sdelphij
242228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
243228940Sdelphij
244228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
245228940Sdelphij
246228940Sdelphij	/* The file is still active but all its names have been removed
247228940Sdelphij	 * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
248228940Sdelphij	 * it is about to die. */
249228940Sdelphij	if (node->tn_links < 1)
250228940Sdelphij		return (ENOENT);
251228940Sdelphij
252228940Sdelphij	/* If the file is marked append-only, deny write requests. */
253228940Sdelphij	if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
254228940Sdelphij		error = EPERM;
255228940Sdelphij	else {
256228940Sdelphij		error = 0;
257228940Sdelphij		vnode_create_vobject(vp, node->tn_size, v->a_td);
258228940Sdelphij	}
259228940Sdelphij
260228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
261228940Sdelphij	return error;
262228940Sdelphij}
263228940Sdelphij
264228940Sdelphij/* --------------------------------------------------------------------- */
265228940Sdelphij
266228940Sdelphijstatic int
267228940Sdelphijtmpfs_close(struct vop_close_args *v)
268228940Sdelphij{
269228940Sdelphij	struct vnode *vp = v->a_vp;
270228940Sdelphij
271228940Sdelphij	struct tmpfs_node *node;
272228940Sdelphij
273228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
274228940Sdelphij
275228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
276228940Sdelphij
277228940Sdelphij	if (node->tn_links > 0) {
278228940Sdelphij		/* Update node times.  No need to do it if the node has
279228940Sdelphij		 * been deleted, because it will vanish after we return. */
280228940Sdelphij		tmpfs_update(vp);
281228940Sdelphij	}
282228940Sdelphij
283228940Sdelphij	return 0;
284228940Sdelphij}
285228940Sdelphij
286228940Sdelphij/* --------------------------------------------------------------------- */
287228940Sdelphij
288228940Sdelphijint
289228940Sdelphijtmpfs_access(struct vop_access_args *v)
290228940Sdelphij{
291228940Sdelphij	struct vnode *vp = v->a_vp;
292228940Sdelphij	int mode = v->a_mode;
293228940Sdelphij	struct ucred *cred = v->a_cred;
294228940Sdelphij
295228940Sdelphij	int error;
296228940Sdelphij	struct tmpfs_node *node;
297228940Sdelphij
298228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
299228940Sdelphij
300228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
301228940Sdelphij
302228940Sdelphij	switch (vp->v_type) {
303228940Sdelphij	case VDIR:
304228940Sdelphij		/* FALLTHROUGH */
305228940Sdelphij	case VLNK:
306228940Sdelphij		/* FALLTHROUGH */
307228940Sdelphij	case VREG:
308228940Sdelphij		if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
309228940Sdelphij			error = EROFS;
310228940Sdelphij			goto out;
311228940Sdelphij		}
312228940Sdelphij		break;
313228940Sdelphij
314228940Sdelphij	case VBLK:
315228940Sdelphij		/* FALLTHROUGH */
316228940Sdelphij	case VCHR:
317228940Sdelphij		/* FALLTHROUGH */
318228940Sdelphij	case VSOCK:
319228940Sdelphij		/* FALLTHROUGH */
320228940Sdelphij	case VFIFO:
321228940Sdelphij		break;
322228940Sdelphij
323228940Sdelphij	default:
324228940Sdelphij		error = EINVAL;
325228940Sdelphij		goto out;
326228940Sdelphij	}
327228940Sdelphij
328228940Sdelphij	if (mode & VWRITE && node->tn_flags & IMMUTABLE) {
329228940Sdelphij		error = EPERM;
330228940Sdelphij		goto out;
331228940Sdelphij	}
332228940Sdelphij
333228940Sdelphij	error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
334228940Sdelphij	    node->tn_gid, mode, cred, NULL);
335228940Sdelphij
336228940Sdelphijout:
337228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
338228940Sdelphij
339228940Sdelphij	return error;
340228940Sdelphij}
341228940Sdelphij
342228940Sdelphij/* --------------------------------------------------------------------- */
343228940Sdelphij
344228940Sdelphijint
345228940Sdelphijtmpfs_getattr(struct vop_getattr_args *v)
346228940Sdelphij{
347228940Sdelphij	struct vnode *vp = v->a_vp;
348228940Sdelphij	struct vattr *vap = v->a_vap;
349228940Sdelphij
350228940Sdelphij	struct tmpfs_node *node;
351228940Sdelphij
352228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
353228940Sdelphij
354228940Sdelphij	VATTR_NULL(vap);
355228940Sdelphij
356228940Sdelphij	tmpfs_update(vp);
357228940Sdelphij
358228940Sdelphij	vap->va_type = vp->v_type;
359228940Sdelphij	vap->va_mode = node->tn_mode;
360228940Sdelphij	vap->va_nlink = node->tn_links;
361228940Sdelphij	vap->va_uid = node->tn_uid;
362228940Sdelphij	vap->va_gid = node->tn_gid;
363228940Sdelphij	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
364228940Sdelphij	vap->va_fileid = node->tn_id;
365228940Sdelphij	vap->va_size = node->tn_size;
366228940Sdelphij	vap->va_blocksize = PAGE_SIZE;
367228940Sdelphij	vap->va_atime = node->tn_atime;
368228940Sdelphij	vap->va_mtime = node->tn_mtime;
369228940Sdelphij	vap->va_ctime = node->tn_ctime;
370228940Sdelphij	vap->va_birthtime = node->tn_birthtime;
371228940Sdelphij	vap->va_gen = node->tn_gen;
372228940Sdelphij	vap->va_flags = node->tn_flags;
373228940Sdelphij	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
374228940Sdelphij		node->tn_rdev : VNOVAL;
375228940Sdelphij	vap->va_bytes = round_page(node->tn_size);
376228940Sdelphij	vap->va_filerev = VNOVAL;
377228940Sdelphij	vap->va_vaflags = 0;
378228940Sdelphij	vap->va_spare = VNOVAL; /* XXX */
379228940Sdelphij
380228940Sdelphij	return 0;
381228940Sdelphij}
382228940Sdelphij
383228940Sdelphij/* --------------------------------------------------------------------- */
384228940Sdelphij
385228940Sdelphij/* XXX Should this operation be atomic?  I think it should, but code in
386228940Sdelphij * XXX other places (e.g., ufs) doesn't seem to be... */
387228940Sdelphijint
388228940Sdelphijtmpfs_setattr(struct vop_setattr_args *v)
389228940Sdelphij{
390228940Sdelphij	struct vnode *vp = v->a_vp;
391228940Sdelphij	struct vattr *vap = v->a_vap;
392228940Sdelphij	struct ucred *cred = v->a_cred;
393228940Sdelphij	struct thread *l = v->a_td;
394228940Sdelphij
395228940Sdelphij	int error;
396228940Sdelphij
397228940Sdelphij	MPASS(VOP_ISLOCKED(vp, l));
398228940Sdelphij
399228940Sdelphij	error = 0;
400228940Sdelphij
401228940Sdelphij	/* Abort if any unsettable attribute is given. */
402228940Sdelphij	if (vap->va_type != VNON ||
403228940Sdelphij	    vap->va_nlink != VNOVAL ||
404228940Sdelphij	    vap->va_fsid != VNOVAL ||
405228940Sdelphij	    vap->va_fileid != VNOVAL ||
406228940Sdelphij	    vap->va_blocksize != VNOVAL ||
407228940Sdelphij	    vap->va_gen != VNOVAL ||
408228940Sdelphij	    vap->va_rdev != VNOVAL ||
409228940Sdelphij	    vap->va_bytes != VNOVAL)
410228940Sdelphij		error = EINVAL;
411228940Sdelphij
412228940Sdelphij	if (error == 0 && (vap->va_flags != VNOVAL))
413228940Sdelphij		error = tmpfs_chflags(vp, vap->va_flags, cred, l);
414228940Sdelphij
415228940Sdelphij	if (error == 0 && (vap->va_size != VNOVAL))
416228940Sdelphij		error = tmpfs_chsize(vp, vap->va_size, cred, l);
417228940Sdelphij
418228940Sdelphij	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
419228940Sdelphij		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred,
420228940Sdelphij		    l);
421228940Sdelphij
422228940Sdelphij	if (error == 0 && (vap->va_mode != (mode_t)VNOVAL))
423228940Sdelphij		error = tmpfs_chmod(vp, vap->va_mode, cred, l);
424228940Sdelphij
425228940Sdelphij	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
426228940Sdelphij	    vap->va_atime.tv_nsec != VNOVAL) ||
427228940Sdelphij	    (vap->va_mtime.tv_sec != VNOVAL &&
428228940Sdelphij	    vap->va_mtime.tv_nsec != VNOVAL) ||
429228940Sdelphij	    (vap->va_birthtime.tv_sec != VNOVAL &&
430228940Sdelphij	    vap->va_birthtime.tv_nsec != VNOVAL)))
431228940Sdelphij		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
432228940Sdelphij			&vap->va_birthtime, vap->va_vaflags, cred, l);
433228940Sdelphij
434228940Sdelphij	/* Update the node times.  We give preference to the error codes
435255871Sscottl	 * generated by this function rather than the ones that may arise
436228940Sdelphij	 * from tmpfs_update. */
437228940Sdelphij	tmpfs_update(vp);
438228940Sdelphij
439228940Sdelphij	MPASS(VOP_ISLOCKED(vp, l));
440228940Sdelphij
441228940Sdelphij	return error;
442228940Sdelphij}
443228940Sdelphij
444228940Sdelphij/* --------------------------------------------------------------------- */
445228940Sdelphij
446228940Sdelphijstatic int
447228940Sdelphijtmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
448228940Sdelphij{
449228940Sdelphij	vm_pindex_t	idx;
450228940Sdelphij	vm_page_t	m;
451228940Sdelphij	struct sf_buf	*sf;
452228940Sdelphij	off_t		offset, addr;
453228940Sdelphij	size_t		tlen;
454228940Sdelphij	caddr_t		va;
455228940Sdelphij	int		error;
456228940Sdelphij
457228940Sdelphij	addr = uio->uio_offset;
458228940Sdelphij	idx = OFF_TO_IDX(addr);
459228940Sdelphij	offset = addr & PAGE_MASK;
460228940Sdelphij	tlen = MIN(PAGE_SIZE - offset, len);
461228940Sdelphij
462228940Sdelphij	if ((vobj == NULL) || (vobj->resident_page_count == 0))
463228940Sdelphij		goto nocache;
464228940Sdelphij
465228940Sdelphij	VM_OBJECT_LOCK(vobj);
466228940Sdelphijlookupvpg:
467228940Sdelphij	if (((m = vm_page_lookup(vobj, idx)) != NULL) &&
468228940Sdelphij	    vm_page_is_valid(m, offset, tlen)) {
469228940Sdelphij		if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr"))
470228940Sdelphij			goto lookupvpg;
471228940Sdelphij		vm_page_busy(m);
472228940Sdelphij		VM_OBJECT_UNLOCK(vobj);
473228940Sdelphij		sched_pin();
474228940Sdelphij		sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
475228940Sdelphij		va = (caddr_t)sf_buf_kva(sf);
476228940Sdelphij		error = uiomove(va + offset, tlen, uio);
477228940Sdelphij		sf_buf_free(sf);
478228940Sdelphij		sched_unpin();
479228940Sdelphij		VM_OBJECT_LOCK(vobj);
480228940Sdelphij		vm_page_wakeup(m);
481228940Sdelphij		VM_OBJECT_UNLOCK(vobj);
482228940Sdelphij		return	(error);
483228940Sdelphij	}
484228940Sdelphij	VM_OBJECT_UNLOCK(vobj);
485228940Sdelphijnocache:
486228940Sdelphij	VM_OBJECT_LOCK(tobj);
487228940Sdelphij	vm_object_pip_add(tobj, 1);
488228940Sdelphij	m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
489228940Sdelphij	    VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
490228940Sdelphij	if (m->valid != VM_PAGE_BITS_ALL) {
491246713Skib		int behind, ahead;
492228940Sdelphij		if (vm_pager_has_page(tobj, idx, &behind, &ahead)) {
493228940Sdelphij			error = vm_pager_get_pages(tobj, &m, 1, 0);
494228940Sdelphij			if (error != 0) {
495228940Sdelphij				printf("tmpfs get pages from pager error [read]\n");
496246713Skib				goto out;
497246713Skib			}
498246713Skib		} else
499246713Skib			vm_page_zero_invalid(m, TRUE);
500246713Skib	}
501246713Skib	VM_OBJECT_UNLOCK(tobj);
502246713Skib	sched_pin();
503228940Sdelphij	sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
504246713Skib	va = (caddr_t)sf_buf_kva(sf);
505246713Skib	error = uiomove(va + offset, tlen, uio);
506246713Skib	sf_buf_free(sf);
507246713Skib	sched_unpin();
508246713Skib	VM_OBJECT_LOCK(tobj);
509246713Skibout:
510246713Skib	vm_page_lock_queues();
511246713Skib	vm_page_unwire(m, 0);
512228940Sdelphij	vm_page_activate(m);
513255871Sscottl	vm_page_unlock_queues();
514228940Sdelphij	vm_page_wakeup(m);
515228940Sdelphij	vm_object_pip_subtract(tobj, 1);
516228940Sdelphij	VM_OBJECT_UNLOCK(tobj);
517228940Sdelphij
518228940Sdelphij	return	(error);
519228940Sdelphij}
520228940Sdelphij
521228940Sdelphijstatic int
522228940Sdelphijtmpfs_read(struct vop_read_args *v)
523228940Sdelphij{
524228940Sdelphij	struct vnode *vp = v->a_vp;
525228940Sdelphij	struct uio *uio = v->a_uio;
526228940Sdelphij
527228940Sdelphij	struct tmpfs_node *node;
528228940Sdelphij	vm_object_t uobj;
529228940Sdelphij	size_t len;
530228940Sdelphij	int resid;
531228940Sdelphij
532228940Sdelphij	int error = 0;
533228940Sdelphij
534228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
535228940Sdelphij
536228940Sdelphij	if (vp->v_type != VREG) {
537228940Sdelphij		error = EISDIR;
538228940Sdelphij		goto out;
539228940Sdelphij	}
540228940Sdelphij
541228940Sdelphij	if (uio->uio_offset < 0) {
542228940Sdelphij		error = EINVAL;
543228940Sdelphij		goto out;
544228940Sdelphij	}
545228940Sdelphij
546228940Sdelphij	node->tn_status |= TMPFS_NODE_ACCESSED;
547228940Sdelphij
548228940Sdelphij	uobj = node->tn_reg.tn_aobj;
549228940Sdelphij	while ((resid = uio->uio_resid) > 0) {
550228940Sdelphij		error = 0;
551228940Sdelphij		if (node->tn_size <= uio->uio_offset)
552228940Sdelphij			break;
553228940Sdelphij		len = MIN(node->tn_size - uio->uio_offset, resid);
554228940Sdelphij		if (len == 0)
555228940Sdelphij			break;
556228940Sdelphij		error = tmpfs_mappedread(vp->v_object, uobj, len, uio);
557228940Sdelphij		if ((error != 0) || (resid == uio->uio_resid))
558228940Sdelphij			break;
559228940Sdelphij	}
560228940Sdelphij
561228940Sdelphijout:
562228940Sdelphij
563228940Sdelphij	return error;
564228940Sdelphij}
565228940Sdelphij
566228940Sdelphij/* --------------------------------------------------------------------- */
567228940Sdelphij
568228940Sdelphijstatic int
569228940Sdelphijtmpfs_mappedwrite(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
570228940Sdelphij{
571228940Sdelphij	vm_pindex_t	idx;
572228940Sdelphij	vm_page_t	vpg, tpg;
573228940Sdelphij	struct sf_buf	*sf;
574228940Sdelphij	off_t		offset, addr;
575228940Sdelphij	size_t		tlen;
576228940Sdelphij	caddr_t		va;
577228940Sdelphij	int		error;
578228940Sdelphij
579228940Sdelphij	error = 0;
580228940Sdelphij
581228940Sdelphij	addr = uio->uio_offset;
582228940Sdelphij	idx = OFF_TO_IDX(addr);
583228940Sdelphij	offset = addr & PAGE_MASK;
584228940Sdelphij	tlen = MIN(PAGE_SIZE - offset, len);
585228940Sdelphij
586228940Sdelphij	if ((vobj == NULL) || (vobj->resident_page_count == 0)) {
587228940Sdelphij		vpg = NULL;
588228940Sdelphij		goto nocache;
589228940Sdelphij	}
590228940Sdelphij
591228940Sdelphij	VM_OBJECT_LOCK(vobj);
592228940Sdelphijlookupvpg:
593228940Sdelphij	if (((vpg = vm_page_lookup(vobj, idx)) != NULL) &&
594228940Sdelphij	    vm_page_is_valid(vpg, offset, tlen)) {
595228940Sdelphij		if (vm_page_sleep_if_busy(vpg, FALSE, "tmfsmw"))
596228940Sdelphij			goto lookupvpg;
597228940Sdelphij		vm_page_busy(vpg);
598228940Sdelphij		vm_page_lock_queues();
599228940Sdelphij		vm_page_undirty(vpg);
600228940Sdelphij		vm_page_unlock_queues();
601228940Sdelphij		VM_OBJECT_UNLOCK(vobj);
602228940Sdelphij		sched_pin();
603228940Sdelphij		sf = sf_buf_alloc(vpg, SFB_CPUPRIVATE);
604228940Sdelphij		va = (caddr_t)sf_buf_kva(sf);
605228940Sdelphij		error = uiomove(va + offset, tlen, uio);
606228940Sdelphij		sf_buf_free(sf);
607228940Sdelphij		sched_unpin();
608228940Sdelphij	} else {
609228940Sdelphij		VM_OBJECT_UNLOCK(vobj);
610228940Sdelphij		vpg = NULL;
611228940Sdelphij	}
612228940Sdelphijnocache:
613228940Sdelphij	VM_OBJECT_LOCK(tobj);
614228940Sdelphij	vm_object_pip_add(tobj, 1);
615228940Sdelphij	tpg = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
616228940Sdelphij	    VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
617228940Sdelphij	if (tpg->valid != VM_PAGE_BITS_ALL) {
618228940Sdelphij		int behind, ahead;
619228940Sdelphij		if (vm_pager_has_page(tobj, idx, &behind, &ahead)) {
620228940Sdelphij			error = vm_pager_get_pages(tobj, &tpg, 1, 0);
621228940Sdelphij			if (error != 0) {
622228940Sdelphij				printf("tmpfs get pages from pager error [write]\n");
623228940Sdelphij				goto out;
624228940Sdelphij			}
625228940Sdelphij		} else
626228940Sdelphij			vm_page_zero_invalid(tpg, TRUE);
627228940Sdelphij	}
628228940Sdelphij	VM_OBJECT_UNLOCK(tobj);
629228940Sdelphij	if (vpg == NULL) {
630228940Sdelphij		sched_pin();
631228940Sdelphij		sf = sf_buf_alloc(tpg, SFB_CPUPRIVATE);
632228940Sdelphij		va = (caddr_t)sf_buf_kva(sf);
633228940Sdelphij		error = uiomove(va + offset, tlen, uio);
634228940Sdelphij		sf_buf_free(sf);
635228940Sdelphij		sched_unpin();
636228940Sdelphij	} else {
637228940Sdelphij		KASSERT(vpg->valid == VM_PAGE_BITS_ALL, ("parts of vpg invalid"));
638228940Sdelphij		pmap_copy_page(vpg, tpg);
639228940Sdelphij	}
640246713Skib	VM_OBJECT_LOCK(tobj);
641228940Sdelphijout:
642228940Sdelphij	if (vobj != NULL)
643228940Sdelphij		VM_OBJECT_LOCK(vobj);
644228940Sdelphij	vm_page_lock_queues();
645228940Sdelphij	if (error == 0) {
646228940Sdelphij		vm_page_set_validclean(tpg, offset, tlen);
647228940Sdelphij		vm_page_zero_invalid(tpg, TRUE);
648228940Sdelphij		vm_page_dirty(tpg);
649228940Sdelphij	}
650228940Sdelphij	vm_page_unwire(tpg, 0);
651228940Sdelphij	vm_page_activate(tpg);
652228940Sdelphij	vm_page_unlock_queues();
653228940Sdelphij	vm_page_wakeup(tpg);
654228940Sdelphij	if (vpg != NULL)
655228940Sdelphij		vm_page_wakeup(vpg);
656228940Sdelphij	if (vobj != NULL)
657228940Sdelphij		VM_OBJECT_UNLOCK(vobj);
658228940Sdelphij	vm_object_pip_subtract(tobj, 1);
659228940Sdelphij	VM_OBJECT_UNLOCK(tobj);
660228940Sdelphij
661228940Sdelphij	return	(error);
662228940Sdelphij}
663228940Sdelphij
664228940Sdelphijstatic int
665228940Sdelphijtmpfs_write(struct vop_write_args *v)
666228940Sdelphij{
667228940Sdelphij	struct vnode *vp = v->a_vp;
668228940Sdelphij	struct uio *uio = v->a_uio;
669228940Sdelphij	int ioflag = v->a_ioflag;
670228940Sdelphij	struct thread *td = uio->uio_td;
671228940Sdelphij
672228940Sdelphij	boolean_t extended;
673228940Sdelphij	int error = 0;
674228940Sdelphij	off_t oldsize;
675228940Sdelphij	struct tmpfs_node *node;
676228940Sdelphij	vm_object_t uobj;
677228940Sdelphij	size_t len;
678228940Sdelphij	int resid;
679228940Sdelphij
680228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
681228940Sdelphij	oldsize = node->tn_size;
682228940Sdelphij
683228940Sdelphij	if (uio->uio_offset < 0 || vp->v_type != VREG) {
684228940Sdelphij		error = EINVAL;
685228940Sdelphij		goto out;
686228940Sdelphij	}
687228940Sdelphij
688228940Sdelphij	if (uio->uio_resid == 0) {
689228940Sdelphij		error = 0;
690228940Sdelphij		goto out;
691228940Sdelphij	}
692228940Sdelphij
693228940Sdelphij	if (ioflag & IO_APPEND)
694228940Sdelphij		uio->uio_offset = node->tn_size;
695228940Sdelphij
696228940Sdelphij	if (uio->uio_offset + uio->uio_resid >
697228940Sdelphij	  VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
698246713Skib		return (EFBIG);
699246713Skib
700246713Skib	if (vp->v_type == VREG && td != NULL) {
701246713Skib		PROC_LOCK(td->td_proc);
702228940Sdelphij		if (uio->uio_offset + uio->uio_resid >
703228940Sdelphij		  lim_cur(td->td_proc, RLIMIT_FSIZE)) {
704246713Skib			psignal(td->td_proc, SIGXFSZ);
705246713Skib			PROC_UNLOCK(td->td_proc);
706246713Skib			return (EFBIG);
707246713Skib		}
708246713Skib		PROC_UNLOCK(td->td_proc);
709246713Skib	}
710246713Skib
711228940Sdelphij	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
712228940Sdelphij	if (extended) {
713228940Sdelphij		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
714228940Sdelphij		if (error != 0)
715228940Sdelphij			goto out;
716228940Sdelphij	}
717228940Sdelphij
718228940Sdelphij	uobj = node->tn_reg.tn_aobj;
719228940Sdelphij	while ((resid = uio->uio_resid) > 0) {
720228940Sdelphij		if (node->tn_size <= uio->uio_offset)
721228940Sdelphij			break;
722228940Sdelphij		len = MIN(node->tn_size - uio->uio_offset, resid);
723228940Sdelphij		if (len == 0)
724228940Sdelphij			break;
725228940Sdelphij		error = tmpfs_mappedwrite(vp->v_object, uobj, len, uio);
726228940Sdelphij		if ((error != 0) || (resid == uio->uio_resid))
727228940Sdelphij			break;
728228940Sdelphij	}
729228940Sdelphij
730228940Sdelphij	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
731228940Sdelphij	    (extended ? TMPFS_NODE_CHANGED : 0);
732228940Sdelphij
733228940Sdelphij	if (node->tn_mode & (S_ISUID | S_ISGID)) {
734228940Sdelphij		if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
735228940Sdelphij			node->tn_mode &= ~(S_ISUID | S_ISGID);
736228940Sdelphij	}
737228940Sdelphij
738228940Sdelphij	if (error != 0)
739228940Sdelphij		(void)tmpfs_reg_resize(vp, oldsize);
740228940Sdelphij
741228940Sdelphijout:
742228940Sdelphij	MPASS(IMPLIES(error == 0, uio->uio_resid == 0));
743228940Sdelphij	MPASS(IMPLIES(error != 0, oldsize == node->tn_size));
744228940Sdelphij
745228940Sdelphij	return error;
746228940Sdelphij}
747228940Sdelphij
748228940Sdelphij/* --------------------------------------------------------------------- */
749228940Sdelphij
750228940Sdelphijstatic int
751228940Sdelphijtmpfs_fsync(struct vop_fsync_args *v)
752228940Sdelphij{
753228940Sdelphij	struct vnode *vp = v->a_vp;
754228940Sdelphij
755228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_td));
756228940Sdelphij
757228940Sdelphij	tmpfs_update(vp);
758228940Sdelphij
759228940Sdelphij	return 0;
760228940Sdelphij}
761228940Sdelphij
762228940Sdelphij/* --------------------------------------------------------------------- */
763228940Sdelphij
764228940Sdelphijstatic int
765228940Sdelphijtmpfs_remove(struct vop_remove_args *v)
766228940Sdelphij{
767228940Sdelphij	struct vnode *dvp = v->a_dvp;
768228940Sdelphij	struct vnode *vp = v->a_vp;
769228940Sdelphij
770228940Sdelphij	int error;
771228940Sdelphij	struct tmpfs_dirent *de;
772228940Sdelphij	struct tmpfs_mount *tmp;
773228940Sdelphij	struct tmpfs_node *dnode;
774228940Sdelphij	struct tmpfs_node *node;
775228940Sdelphij
776228940Sdelphij	MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread));
777228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread));
778228940Sdelphij
779228940Sdelphij	if (vp->v_type == VDIR) {
780228940Sdelphij		error = EISDIR;
781228940Sdelphij		goto out;
782228940Sdelphij	}
783228940Sdelphij
784228940Sdelphij	dnode = VP_TO_TMPFS_DIR(dvp);
785228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
786228940Sdelphij	tmp = VFS_TO_TMPFS(vp->v_mount);
787228940Sdelphij	de = tmpfs_dir_search(dnode, node);
788228940Sdelphij	MPASS(de != NULL);
789228940Sdelphij
790228940Sdelphij	/* Files marked as immutable or append-only cannot be deleted. */
791228940Sdelphij	if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
792228940Sdelphij	    (dnode->tn_flags & APPEND)) {
793228940Sdelphij		error = EPERM;
794228940Sdelphij		goto out;
795228940Sdelphij	}
796228940Sdelphij
797228940Sdelphij	/* Remove the entry from the directory; as it is a file, we do not
798228940Sdelphij	 * have to change the number of hard links of the directory. */
799228940Sdelphij	tmpfs_dir_detach(dvp, de);
800228940Sdelphij
801228940Sdelphij	/* Free the directory entry we just deleted.  Note that the node
802228940Sdelphij	 * referred by it will not be removed until the vnode is really
803228940Sdelphij	 * reclaimed. */
804228940Sdelphij	tmpfs_free_dirent(tmp, de, TRUE);
805228940Sdelphij
806228940Sdelphij	if (node->tn_links > 0)
807228940Sdelphij		node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
808228940Sdelphij	    TMPFS_NODE_MODIFIED;
809228940Sdelphij	error = 0;
810228940Sdelphij
811228940Sdelphijout:
812228940Sdelphij
813228940Sdelphij	return error;
814228940Sdelphij}
815228940Sdelphij
816228940Sdelphij/* --------------------------------------------------------------------- */
817228940Sdelphij
818228940Sdelphijstatic int
819228940Sdelphijtmpfs_link(struct vop_link_args *v)
820228940Sdelphij{
821228940Sdelphij	struct vnode *dvp = v->a_tdvp;
822228940Sdelphij	struct vnode *vp = v->a_vp;
823228940Sdelphij	struct componentname *cnp = v->a_cnp;
824228940Sdelphij
825228940Sdelphij	int error;
826228940Sdelphij	struct tmpfs_dirent *de;
827228940Sdelphij	struct tmpfs_node *node;
828228940Sdelphij
829228940Sdelphij	MPASS(VOP_ISLOCKED(dvp, cnp->cn_thread));
830228940Sdelphij	MPASS(cnp->cn_flags & HASBUF);
831228940Sdelphij	MPASS(dvp != vp); /* XXX When can this be false? */
832228940Sdelphij
833228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
834228940Sdelphij
835228940Sdelphij	/* XXX: Why aren't the following two tests done by the caller? */
836228940Sdelphij
837228940Sdelphij	/* Hard links of directories are forbidden. */
838228940Sdelphij	if (vp->v_type == VDIR) {
839228940Sdelphij		error = EPERM;
840228940Sdelphij		goto out;
841228940Sdelphij	}
842228940Sdelphij
843228940Sdelphij	/* Cannot create cross-device links. */
844228940Sdelphij	if (dvp->v_mount != vp->v_mount) {
845228940Sdelphij		error = EXDEV;
846228940Sdelphij		goto out;
847228940Sdelphij	}
848228940Sdelphij
849228940Sdelphij	/* Ensure that we do not overflow the maximum number of links imposed
850228940Sdelphij	 * by the system. */
851228940Sdelphij	MPASS(node->tn_links <= LINK_MAX);
852228940Sdelphij	if (node->tn_links == LINK_MAX) {
853228940Sdelphij		error = EMLINK;
854228940Sdelphij		goto out;
855228940Sdelphij	}
856228940Sdelphij
857228940Sdelphij	/* We cannot create links of files marked immutable or append-only. */
858228940Sdelphij	if (node->tn_flags & (IMMUTABLE | APPEND)) {
859228940Sdelphij		error = EPERM;
860228940Sdelphij		goto out;
861228940Sdelphij	}
862228940Sdelphij
863228940Sdelphij	/* Allocate a new directory entry to represent the node. */
864228940Sdelphij	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
865228940Sdelphij	    cnp->cn_nameptr, cnp->cn_namelen, &de);
866228940Sdelphij	if (error != 0)
867228940Sdelphij		goto out;
868228940Sdelphij
869228940Sdelphij	/* Insert the new directory entry into the appropriate directory. */
870228940Sdelphij	tmpfs_dir_attach(dvp, de);
871228940Sdelphij
872228940Sdelphij	/* vp link count has changed, so update node times. */
873228940Sdelphij	node->tn_status |= TMPFS_NODE_CHANGED;
874228940Sdelphij	tmpfs_update(vp);
875228940Sdelphij
876228940Sdelphij	error = 0;
877228940Sdelphij
878228940Sdelphijout:
879228940Sdelphij	return error;
880228940Sdelphij}
881228940Sdelphij
882228940Sdelphij/* --------------------------------------------------------------------- */
883228940Sdelphij
884228940Sdelphijstatic int
885228940Sdelphijtmpfs_rename(struct vop_rename_args *v)
886228940Sdelphij{
887228940Sdelphij	struct vnode *fdvp = v->a_fdvp;
888228940Sdelphij	struct vnode *fvp = v->a_fvp;
889228940Sdelphij	struct componentname *fcnp = v->a_fcnp;
890228940Sdelphij	struct vnode *tdvp = v->a_tdvp;
891228940Sdelphij	struct vnode *tvp = v->a_tvp;
892228940Sdelphij	struct componentname *tcnp = v->a_tcnp;
893228940Sdelphij
894228940Sdelphij	char *newname;
895228940Sdelphij	int error;
896228940Sdelphij	struct tmpfs_dirent *de;
897228940Sdelphij	struct tmpfs_node *fdnode;
898228940Sdelphij	struct tmpfs_node *fnode;
899228940Sdelphij	struct tmpfs_node *tnode;
900228940Sdelphij	struct tmpfs_node *tdnode;
901228940Sdelphij
902228940Sdelphij	MPASS(VOP_ISLOCKED(tdvp, tcnp->cn_thread));
903228940Sdelphij	MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp, tcnp->cn_thread)));
904228940Sdelphij	MPASS(fcnp->cn_flags & HASBUF);
905228940Sdelphij	MPASS(tcnp->cn_flags & HASBUF);
906228940Sdelphij
907228940Sdelphij  	tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
908228940Sdelphij
909228940Sdelphij	/* Disallow cross-device renames.
910228940Sdelphij	 * XXX Why isn't this done by the caller? */
911228940Sdelphij	if (fvp->v_mount != tdvp->v_mount ||
912228940Sdelphij	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
913228940Sdelphij		error = EXDEV;
914228940Sdelphij		goto out;
915228940Sdelphij	}
916228940Sdelphij
917228940Sdelphij	tdnode = VP_TO_TMPFS_DIR(tdvp);
918228940Sdelphij
919228940Sdelphij	/* If source and target are the same file, there is nothing to do. */
920228940Sdelphij	if (fvp == tvp) {
921228940Sdelphij		error = 0;
922228940Sdelphij		goto out;
923228940Sdelphij	}
924228940Sdelphij
925228940Sdelphij	/* If we need to move the directory between entries, lock the
926228940Sdelphij	 * source so that we can safely operate on it. */
927228940Sdelphij	if (tdvp != fdvp) {
928228940Sdelphij		error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
929228940Sdelphij		if (error != 0)
930228940Sdelphij			goto out;
931228940Sdelphij	}
932228940Sdelphij	fdnode = VP_TO_TMPFS_DIR(fdvp);
933228940Sdelphij	fnode = VP_TO_TMPFS_NODE(fvp);
934228940Sdelphij	de = tmpfs_dir_search(fdnode, fnode);
935228940Sdelphij
936228940Sdelphij	/* Avoid manipulating '.' and '..' entries. */
937228940Sdelphij	if (de == NULL) {
938228940Sdelphij		MPASS(fvp->v_type == VDIR);
939228940Sdelphij		error = EINVAL;
940228940Sdelphij		goto out_locked;
941228940Sdelphij	}
942228940Sdelphij	MPASS(de->td_node == fnode);
943228940Sdelphij
944228940Sdelphij	/* If re-naming a directory to another preexisting directory
945228940Sdelphij	 * ensure that the target directory is empty so that its
946228940Sdelphij	 * removal causes no side effects.
947228940Sdelphij	 * Kern_rename gurantees the destination to be a directory
948228940Sdelphij	 * if the source is one. */
949228940Sdelphij	if (tvp != NULL) {
950228940Sdelphij		MPASS(tnode != NULL);
951228940Sdelphij
952228940Sdelphij		if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
953228940Sdelphij		    (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
954228940Sdelphij			error = EPERM;
955228940Sdelphij			goto out_locked;
956228940Sdelphij		}
957228940Sdelphij
958228940Sdelphij		if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
959228940Sdelphij			if (tnode->tn_size > 0) {
960228940Sdelphij				error = ENOTEMPTY;
961228940Sdelphij				goto out_locked;
962228940Sdelphij			}
963228940Sdelphij		} else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
964228940Sdelphij			error = ENOTDIR;
965228940Sdelphij			goto out_locked;
966228940Sdelphij		} else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
967228940Sdelphij			error = EISDIR;
968228940Sdelphij			goto out_locked;
969228940Sdelphij		} else {
970228940Sdelphij			MPASS(fnode->tn_type != VDIR &&
971228940Sdelphij				tnode->tn_type != VDIR);
972228940Sdelphij		}
973228940Sdelphij	}
974228940Sdelphij
975252852Sdelphij	if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
976228940Sdelphij	    || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
977228940Sdelphij		error = EPERM;
978228940Sdelphij		goto out_locked;
979228940Sdelphij	}
980228940Sdelphij
981228940Sdelphij	/* Ensure that we have enough memory to hold the new name, if it
982228940Sdelphij	 * has to be changed. */
983228940Sdelphij	if (fcnp->cn_namelen != tcnp->cn_namelen ||
984228940Sdelphij	    memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
985228940Sdelphij		newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
986228940Sdelphij	} else
987228940Sdelphij		newname = NULL;
988228940Sdelphij
989228940Sdelphij	/* If the node is being moved to another directory, we have to do
990228940Sdelphij	 * the move. */
991228940Sdelphij	if (fdnode != tdnode) {
992228940Sdelphij		/* In case we are moving a directory, we have to adjust its
993228940Sdelphij		 * parent to point to the new parent. */
994245768Sdelphij		if (de->td_node->tn_type == VDIR) {
995228940Sdelphij			struct tmpfs_node *n;
996228940Sdelphij
997228940Sdelphij			/* Ensure the target directory is not a child of the
998228940Sdelphij			 * directory being moved.  Otherwise, we'd end up
999228940Sdelphij			 * with stale nodes. */
1000228940Sdelphij			n = tdnode;
1001228940Sdelphij			while (n != n->tn_dir.tn_parent) {
1002228940Sdelphij				if (n == fnode) {
1003228940Sdelphij					error = EINVAL;
1004228940Sdelphij					if (newname != NULL)
1005228940Sdelphij						    free(newname, M_TMPFSNAME);
1006228940Sdelphij					goto out_locked;
1007228940Sdelphij				}
1008228940Sdelphij				n = n->tn_dir.tn_parent;
1009228940Sdelphij			}
1010228940Sdelphij
1011228940Sdelphij			/* Adjust the parent pointer. */
1012228940Sdelphij			TMPFS_VALIDATE_DIR(fnode);
1013228940Sdelphij			de->td_node->tn_dir.tn_parent = tdnode;
1014228940Sdelphij
1015228940Sdelphij			/* As a result of changing the target of the '..'
1016228940Sdelphij			 * entry, the link count of the source and target
1017228940Sdelphij			 * directories has to be adjusted. */
1018228940Sdelphij			fdnode->tn_links--;
1019228940Sdelphij			tdnode->tn_links++;
1020228940Sdelphij		}
1021228940Sdelphij
1022228940Sdelphij		/* Do the move: just remove the entry from the source directory
1023228940Sdelphij		 * and insert it into the target one. */
1024228940Sdelphij		tmpfs_dir_detach(fdvp, de);
1025228940Sdelphij		tmpfs_dir_attach(tdvp, de);
1026228940Sdelphij	}
1027228940Sdelphij
1028228940Sdelphij	/* If the name has changed, we need to make it effective by changing
1029228940Sdelphij	 * it in the directory entry. */
1030228940Sdelphij	if (newname != NULL) {
1031228940Sdelphij		MPASS(tcnp->cn_namelen <= MAXNAMLEN);
1032228940Sdelphij
1033228940Sdelphij		free(de->td_name, M_TMPFSNAME);
1034228940Sdelphij		de->td_namelen = (uint16_t)tcnp->cn_namelen;
1035228940Sdelphij		memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
1036228940Sdelphij		de->td_name = newname;
1037228940Sdelphij
1038228940Sdelphij		fnode->tn_status |= TMPFS_NODE_CHANGED;
1039228940Sdelphij		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
1040228940Sdelphij	}
1041228940Sdelphij
1042228940Sdelphij	/* If we are overwriting an entry, we have to remove the old one
1043228940Sdelphij	 * from the target directory. */
1044228940Sdelphij	if (tvp != NULL) {
1045228940Sdelphij		/* Remove the old entry from the target directory. */
1046228940Sdelphij		de = tmpfs_dir_search(tdnode, tnode);
1047228940Sdelphij		tmpfs_dir_detach(tdvp, de);
1048228940Sdelphij
1049228940Sdelphij		/* Free the directory entry we just deleted.  Note that the
1050255871Sscottl		 * node referred by it will not be removed until the vnode is
1051228940Sdelphij		 * really reclaimed. */
1052228940Sdelphij		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE);
1053228940Sdelphij	}
1054228940Sdelphij
1055228940Sdelphij	error = 0;
1056228940Sdelphij
1057228940Sdelphijout_locked:
1058228940Sdelphij	if (fdnode != tdnode)
1059228940Sdelphij		VOP_UNLOCK(fdvp, 0);
1060252852Sdelphij
1061228940Sdelphijout:
1062228940Sdelphij	/* Release target nodes. */
1063252852Sdelphij	/* XXX: I don't understand when tdvp can be the same as tvp, but
1064228940Sdelphij	 * other code takes care of this... */
1065252852Sdelphij	if (tdvp == tvp)
1066228940Sdelphij		vrele(tdvp);
1067228940Sdelphij	else
1068228940Sdelphij		vput(tdvp);
1069228940Sdelphij	if (tvp != NULL)
1070228940Sdelphij		vput(tvp);
1071228940Sdelphij
1072228940Sdelphij	/* Release source nodes. */
1073228940Sdelphij	vrele(fdvp);
1074228940Sdelphij	vrele(fvp);
1075228940Sdelphij
1076228940Sdelphij	return error;
1077228940Sdelphij}
1078228940Sdelphij
1079228940Sdelphij/* --------------------------------------------------------------------- */
1080228940Sdelphij
1081228940Sdelphijstatic int
1082228940Sdelphijtmpfs_mkdir(struct vop_mkdir_args *v)
1083228940Sdelphij{
1084228940Sdelphij	struct vnode *dvp = v->a_dvp;
1085228940Sdelphij	struct vnode **vpp = v->a_vpp;
1086228940Sdelphij	struct componentname *cnp = v->a_cnp;
1087228940Sdelphij	struct vattr *vap = v->a_vap;
1088228940Sdelphij
1089228940Sdelphij	MPASS(vap->va_type == VDIR);
1090228940Sdelphij
1091228940Sdelphij	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
1092228940Sdelphij}
1093228940Sdelphij
1094228940Sdelphij/* --------------------------------------------------------------------- */
1095228940Sdelphij
1096228940Sdelphijstatic int
1097228940Sdelphijtmpfs_rmdir(struct vop_rmdir_args *v)
1098228940Sdelphij{
1099228940Sdelphij	struct vnode *dvp = v->a_dvp;
1100228940Sdelphij	struct vnode *vp = v->a_vp;
1101228940Sdelphij
1102228940Sdelphij	int error;
1103228940Sdelphij	struct tmpfs_dirent *de;
1104228940Sdelphij	struct tmpfs_mount *tmp;
1105228940Sdelphij	struct tmpfs_node *dnode;
1106228940Sdelphij	struct tmpfs_node *node;
1107228940Sdelphij
1108228940Sdelphij	MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread));
1109228940Sdelphij	MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread));
1110228940Sdelphij
1111228940Sdelphij	tmp = VFS_TO_TMPFS(dvp->v_mount);
1112228940Sdelphij	dnode = VP_TO_TMPFS_DIR(dvp);
1113228940Sdelphij	node = VP_TO_TMPFS_DIR(vp);
1114228940Sdelphij
1115228940Sdelphij	/* Directories with more than two entries ('.' and '..') cannot be
1116228940Sdelphij	 * removed. */
1117228940Sdelphij	 if (node->tn_size > 0) {
1118228940Sdelphij		 error = ENOTEMPTY;
1119228940Sdelphij		 goto out;
1120228940Sdelphij	 }
1121228940Sdelphij
1122228940Sdelphij	if ((dnode->tn_flags & APPEND)
1123228940Sdelphij	    || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
1124228940Sdelphij		error = EPERM;
1125228940Sdelphij		goto out;
1126228940Sdelphij	}
1127228940Sdelphij
1128228940Sdelphij	/* This invariant holds only if we are not trying to remove "..".
1129228940Sdelphij	  * We checked for that above so this is safe now. */
1130228940Sdelphij	MPASS(node->tn_dir.tn_parent == dnode);
1131228940Sdelphij
1132228940Sdelphij	/* Get the directory entry associated with node (vp).  This was
1133228940Sdelphij	 * filled by tmpfs_lookup while looking up the entry. */
1134228940Sdelphij	de = tmpfs_dir_search(dnode, node);
1135228940Sdelphij	MPASS(TMPFS_DIRENT_MATCHES(de,
1136228940Sdelphij	    v->a_cnp->cn_nameptr,
1137245768Sdelphij	    v->a_cnp->cn_namelen));
1138228940Sdelphij
1139228940Sdelphij	/* Check flags to see if we are allowed to remove the directory. */
1140228940Sdelphij	if (dnode->tn_flags & APPEND
1141228940Sdelphij		|| node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
1142228940Sdelphij		error = EPERM;
1143228940Sdelphij		goto out;
1144228940Sdelphij	}
1145228940Sdelphij
1146228940Sdelphij	/* Detach the directory entry from the directory (dnode). */
1147228940Sdelphij	tmpfs_dir_detach(dvp, de);
1148228940Sdelphij
1149228940Sdelphij	node->tn_links--;
1150228940Sdelphij	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
1151228940Sdelphij	    TMPFS_NODE_MODIFIED;
1152228940Sdelphij	node->tn_dir.tn_parent->tn_links--;
1153228940Sdelphij	node->tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \
1154228940Sdelphij	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1155228940Sdelphij
1156228940Sdelphij	cache_purge(dvp);
1157228940Sdelphij	cache_purge(vp);
1158228940Sdelphij
1159228940Sdelphij	/* Free the directory entry we just deleted.  Note that the node
1160228940Sdelphij	 * referred by it will not be removed until the vnode is really
1161228940Sdelphij	 * reclaimed. */
1162228940Sdelphij	tmpfs_free_dirent(tmp, de, TRUE);
1163228940Sdelphij
1164228940Sdelphij	/* Release the deleted vnode (will destroy the node, notify
1165228940Sdelphij	 * interested parties and clean it from the cache). */
1166228940Sdelphij
1167228940Sdelphij	dnode->tn_status |= TMPFS_NODE_CHANGED;
1168228940Sdelphij	tmpfs_update(dvp);
1169228940Sdelphij
1170228940Sdelphij	error = 0;
1171228940Sdelphij
1172228940Sdelphijout:
1173228940Sdelphij	return error;
1174228940Sdelphij}
1175228940Sdelphij
1176228940Sdelphij/* --------------------------------------------------------------------- */
1177228940Sdelphij
1178228940Sdelphijstatic int
1179228940Sdelphijtmpfs_symlink(struct vop_symlink_args *v)
1180228940Sdelphij{
1181228940Sdelphij	struct vnode *dvp = v->a_dvp;
1182228940Sdelphij	struct vnode **vpp = v->a_vpp;
1183228940Sdelphij	struct componentname *cnp = v->a_cnp;
1184245768Sdelphij	struct vattr *vap = v->a_vap;
1185228940Sdelphij	char *target = v->a_target;
1186228940Sdelphij
1187228940Sdelphij#ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */
1188228940Sdelphij	MPASS(vap->va_type == VLNK);
1189228940Sdelphij#else
1190228940Sdelphij	vap->va_type = VLNK;
1191228940Sdelphij#endif
1192228940Sdelphij
1193228940Sdelphij	return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1194228940Sdelphij}
1195228940Sdelphij
1196228940Sdelphij/* --------------------------------------------------------------------- */
1197228940Sdelphij
1198228940Sdelphijstatic int
1199228940Sdelphijtmpfs_readdir(struct vop_readdir_args *v)
1200228940Sdelphij{
1201228940Sdelphij	struct vnode *vp = v->a_vp;
1202228940Sdelphij	struct uio *uio = v->a_uio;
1203228940Sdelphij	int *eofflag = v->a_eofflag;
1204228940Sdelphij	u_long **cookies = v->a_cookies;
1205228940Sdelphij	int *ncookies = v->a_ncookies;
1206228940Sdelphij
1207228940Sdelphij	int error;
1208228940Sdelphij	off_t startoff;
1209228940Sdelphij	off_t cnt = 0;
1210228940Sdelphij	struct tmpfs_node *node;
1211228940Sdelphij
1212228940Sdelphij	/* This operation only makes sense on directory nodes. */
1213228940Sdelphij	if (vp->v_type != VDIR)
1214228940Sdelphij		return ENOTDIR;
1215228940Sdelphij
1216228940Sdelphij	node = VP_TO_TMPFS_DIR(vp);
1217228940Sdelphij
1218228940Sdelphij	startoff = uio->uio_offset;
1219228940Sdelphij
1220228940Sdelphij	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
1221228940Sdelphij		error = tmpfs_dir_getdotdent(node, uio);
1222228940Sdelphij		if (error != 0)
1223228940Sdelphij			goto outok;
1224228940Sdelphij		cnt++;
1225228940Sdelphij	}
1226228940Sdelphij
1227228940Sdelphij	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1228228940Sdelphij		error = tmpfs_dir_getdotdotdent(node, uio);
1229228940Sdelphij		if (error != 0)
1230228940Sdelphij			goto outok;
1231228940Sdelphij		cnt++;
1232228940Sdelphij	}
1233228940Sdelphij
1234228940Sdelphij	error = tmpfs_dir_getdents(node, uio, &cnt);
1235228940Sdelphij
1236228940Sdelphijoutok:
1237228940Sdelphij	MPASS(error >= -1);
1238228940Sdelphij
1239228940Sdelphij	if (error == -1)
1240228940Sdelphij		error = 0;
1241228940Sdelphij
1242228940Sdelphij	if (eofflag != NULL)
1243228940Sdelphij		*eofflag =
1244228940Sdelphij		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1245228940Sdelphij
1246228940Sdelphij	/* Update NFS-related variables. */
1247228940Sdelphij	if (error == 0 && cookies != NULL && ncookies != NULL) {
1248228940Sdelphij		off_t i;
1249228940Sdelphij		off_t off = startoff;
1250228940Sdelphij		struct tmpfs_dirent *de = NULL;
1251228940Sdelphij
1252228940Sdelphij		*ncookies = cnt;
1253228940Sdelphij		*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
1254228940Sdelphij
1255228940Sdelphij		for (i = 0; i < cnt; i++) {
1256228940Sdelphij			MPASS(off != TMPFS_DIRCOOKIE_EOF);
1257228940Sdelphij			if (off == TMPFS_DIRCOOKIE_DOT) {
1258228940Sdelphij				off = TMPFS_DIRCOOKIE_DOTDOT;
1259228940Sdelphij			} else {
1260228940Sdelphij				if (off == TMPFS_DIRCOOKIE_DOTDOT) {
1261228940Sdelphij					de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
1262228940Sdelphij				} else if (de != NULL) {
1263228940Sdelphij					de = TAILQ_NEXT(de, td_entries);
1264228940Sdelphij				} else {
1265228940Sdelphij					de = tmpfs_dir_lookupbycookie(node,
1266228940Sdelphij					    off);
1267228940Sdelphij					MPASS(de != NULL);
1268228940Sdelphij					de = TAILQ_NEXT(de, td_entries);
1269228940Sdelphij				}
1270228940Sdelphij				if (de == NULL)
1271228940Sdelphij					off = TMPFS_DIRCOOKIE_EOF;
1272228940Sdelphij				else
1273228940Sdelphij					off = tmpfs_dircookie(de);
1274228940Sdelphij			}
1275228940Sdelphij
1276228940Sdelphij			(*cookies)[i] = off;
1277228940Sdelphij		}
1278228940Sdelphij		MPASS(uio->uio_offset == off);
1279228940Sdelphij	}
1280228940Sdelphij
1281228940Sdelphij	return error;
1282228940Sdelphij}
1283228940Sdelphij
1284228940Sdelphij/* --------------------------------------------------------------------- */
1285228940Sdelphij
1286228940Sdelphijstatic int
1287228940Sdelphijtmpfs_readlink(struct vop_readlink_args *v)
1288228940Sdelphij{
1289228940Sdelphij	struct vnode *vp = v->a_vp;
1290228940Sdelphij	struct uio *uio = v->a_uio;
1291228940Sdelphij
1292228940Sdelphij	int error;
1293228940Sdelphij	struct tmpfs_node *node;
1294228940Sdelphij
1295228940Sdelphij	MPASS(uio->uio_offset == 0);
1296228940Sdelphij	MPASS(vp->v_type == VLNK);
1297228940Sdelphij
1298228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
1299228940Sdelphij
1300228940Sdelphij	error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
1301228940Sdelphij	    uio);
1302228940Sdelphij	node->tn_status |= TMPFS_NODE_ACCESSED;
1303228940Sdelphij
1304228940Sdelphij	return error;
1305228940Sdelphij}
1306228940Sdelphij
1307228940Sdelphij/* --------------------------------------------------------------------- */
1308228940Sdelphij
1309228940Sdelphijstatic int
1310228940Sdelphijtmpfs_inactive(struct vop_inactive_args *v)
1311228940Sdelphij{
1312228940Sdelphij	struct vnode *vp = v->a_vp;
1313228940Sdelphij	struct thread *l = v->a_td;
1314228940Sdelphij
1315228940Sdelphij	struct tmpfs_node *node;
1316228940Sdelphij
1317228940Sdelphij	MPASS(VOP_ISLOCKED(vp, l));
1318228940Sdelphij
1319228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
1320228940Sdelphij
1321228940Sdelphij	if (node->tn_links == 0)
1322228940Sdelphij		vrecycle(vp, l);
1323228940Sdelphij
1324228940Sdelphij	return 0;
1325228940Sdelphij}
1326228940Sdelphij
1327228940Sdelphij/* --------------------------------------------------------------------- */
1328228940Sdelphij
1329228940Sdelphijint
1330228940Sdelphijtmpfs_reclaim(struct vop_reclaim_args *v)
1331228940Sdelphij{
1332228940Sdelphij	struct vnode *vp = v->a_vp;
1333228940Sdelphij
1334228940Sdelphij	struct tmpfs_mount *tmp;
1335228940Sdelphij	struct tmpfs_node *node;
1336228940Sdelphij
1337228940Sdelphij	node = VP_TO_TMPFS_NODE(vp);
1338228940Sdelphij	tmp = VFS_TO_TMPFS(vp->v_mount);
1339228940Sdelphij
1340228940Sdelphij	vnode_destroy_vobject(vp);
1341228940Sdelphij	cache_purge(vp);
1342228940Sdelphij	tmpfs_free_vp(vp);
1343228940Sdelphij
1344228940Sdelphij	/* If the node referenced by this vnode was deleted by the user,
1345228940Sdelphij	 * we must free its associated data structures (now that the vnode
1346252852Sdelphij	 * is being reclaimed). */
1347252852Sdelphij	if (node->tn_links == 0)
1348252852Sdelphij		tmpfs_free_node(tmp, node);
1349252852Sdelphij
1350252852Sdelphij	MPASS(vp->v_data == NULL);
1351228940Sdelphij	return 0;
1352252852Sdelphij}
1353252852Sdelphij
1354228940Sdelphij/* --------------------------------------------------------------------- */
1355252852Sdelphij
1356252852Sdelphijstatic int
1357228940Sdelphijtmpfs_print(struct vop_print_args *v)
1358228940Sdelphij{
1359228940Sdelphij	struct vnode *vp = v->a_vp;
1360228940Sdelphij
1361228940Sdelphij	struct tmpfs_node *node;
1362
1363	node = VP_TO_TMPFS_NODE(vp);
1364
1365	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
1366	    node, node->tn_flags, node->tn_links);
1367	printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX
1368	    ", status 0x%x\n",
1369	    node->tn_mode, node->tn_uid, node->tn_gid,
1370	    (uintmax_t)node->tn_size, node->tn_status);
1371
1372	if (vp->v_type == VFIFO)
1373		fifo_printinfo(vp);
1374
1375	printf("\n");
1376
1377	return 0;
1378}
1379
1380/* --------------------------------------------------------------------- */
1381
1382static int
1383tmpfs_pathconf(struct vop_pathconf_args *v)
1384{
1385	int name = v->a_name;
1386	register_t *retval = v->a_retval;
1387
1388	int error;
1389
1390	error = 0;
1391
1392	switch (name) {
1393	case _PC_LINK_MAX:
1394		*retval = LINK_MAX;
1395		break;
1396
1397	case _PC_NAME_MAX:
1398		*retval = NAME_MAX;
1399		break;
1400
1401	case _PC_PATH_MAX:
1402		*retval = PATH_MAX;
1403		break;
1404
1405	case _PC_PIPE_BUF:
1406		*retval = PIPE_BUF;
1407		break;
1408
1409	case _PC_CHOWN_RESTRICTED:
1410		*retval = 1;
1411		break;
1412
1413	case _PC_NO_TRUNC:
1414		*retval = 1;
1415		break;
1416
1417	case _PC_SYNC_IO:
1418		*retval = 1;
1419		break;
1420
1421	case _PC_FILESIZEBITS:
1422		*retval = 0; /* XXX Don't know which value should I return. */
1423		break;
1424
1425	default:
1426		error = EINVAL;
1427	}
1428
1429	return error;
1430}
1431
1432/* --------------------------------------------------------------------- */
1433
1434static int
1435tmpfs_advlock(struct vop_advlock_args *v)
1436{
1437	struct vnode *vp = v->a_vp;
1438
1439	struct tmpfs_node *node;
1440
1441	node = VP_TO_TMPFS_NODE(vp);
1442
1443	return lf_advlock(v, &node->tn_lockf, node->tn_size);
1444}
1445
1446/* --------------------------------------------------------------------- */
1447
1448static int
1449tmpfs_vptofh(struct vop_vptofh_args *ap)
1450{
1451	struct tmpfs_fid *tfhp;
1452	struct tmpfs_node *node;
1453
1454	tfhp = (struct tmpfs_fid *)ap->a_fhp;
1455	node = VP_TO_TMPFS_NODE(ap->a_vp);
1456
1457	tfhp->tf_len = sizeof(struct tmpfs_fid);
1458	tfhp->tf_id = node->tn_id;
1459	tfhp->tf_gen = node->tn_gen;
1460
1461	return (0);
1462}
1463
1464/* --------------------------------------------------------------------- */
1465
1466/*
1467 * vnode operations vector used for files stored in a tmpfs file system.
1468 */
1469struct vop_vector tmpfs_vnodeop_entries = {
1470	.vop_default =			&default_vnodeops,
1471	.vop_lookup =			vfs_cache_lookup,
1472	.vop_cachedlookup =		tmpfs_lookup,
1473	.vop_create =			tmpfs_create,
1474	.vop_mknod =			tmpfs_mknod,
1475	.vop_open =			tmpfs_open,
1476	.vop_close =			tmpfs_close,
1477	.vop_access =			tmpfs_access,
1478	.vop_getattr =			tmpfs_getattr,
1479	.vop_setattr =			tmpfs_setattr,
1480	.vop_read =			tmpfs_read,
1481	.vop_write =			tmpfs_write,
1482	.vop_fsync =			tmpfs_fsync,
1483	.vop_remove =			tmpfs_remove,
1484	.vop_link =			tmpfs_link,
1485	.vop_rename =			tmpfs_rename,
1486	.vop_mkdir =			tmpfs_mkdir,
1487	.vop_rmdir =			tmpfs_rmdir,
1488	.vop_symlink =			tmpfs_symlink,
1489	.vop_readdir =			tmpfs_readdir,
1490	.vop_readlink =			tmpfs_readlink,
1491	.vop_inactive =			tmpfs_inactive,
1492	.vop_reclaim =			tmpfs_reclaim,
1493	.vop_print =			tmpfs_print,
1494	.vop_pathconf =			tmpfs_pathconf,
1495	.vop_advlock =			tmpfs_advlock,
1496	.vop_vptofh =			tmpfs_vptofh,
1497	.vop_bmap =			VOP_EOPNOTSUPP,
1498};
1499
1500