xref: /netbsd-src/sys/fs/tmpfs/tmpfs_vnops.c (revision ccb670c8b8f3dbcc0cf3aef58b2fc96b7183818f)
1*ccb670c8Sjmmv /*	$NetBSD: tmpfs_vnops.c,v 1.32 2006/11/09 15:36:30 jmmv Exp $	*/
2ec933656Sjmmv 
3ec933656Sjmmv /*
4064fbe7eSjmmv  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
5ec933656Sjmmv  * All rights reserved.
6ec933656Sjmmv  *
7ec933656Sjmmv  * This code is derived from software contributed to The NetBSD Foundation
8b0085cabSjmmv  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9b0085cabSjmmv  * 2005 program.
10ec933656Sjmmv  *
11ec933656Sjmmv  * Redistribution and use in source and binary forms, with or without
12ec933656Sjmmv  * modification, are permitted provided that the following conditions
13ec933656Sjmmv  * are met:
14ec933656Sjmmv  * 1. Redistributions of source code must retain the above copyright
15ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer.
16ec933656Sjmmv  * 2. Redistributions in binary form must reproduce the above copyright
17ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer in the
18ec933656Sjmmv  *    documentation and/or other materials provided with the distribution.
19ec933656Sjmmv  * 3. All advertising materials mentioning features or use of this software
20ec933656Sjmmv  *    must display the following acknowledgement:
21ec933656Sjmmv  *        This product includes software developed by the NetBSD
22ec933656Sjmmv  *        Foundation, Inc. and its contributors.
23ec933656Sjmmv  * 4. Neither the name of The NetBSD Foundation nor the names of its
24ec933656Sjmmv  *    contributors may be used to endorse or promote products derived
25ec933656Sjmmv  *    from this software without specific prior written permission.
26ec933656Sjmmv  *
27ec933656Sjmmv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28ec933656Sjmmv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29ec933656Sjmmv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30ec933656Sjmmv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31ec933656Sjmmv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32ec933656Sjmmv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33ec933656Sjmmv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34ec933656Sjmmv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35ec933656Sjmmv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36ec933656Sjmmv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37ec933656Sjmmv  * POSSIBILITY OF SUCH DAMAGE.
38ec933656Sjmmv  */
39ec933656Sjmmv 
40ec933656Sjmmv /*
41ec933656Sjmmv  * tmpfs vnode interface.
42ec933656Sjmmv  */
43ec933656Sjmmv 
44ec933656Sjmmv #include <sys/cdefs.h>
45*ccb670c8Sjmmv __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.32 2006/11/09 15:36:30 jmmv Exp $");
46ec933656Sjmmv 
47ec933656Sjmmv #include <sys/param.h>
48ec933656Sjmmv #include <sys/dirent.h>
49ec933656Sjmmv #include <sys/fcntl.h>
50ec933656Sjmmv #include <sys/event.h>
51ec933656Sjmmv #include <sys/malloc.h>
52ec933656Sjmmv #include <sys/namei.h>
53ec933656Sjmmv #include <sys/proc.h>
54ec933656Sjmmv #include <sys/stat.h>
55ec933656Sjmmv #include <sys/uio.h>
56ec933656Sjmmv #include <sys/unistd.h>
57ec933656Sjmmv #include <sys/vnode.h>
58b6d141c7Sjmmv #include <sys/lockf.h>
59fc6d984bSchristos #include <sys/kauth.h>
60ec933656Sjmmv 
61ec933656Sjmmv #include <uvm/uvm.h>
62ec933656Sjmmv 
63ec933656Sjmmv #include <miscfs/fifofs/fifo.h>
64ec933656Sjmmv #include <fs/tmpfs/tmpfs_vnops.h>
65ec933656Sjmmv #include <fs/tmpfs/tmpfs.h>
66ec933656Sjmmv 
67ec933656Sjmmv /* --------------------------------------------------------------------- */
68ec933656Sjmmv 
69ec933656Sjmmv /*
708e0a777aSjmmv  * vnode operations vector used for files stored in a tmpfs file system.
71ec933656Sjmmv  */
72ec933656Sjmmv int (**tmpfs_vnodeop_p)(void *);
73ec933656Sjmmv const struct vnodeopv_entry_desc tmpfs_vnodeop_entries[] = {
74ec933656Sjmmv 	{ &vop_default_desc,		vn_default_error },
75ec933656Sjmmv 	{ &vop_lookup_desc,		tmpfs_lookup },
76ec933656Sjmmv 	{ &vop_create_desc,		tmpfs_create },
77ec933656Sjmmv 	{ &vop_mknod_desc,		tmpfs_mknod },
78ec933656Sjmmv 	{ &vop_open_desc,		tmpfs_open },
79ec933656Sjmmv 	{ &vop_close_desc,		tmpfs_close },
80ec933656Sjmmv 	{ &vop_access_desc,		tmpfs_access },
81ec933656Sjmmv 	{ &vop_getattr_desc,		tmpfs_getattr },
82ec933656Sjmmv 	{ &vop_setattr_desc,		tmpfs_setattr },
83ec933656Sjmmv 	{ &vop_read_desc,		tmpfs_read },
84ec933656Sjmmv 	{ &vop_write_desc,		tmpfs_write },
85ec933656Sjmmv 	{ &vop_ioctl_desc,		tmpfs_ioctl },
86ec933656Sjmmv 	{ &vop_fcntl_desc,		tmpfs_fcntl },
87ec933656Sjmmv 	{ &vop_poll_desc,		tmpfs_poll },
88ec933656Sjmmv 	{ &vop_kqfilter_desc,		tmpfs_kqfilter },
89ec933656Sjmmv 	{ &vop_revoke_desc,		tmpfs_revoke },
90ec933656Sjmmv 	{ &vop_mmap_desc,		tmpfs_mmap },
91ec933656Sjmmv 	{ &vop_fsync_desc,		tmpfs_fsync },
92ec933656Sjmmv 	{ &vop_seek_desc,		tmpfs_seek },
93ec933656Sjmmv 	{ &vop_remove_desc,		tmpfs_remove },
94ec933656Sjmmv 	{ &vop_link_desc,		tmpfs_link },
95ec933656Sjmmv 	{ &vop_rename_desc,		tmpfs_rename },
96ec933656Sjmmv 	{ &vop_mkdir_desc,		tmpfs_mkdir },
97ec933656Sjmmv 	{ &vop_rmdir_desc,		tmpfs_rmdir },
98ec933656Sjmmv 	{ &vop_symlink_desc,		tmpfs_symlink },
99ec933656Sjmmv 	{ &vop_readdir_desc,		tmpfs_readdir },
100ec933656Sjmmv 	{ &vop_readlink_desc,		tmpfs_readlink },
101ec933656Sjmmv 	{ &vop_abortop_desc,		tmpfs_abortop },
102ec933656Sjmmv 	{ &vop_inactive_desc,		tmpfs_inactive },
103ec933656Sjmmv 	{ &vop_reclaim_desc,		tmpfs_reclaim },
104ec933656Sjmmv 	{ &vop_lock_desc,		tmpfs_lock },
105ec933656Sjmmv 	{ &vop_unlock_desc,		tmpfs_unlock },
106ec933656Sjmmv 	{ &vop_bmap_desc,		tmpfs_bmap },
107ec933656Sjmmv 	{ &vop_strategy_desc,		tmpfs_strategy },
108ec933656Sjmmv 	{ &vop_print_desc,		tmpfs_print },
109ec933656Sjmmv 	{ &vop_pathconf_desc,		tmpfs_pathconf },
110ec933656Sjmmv 	{ &vop_islocked_desc,		tmpfs_islocked },
111ec933656Sjmmv 	{ &vop_advlock_desc,		tmpfs_advlock },
112ec933656Sjmmv 	{ &vop_lease_desc,		tmpfs_lease },
113ec933656Sjmmv 	{ &vop_bwrite_desc,		tmpfs_bwrite },
114ec933656Sjmmv 	{ &vop_getpages_desc,		tmpfs_getpages },
115ec933656Sjmmv 	{ &vop_putpages_desc,		tmpfs_putpages },
116ec933656Sjmmv 	{ NULL, NULL }
117ec933656Sjmmv };
118ec933656Sjmmv const struct vnodeopv_desc tmpfs_vnodeop_opv_desc =
119ec933656Sjmmv 	{ &tmpfs_vnodeop_p, tmpfs_vnodeop_entries };
120ec933656Sjmmv 
121ec933656Sjmmv /* --------------------------------------------------------------------- */
122ec933656Sjmmv 
123ec933656Sjmmv int
124ec933656Sjmmv tmpfs_lookup(void *v)
125ec933656Sjmmv {
126ec933656Sjmmv 	struct vnode *dvp = ((struct vop_lookup_args *)v)->a_dvp;
127ec933656Sjmmv 	struct vnode **vpp = ((struct vop_lookup_args *)v)->a_vpp;
128ec933656Sjmmv 	struct componentname *cnp = ((struct vop_lookup_args *)v)->a_cnp;
129ec933656Sjmmv 
130ec933656Sjmmv 	int error;
131ec933656Sjmmv 	struct tmpfs_dirent *de;
132ec933656Sjmmv 	struct tmpfs_node *dnode;
133ec933656Sjmmv 
134ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
135ec933656Sjmmv 
136ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
137ec933656Sjmmv 	*vpp = NULL;
138ec933656Sjmmv 
139ec933656Sjmmv 	/* Check accessibility of requested node as a first step. */
14095e1ffb1Schristos 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_lwp);
141ec933656Sjmmv 	if (error != 0)
142ec933656Sjmmv 		goto out;
143ec933656Sjmmv 
1448e0a777aSjmmv 	/* If requesting the last path component on a read-only file system
145ec933656Sjmmv 	 * with a write operation, deny it. */
146ec933656Sjmmv 	if ((cnp->cn_flags & ISLASTCN) &&
147ec933656Sjmmv 	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
148ec933656Sjmmv 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
149ec933656Sjmmv 		error = EROFS;
150ec933656Sjmmv 		goto out;
151ec933656Sjmmv 	}
152ec933656Sjmmv 
153ec933656Sjmmv 	/* Avoid doing a linear scan of the directory if the requested
154ec933656Sjmmv 	 * directory/name couple is already in the cache. */
155ec933656Sjmmv 	error = cache_lookup(dvp, vpp, cnp);
156ec933656Sjmmv 	if (error >= 0)
157ec933656Sjmmv 		goto out;
158ec933656Sjmmv 
159ec933656Sjmmv 	/* We cannot be requesting the parent directory of the root node. */
160ec933656Sjmmv 	KASSERT(IMPLIES(dnode->tn_type == VDIR &&
161064fbe7eSjmmv 	    dnode->tn_spec.tn_dir.tn_parent == dnode,
162064fbe7eSjmmv 	    !(cnp->cn_flags & ISDOTDOT)));
163ec933656Sjmmv 
164ec933656Sjmmv 	if (cnp->cn_flags & ISDOTDOT) {
165ec933656Sjmmv 		VOP_UNLOCK(dvp, 0);
166ec933656Sjmmv 
167ec933656Sjmmv 		/* Allocate a new vnode on the matching entry. */
168064fbe7eSjmmv 		error = tmpfs_alloc_vp(dvp->v_mount,
169064fbe7eSjmmv 		    dnode->tn_spec.tn_dir.tn_parent, vpp);
170ec933656Sjmmv 
171ec933656Sjmmv 		if (cnp->cn_flags & LOCKPARENT &&
172ec933656Sjmmv 		    cnp->cn_flags & ISLASTCN) {
173ec933656Sjmmv 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
174ec933656Sjmmv 				cnp->cn_flags |= PDIRUNLOCK;
175ec933656Sjmmv 		}
176064fbe7eSjmmv 		dnode->tn_spec.tn_dir.tn_parent->tn_lookup_dirent = NULL;
177ec933656Sjmmv 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
178ec933656Sjmmv 		VREF(dvp);
179ec933656Sjmmv 		*vpp = dvp;
180ec933656Sjmmv 		dnode->tn_lookup_dirent = NULL;
181ec933656Sjmmv 		error = 0;
182ec933656Sjmmv 	} else {
183ec933656Sjmmv 		de = tmpfs_dir_lookup(dnode, cnp);
184ec933656Sjmmv 		if (de == NULL) {
185ec933656Sjmmv 			/* The entry was not found in the directory.
186ec933656Sjmmv 			 * This is OK iff we are creating or renaming an
187ec933656Sjmmv 			 * entry and are working on the last component of
188ec933656Sjmmv 			 * the path name. */
189ec933656Sjmmv 			if ((cnp->cn_flags & ISLASTCN) &&
190ec933656Sjmmv 			    (cnp->cn_nameiop == CREATE || \
191ec933656Sjmmv 			    cnp->cn_nameiop == RENAME)) {
192ec933656Sjmmv 				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
19395e1ffb1Schristos 				    cnp->cn_lwp);
194ec933656Sjmmv 				if (error != 0)
195ec933656Sjmmv 					goto out;
196ec933656Sjmmv 
197ec933656Sjmmv 				/* Keep the component name in the buffer for
198ec933656Sjmmv 				 * future uses. */
199ec933656Sjmmv 				cnp->cn_flags |= SAVENAME;
200ec933656Sjmmv 
201ec933656Sjmmv 				error = EJUSTRETURN;
202ec933656Sjmmv 			} else
203ec933656Sjmmv 				error = ENOENT;
204ec933656Sjmmv 		} else {
205ec933656Sjmmv 			struct tmpfs_node *tnode;
206ec933656Sjmmv 
207ec933656Sjmmv 			/* The entry was found, so get its associated
208ec933656Sjmmv 			 * tmpfs_node. */
209ec933656Sjmmv 			tnode = de->td_node;
210ec933656Sjmmv 
211ec933656Sjmmv 			/* If we are not at the last path component and
212ab7e099bSjmmv 			 * found a non-directory or non-link entry (which
213ab7e099bSjmmv 			 * may itself be pointing to a directory), raise
214ab7e099bSjmmv 			 * an error. */
215ab7e099bSjmmv 			if ((tnode->tn_type != VDIR &&
216ab7e099bSjmmv 			    tnode->tn_type != VLNK) &&
217ec933656Sjmmv 			    !(cnp->cn_flags & ISLASTCN)) {
218ec933656Sjmmv 				error = ENOTDIR;
219ec933656Sjmmv 				goto out;
220ec933656Sjmmv 			}
221ec933656Sjmmv 
222ec933656Sjmmv 			/* If we are deleting or renaming the entry, keep
223ec933656Sjmmv 			 * track of its tmpfs_dirent so that it can be
224ec933656Sjmmv 			 * easily deleted later. */
225ec933656Sjmmv 			if ((cnp->cn_flags & ISLASTCN) &&
226ec933656Sjmmv 			    (cnp->cn_nameiop == DELETE ||
227ec933656Sjmmv 			    cnp->cn_nameiop == RENAME)) {
22861315901Schristos 				if ((dnode->tn_mode & S_ISTXT) != 0 &&
229fc9422c9Selad 				    kauth_cred_geteuid(cnp->cn_cred) != 0 &&
230fc9422c9Selad 				    kauth_cred_geteuid(cnp->cn_cred) != dnode->tn_uid &&
231fc9422c9Selad 				    kauth_cred_geteuid(cnp->cn_cred) != tnode->tn_uid)
23261315901Schristos 					return EPERM;
233ec933656Sjmmv 				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
23495e1ffb1Schristos 				    cnp->cn_lwp);
235ec933656Sjmmv 				if (error != 0)
236ec933656Sjmmv 					goto out;
237ec933656Sjmmv 				tnode->tn_lookup_dirent = de;
238ec933656Sjmmv 			}
239ec933656Sjmmv 
240ec933656Sjmmv 			/* Allocate a new vnode on the matching entry. */
241ec933656Sjmmv 			error = tmpfs_alloc_vp(dvp->v_mount, tnode, vpp);
242ec933656Sjmmv 
243ec933656Sjmmv 			if (error == 0 && (!(cnp->cn_flags & LOCKPARENT) ||
244ec933656Sjmmv 			    !(cnp->cn_flags & ISLASTCN)))
245ec933656Sjmmv 				VOP_UNLOCK(dvp, 0);
246ec933656Sjmmv 		}
247ec933656Sjmmv 	}
248ec933656Sjmmv 
249ec933656Sjmmv 	/* Store the result of this lookup in the cache.  Avoid this if the
250ec933656Sjmmv 	 * request was for creation, as it does not improve timings on
251ec933656Sjmmv 	 * emprical tests. */
252ec933656Sjmmv 	if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
253ec933656Sjmmv 		cache_enter(dvp, *vpp, cnp);
254ec933656Sjmmv 
255ec933656Sjmmv out:
256ec933656Sjmmv 	/* If there were no errors, *vpp cannot be null and it must be
257ec933656Sjmmv 	 * locked. */
258ec933656Sjmmv 	KASSERT(IFF(error == 0, *vpp != NULL && VOP_ISLOCKED(*vpp)));
259ec933656Sjmmv 
260ec933656Sjmmv 	/* dvp has to be locked if:
261ec933656Sjmmv 	 * - There were errors and relocking of dvp did not fail.
262ec933656Sjmmv 	 * - We are doing a '..' lookup, relocking of dvp did not fail
263ec933656Sjmmv 	 *   (PDIRUNLOCK is unset) and LOCKPARENT or ISLASTCN are not set.
264ec933656Sjmmv 	 * - LOCKPARENT and ISLASTCN are set. */
265ec933656Sjmmv 	KASSERT(IMPLIES(
266ec933656Sjmmv 	    (error != 0 && !(cnp->cn_flags & PDIRUNLOCK)) ||
267ec933656Sjmmv 	    (cnp->cn_flags & ISDOTDOT && !(cnp->cn_flags & PDIRUNLOCK) &&
268ec933656Sjmmv 	     ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))) ||
269ec933656Sjmmv 	    (cnp->cn_flags & LOCKPARENT && cnp->cn_flags & ISLASTCN)
270ec933656Sjmmv 	    ,
271ec933656Sjmmv 	    VOP_ISLOCKED(dvp)));
272ec933656Sjmmv 
273ec933656Sjmmv 	return error;
274ec933656Sjmmv }
275ec933656Sjmmv 
276ec933656Sjmmv /* --------------------------------------------------------------------- */
277ec933656Sjmmv 
278ec933656Sjmmv int
279ec933656Sjmmv tmpfs_create(void *v)
280ec933656Sjmmv {
281ec933656Sjmmv 	struct vnode *dvp = ((struct vop_create_args *)v)->a_dvp;
282ec933656Sjmmv 	struct vnode **vpp = ((struct vop_create_args *)v)->a_vpp;
283ec933656Sjmmv 	struct componentname *cnp = ((struct vop_create_args *)v)->a_cnp;
284ec933656Sjmmv 	struct vattr *vap = ((struct vop_create_args *)v)->a_vap;
285ec933656Sjmmv 
286ec933656Sjmmv 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
287ec933656Sjmmv 
288ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
289ec933656Sjmmv }
290ec933656Sjmmv /* --------------------------------------------------------------------- */
291ec933656Sjmmv 
292ec933656Sjmmv int
293ec933656Sjmmv tmpfs_mknod(void *v)
294ec933656Sjmmv {
295ec933656Sjmmv 	struct vnode *dvp = ((struct vop_mknod_args *)v)->a_dvp;
296ec933656Sjmmv 	struct vnode **vpp = ((struct vop_mknod_args *)v)->a_vpp;
297ec933656Sjmmv 	struct componentname *cnp = ((struct vop_mknod_args *)v)->a_cnp;
298ec933656Sjmmv 	struct vattr *vap = ((struct vop_mknod_args *)v)->a_vap;
299ec933656Sjmmv 
300ec933656Sjmmv 	if (vap->va_type != VBLK && vap->va_type != VCHR &&
301ec933656Sjmmv 	    vap->va_type != VFIFO)
302ec933656Sjmmv 		return EINVAL;
303ec933656Sjmmv 
304ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
305ec933656Sjmmv }
306ec933656Sjmmv 
307ec933656Sjmmv /* --------------------------------------------------------------------- */
308ec933656Sjmmv 
309ec933656Sjmmv int
310ec933656Sjmmv tmpfs_open(void *v)
311ec933656Sjmmv {
312ec933656Sjmmv 	struct vnode *vp = ((struct vop_open_args *)v)->a_vp;
313ec933656Sjmmv 	int mode = ((struct vop_open_args *)v)->a_mode;
314ec933656Sjmmv 
315ec933656Sjmmv 	int error;
316ec933656Sjmmv 	struct tmpfs_node *node;
317ec933656Sjmmv 
318ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
319ec933656Sjmmv 
320ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
321ec933656Sjmmv 
322*ccb670c8Sjmmv 	/* The file is still active but all its names have been removed
323*ccb670c8Sjmmv 	 * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
324*ccb670c8Sjmmv 	 * it is about to die. */
325*ccb670c8Sjmmv 	if (node->tn_links < 1) {
326*ccb670c8Sjmmv 		error = ENOENT;
327*ccb670c8Sjmmv 		goto out;
328*ccb670c8Sjmmv 	}
329*ccb670c8Sjmmv 
330ec933656Sjmmv 	/* If the file is marked append-only, deny write requests. */
331ec933656Sjmmv 	if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
332ec933656Sjmmv 		error = EPERM;
333ec933656Sjmmv 	else
334ec933656Sjmmv 		error = 0;
335ec933656Sjmmv 
336*ccb670c8Sjmmv out:
337ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
338ec933656Sjmmv 
339ec933656Sjmmv 	return error;
340ec933656Sjmmv }
341ec933656Sjmmv 
342ec933656Sjmmv /* --------------------------------------------------------------------- */
343ec933656Sjmmv 
344ec933656Sjmmv int
345ec933656Sjmmv tmpfs_close(void *v)
346ec933656Sjmmv {
347ec933656Sjmmv 	struct vnode *vp = ((struct vop_close_args *)v)->a_vp;
348ec933656Sjmmv 
349ec933656Sjmmv 	struct tmpfs_node *node;
350ec933656Sjmmv 
351ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
352ec933656Sjmmv 
353ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
354ec933656Sjmmv 
355ec933656Sjmmv 	if (node->tn_links > 0) {
356ec933656Sjmmv 		/* Update node times.  No need to do it if the node has
357ec933656Sjmmv 		 * been deleted, because it will vanish after we return. */
358a748ea88Syamt 		tmpfs_update(vp, NULL, NULL, UPDATE_CLOSE);
359a748ea88Syamt 	}
360ec933656Sjmmv 
361a748ea88Syamt 	return 0;
362ec933656Sjmmv }
363ec933656Sjmmv 
364ec933656Sjmmv /* --------------------------------------------------------------------- */
365ec933656Sjmmv 
366ec933656Sjmmv int
367ec933656Sjmmv tmpfs_access(void *v)
368ec933656Sjmmv {
369ec933656Sjmmv 	struct vnode *vp = ((struct vop_access_args *)v)->a_vp;
370ec933656Sjmmv 	int mode = ((struct vop_access_args *)v)->a_mode;
371fc9422c9Selad 	kauth_cred_t cred = ((struct vop_access_args *)v)->a_cred;
372ec933656Sjmmv 
373ec933656Sjmmv 	int error;
374ec933656Sjmmv 	struct tmpfs_node *node;
375ec933656Sjmmv 
376ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
377ec933656Sjmmv 
378ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
379ec933656Sjmmv 
380ec933656Sjmmv 	switch (vp->v_type) {
381ec933656Sjmmv 	case VDIR:
382ec933656Sjmmv 		/* FALLTHROUGH */
383ec933656Sjmmv 	case VLNK:
384ec933656Sjmmv 		/* FALLTHROUGH */
385ec933656Sjmmv 	case VREG:
386ec933656Sjmmv 		if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
387ec933656Sjmmv 			error = EROFS;
388ec933656Sjmmv 			goto out;
389ec933656Sjmmv 		}
390ec933656Sjmmv 		break;
391ec933656Sjmmv 
392ec933656Sjmmv 	case VBLK:
393ec933656Sjmmv 		/* FALLTHROUGH */
394ec933656Sjmmv 	case VCHR:
395ec933656Sjmmv 		/* FALLTHROUGH */
396ec933656Sjmmv 	case VSOCK:
397ec933656Sjmmv 		/* FALLTHROUGH */
398ec933656Sjmmv 	case VFIFO:
399ec933656Sjmmv 		break;
400ec933656Sjmmv 
401ec933656Sjmmv 	default:
402ec933656Sjmmv 		error = EINVAL;
403ec933656Sjmmv 		goto out;
404ec933656Sjmmv 	}
405ec933656Sjmmv 
406ec933656Sjmmv 	if (mode & VWRITE && node->tn_flags & IMMUTABLE) {
407ec933656Sjmmv 		error = EPERM;
408ec933656Sjmmv 		goto out;
409ec933656Sjmmv 	}
410ec933656Sjmmv 
411ec933656Sjmmv 	error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
412ec933656Sjmmv 	    node->tn_gid, mode, cred);
413ec933656Sjmmv 
414ec933656Sjmmv out:
415ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
416ec933656Sjmmv 
417ec933656Sjmmv 	return error;
418ec933656Sjmmv }
419ec933656Sjmmv 
420ec933656Sjmmv /* --------------------------------------------------------------------- */
421ec933656Sjmmv 
422ec933656Sjmmv int
423ec933656Sjmmv tmpfs_getattr(void *v)
424ec933656Sjmmv {
425ec933656Sjmmv 	struct vnode *vp = ((struct vop_getattr_args *)v)->a_vp;
426ec933656Sjmmv 	struct vattr *vap = ((struct vop_getattr_args *)v)->a_vap;
427ec933656Sjmmv 
428ec933656Sjmmv 	struct tmpfs_node *node;
429ec933656Sjmmv 
430ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
431ec933656Sjmmv 
432ec933656Sjmmv 	VATTR_NULL(vap);
433ec933656Sjmmv 
4345ce7d987Syamt 	tmpfs_itimes(vp, NULL, NULL);
4355ce7d987Syamt 
436ec933656Sjmmv 	vap->va_type = vp->v_type;
437ec933656Sjmmv 	vap->va_mode = node->tn_mode;
438ec933656Sjmmv 	vap->va_nlink = node->tn_links;
439ec933656Sjmmv 	vap->va_uid = node->tn_uid;
440ec933656Sjmmv 	vap->va_gid = node->tn_gid;
441ec933656Sjmmv 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
442ec933656Sjmmv 	vap->va_fileid = node->tn_id;
443ec933656Sjmmv 	vap->va_size = node->tn_size;
444ec933656Sjmmv 	vap->va_blocksize = PAGE_SIZE;
445ec933656Sjmmv 	vap->va_atime = node->tn_atime;
446ec933656Sjmmv 	vap->va_mtime = node->tn_mtime;
447ec933656Sjmmv 	vap->va_ctime = node->tn_ctime;
448ec933656Sjmmv 	vap->va_birthtime = node->tn_birthtime;
449ec933656Sjmmv 	vap->va_gen = node->tn_gen;
450ec933656Sjmmv 	vap->va_flags = node->tn_flags;
451ec933656Sjmmv 	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
452064fbe7eSjmmv 		node->tn_spec.tn_dev.tn_rdev : VNOVAL;
453ec933656Sjmmv 	vap->va_bytes = round_page(node->tn_size);
454ec933656Sjmmv 	vap->va_filerev = VNOVAL;
455ec933656Sjmmv 	vap->va_vaflags = 0;
456ec933656Sjmmv 	vap->va_spare = VNOVAL; /* XXX */
457ec933656Sjmmv 
458ec933656Sjmmv 	return 0;
459ec933656Sjmmv }
460ec933656Sjmmv 
461ec933656Sjmmv /* --------------------------------------------------------------------- */
462ec933656Sjmmv 
463ec933656Sjmmv /* XXX Should this operation be atomic?  I think it should, but code in
464ec933656Sjmmv  * XXX other places (e.g., ufs) doesn't seem to be... */
465ec933656Sjmmv int
466ec933656Sjmmv tmpfs_setattr(void *v)
467ec933656Sjmmv {
468ec933656Sjmmv 	struct vnode *vp = ((struct vop_setattr_args *)v)->a_vp;
469ec933656Sjmmv 	struct vattr *vap = ((struct vop_setattr_args *)v)->a_vap;
470fc9422c9Selad 	kauth_cred_t cred = ((struct vop_setattr_args *)v)->a_cred;
47195e1ffb1Schristos 	struct lwp *l = ((struct vop_setattr_args *)v)->a_l;
472ec933656Sjmmv 
473a748ea88Syamt 	int error;
474ec933656Sjmmv 
475ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
476ec933656Sjmmv 
477ec933656Sjmmv 	error = 0;
478ec933656Sjmmv 
479ec933656Sjmmv 	/* Abort if any unsettable attribute is given. */
480ec933656Sjmmv 	if (vap->va_type != VNON ||
481ec933656Sjmmv 	    vap->va_nlink != VNOVAL ||
482ec933656Sjmmv 	    vap->va_fsid != VNOVAL ||
483ec933656Sjmmv 	    vap->va_fileid != VNOVAL ||
484ec933656Sjmmv 	    vap->va_blocksize != VNOVAL ||
485ec933656Sjmmv 	    vap->va_ctime.tv_sec != VNOVAL ||
486ec933656Sjmmv 	    vap->va_ctime.tv_nsec != VNOVAL ||
487ec933656Sjmmv 	    vap->va_birthtime.tv_sec != VNOVAL ||
488ec933656Sjmmv 	    vap->va_birthtime.tv_nsec != VNOVAL ||
489ec933656Sjmmv 	    vap->va_gen != VNOVAL ||
490ec933656Sjmmv 	    vap->va_rdev != VNOVAL ||
491ec933656Sjmmv 	    vap->va_bytes != VNOVAL)
492ec933656Sjmmv 		error = EINVAL;
493ec933656Sjmmv 
494ec933656Sjmmv 	if (error == 0 && (vap->va_flags != VNOVAL))
495f474dcebSad 		error = tmpfs_chflags(vp, vap->va_flags, cred, l);
496ec933656Sjmmv 
497ec933656Sjmmv 	if (error == 0 && (vap->va_size != VNOVAL))
498f474dcebSad 		error = tmpfs_chsize(vp, vap->va_size, cred, l);
499ec933656Sjmmv 
500ec933656Sjmmv 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
501f474dcebSad 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
502ec933656Sjmmv 
503ec933656Sjmmv 	if (error == 0 && (vap->va_mode != VNOVAL))
504f474dcebSad 		error = tmpfs_chmod(vp, vap->va_mode, cred, l);
505ec933656Sjmmv 
506ec933656Sjmmv 	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
507ec933656Sjmmv 	    vap->va_atime.tv_nsec != VNOVAL) ||
508ec933656Sjmmv 	    (vap->va_mtime.tv_sec != VNOVAL &&
509ec933656Sjmmv 	    vap->va_mtime.tv_nsec != VNOVAL)))
510ec933656Sjmmv 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
51195e1ffb1Schristos 		    vap->va_vaflags, cred, l);
512ec933656Sjmmv 
513ec933656Sjmmv 	/* Update the node times.  We give preference to the error codes
514ec933656Sjmmv 	 * generated by this function rather than the ones that may arise
515ec933656Sjmmv 	 * from tmpfs_update. */
516a748ea88Syamt 	tmpfs_update(vp, NULL, NULL, 0);
517ec933656Sjmmv 
518ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
519ec933656Sjmmv 
520ec933656Sjmmv 	return error;
521ec933656Sjmmv }
522ec933656Sjmmv 
523ec933656Sjmmv /* --------------------------------------------------------------------- */
524ec933656Sjmmv 
525ec933656Sjmmv int
526ec933656Sjmmv tmpfs_read(void *v)
527ec933656Sjmmv {
528ec933656Sjmmv 	struct vnode *vp = ((struct vop_read_args *)v)->a_vp;
529ec933656Sjmmv 	struct uio *uio = ((struct vop_read_args *)v)->a_uio;
530ec933656Sjmmv 
5315f4b660eSjmmv 	int error;
5325f4b660eSjmmv 	int flags;
533ec933656Sjmmv 	struct tmpfs_node *node;
534647aa775Syamt 	struct uvm_object *uobj;
535ec933656Sjmmv 
536ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
537ec933656Sjmmv 
538ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
539ec933656Sjmmv 
540a7ca1cc6Syamt 	if (vp->v_type != VREG) {
541a7ca1cc6Syamt 		error = EISDIR;
542a7ca1cc6Syamt 		goto out;
543a7ca1cc6Syamt 	}
544a7ca1cc6Syamt 
545a7ca1cc6Syamt 	if (uio->uio_offset < 0) {
546ec933656Sjmmv 		error = EINVAL;
547ec933656Sjmmv 		goto out;
548ec933656Sjmmv 	}
549ec933656Sjmmv 
550ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED;
551ec933656Sjmmv 
552064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
553647aa775Syamt 	flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;
554ec933656Sjmmv 	error = 0;
5555f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
556647aa775Syamt 		vsize_t len;
557647aa775Syamt 		void *win;
558ec933656Sjmmv 
5597720dda1Syamt 		if (node->tn_size <= uio->uio_offset)
5607720dda1Syamt 			break;
5617720dda1Syamt 
562ec933656Sjmmv 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
5635f4b660eSjmmv 		if (len == 0)
564647aa775Syamt 			break;
5655f4b660eSjmmv 
56622161687Syamt 		win = ubc_alloc(uobj, uio->uio_offset, &len, UVM_ADV_NORMAL,
56722161687Syamt 		    UBC_READ);
568647aa775Syamt 		error = uiomove(win, len, uio);
569647aa775Syamt 		ubc_release(win, flags);
570647aa775Syamt 	}
571ec933656Sjmmv 
572ec933656Sjmmv out:
573ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
574ec933656Sjmmv 
575ec933656Sjmmv 	return error;
576ec933656Sjmmv }
577ec933656Sjmmv 
578ec933656Sjmmv /* --------------------------------------------------------------------- */
579ec933656Sjmmv 
580ec933656Sjmmv int
581ec933656Sjmmv tmpfs_write(void *v)
582ec933656Sjmmv {
583ec933656Sjmmv 	struct vnode *vp = ((struct vop_write_args *)v)->a_vp;
584ec933656Sjmmv 	struct uio *uio = ((struct vop_write_args *)v)->a_uio;
585ec933656Sjmmv 	int ioflag = ((struct vop_write_args *)v)->a_ioflag;
586ec933656Sjmmv 
587ec933656Sjmmv 	boolean_t extended;
588ec933656Sjmmv 	int error;
5895f4b660eSjmmv 	int flags;
590ec933656Sjmmv 	off_t oldsize;
591ec933656Sjmmv 	struct tmpfs_node *node;
592647aa775Syamt 	struct uvm_object *uobj;
593ec933656Sjmmv 
594ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
595ec933656Sjmmv 
596ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
597ec933656Sjmmv 	oldsize = node->tn_size;
598ec933656Sjmmv 
599ec933656Sjmmv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
600ec933656Sjmmv 		error = EINVAL;
601ec933656Sjmmv 		goto out;
602ec933656Sjmmv 	}
603ec933656Sjmmv 
604ec933656Sjmmv 	if (uio->uio_resid == 0) {
605ec933656Sjmmv 		error = 0;
606ec933656Sjmmv 		goto out;
607ec933656Sjmmv 	}
608ec933656Sjmmv 
609ec933656Sjmmv 	if (ioflag & IO_APPEND)
610ec933656Sjmmv 		uio->uio_offset = node->tn_size;
611ec933656Sjmmv 
612ec933656Sjmmv 	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
613ec933656Sjmmv 	if (extended) {
614ec933656Sjmmv 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
615ec933656Sjmmv 		if (error != 0)
616ec933656Sjmmv 			goto out;
617ec933656Sjmmv 	}
618ec933656Sjmmv 
619064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
620647aa775Syamt 	flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;
621647aa775Syamt 	error = 0;
6225f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
623647aa775Syamt 		vsize_t len;
624647aa775Syamt 		void *win;
625647aa775Syamt 
626647aa775Syamt 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
6275f4b660eSjmmv 		if (len == 0)
628647aa775Syamt 			break;
6295f4b660eSjmmv 
63022161687Syamt 		win = ubc_alloc(uobj, uio->uio_offset, &len, UVM_ADV_NORMAL,
63122161687Syamt 		    UBC_WRITE);
632647aa775Syamt 		error = uiomove(win, len, uio);
633647aa775Syamt 		ubc_release(win, flags);
634647aa775Syamt 	}
635647aa775Syamt 
636ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
637ec933656Sjmmv 	    (extended ? TMPFS_NODE_CHANGED : 0);
638ec933656Sjmmv 
6395f4b660eSjmmv 	if (error != 0)
640647aa775Syamt 		(void)tmpfs_reg_resize(vp, oldsize);
641647aa775Syamt 
642117b5f51Sjmmv 	VN_KNOTE(vp, NOTE_WRITE);
643117b5f51Sjmmv 
644ec933656Sjmmv out:
645ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
646ec933656Sjmmv 	KASSERT(IMPLIES(error == 0, uio->uio_resid == 0));
647ec933656Sjmmv 	KASSERT(IMPLIES(error != 0, oldsize == node->tn_size));
648ec933656Sjmmv 
649ec933656Sjmmv 	return error;
650ec933656Sjmmv }
651ec933656Sjmmv 
652ec933656Sjmmv /* --------------------------------------------------------------------- */
653ec933656Sjmmv 
654ec933656Sjmmv int
655ec933656Sjmmv tmpfs_fsync(void *v)
656ec933656Sjmmv {
657ec933656Sjmmv 	struct vnode *vp = ((struct vop_fsync_args *)v)->a_vp;
658ec933656Sjmmv 
659ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
660ec933656Sjmmv 
661a748ea88Syamt 	tmpfs_update(vp, NULL, NULL, 0);
662a748ea88Syamt 
663a748ea88Syamt 	return 0;
664ec933656Sjmmv }
665ec933656Sjmmv 
666ec933656Sjmmv /* --------------------------------------------------------------------- */
667ec933656Sjmmv 
668ec933656Sjmmv int
669ec933656Sjmmv tmpfs_remove(void *v)
670ec933656Sjmmv {
671ec933656Sjmmv 	struct vnode *dvp = ((struct vop_remove_args *)v)->a_dvp;
672ec933656Sjmmv 	struct vnode *vp = ((struct vop_remove_args *)v)->a_vp;
673ec933656Sjmmv 
674ec933656Sjmmv 	int error;
675ec933656Sjmmv 	struct tmpfs_dirent *de;
676ec933656Sjmmv 	struct tmpfs_mount *tmp;
677ec933656Sjmmv 	struct tmpfs_node *dnode;
678ec933656Sjmmv 	struct tmpfs_node *node;
679ec933656Sjmmv 
680ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
681ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
682ec933656Sjmmv 
683ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
684ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
685ec933656Sjmmv 	tmp = VFS_TO_TMPFS(vp->v_mount);
686ec933656Sjmmv 	de = node->tn_lookup_dirent;
687ec933656Sjmmv 	KASSERT(de != NULL);
688ec933656Sjmmv 
689ec933656Sjmmv 	/* XXX: Why isn't this done by the caller? */
690ec933656Sjmmv 	if (vp->v_type == VDIR) {
691ec933656Sjmmv 		error = EISDIR;
692ec933656Sjmmv 		goto out;
693ec933656Sjmmv 	}
694ec933656Sjmmv 
695ec933656Sjmmv 	/* Files marked as immutable or append-only cannot be deleted. */
696ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
697ec933656Sjmmv 		error = EPERM;
698ec933656Sjmmv 		goto out;
699ec933656Sjmmv 	}
700ec933656Sjmmv 
701ec933656Sjmmv 	/* Remove the entry from the directory; as it is a file, we do not
702ec933656Sjmmv 	 * have to change the number of hard links of the directory. */
703ec933656Sjmmv 	tmpfs_dir_detach(dvp, de);
704ec933656Sjmmv 
705ec933656Sjmmv 	/* Free the directory entry we just deleted.  Note that the node
706ec933656Sjmmv 	 * referred by it will not be removed until the vnode is really
707ec933656Sjmmv 	 * reclaimed. */
708ec933656Sjmmv 	tmpfs_free_dirent(tmp, de, TRUE);
709ec933656Sjmmv 
710ec933656Sjmmv 	error = 0;
711ec933656Sjmmv 
712ec933656Sjmmv out:
713ec933656Sjmmv 	vput(dvp);
714ec933656Sjmmv 	vput(vp);
715ec933656Sjmmv 
716ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(dvp));
717ec933656Sjmmv 
718ec933656Sjmmv 	return error;
719ec933656Sjmmv }
720ec933656Sjmmv 
721ec933656Sjmmv /* --------------------------------------------------------------------- */
722ec933656Sjmmv 
723ec933656Sjmmv int
724ec933656Sjmmv tmpfs_link(void *v)
725ec933656Sjmmv {
726ec933656Sjmmv 	struct vnode *dvp = ((struct vop_link_args *)v)->a_dvp;
727ec933656Sjmmv 	struct vnode *vp = ((struct vop_link_args *)v)->a_vp;
728ec933656Sjmmv 	struct componentname *cnp = ((struct vop_link_args *)v)->a_cnp;
729ec933656Sjmmv 
730ec933656Sjmmv 	int error;
731ec933656Sjmmv 	struct tmpfs_dirent *de;
732ec933656Sjmmv 	struct tmpfs_node *dnode;
733ec933656Sjmmv 	struct tmpfs_node *node;
734ec933656Sjmmv 
735ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
736ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
737ec933656Sjmmv 	KASSERT(cnp->cn_flags & HASBUF);
738ec933656Sjmmv 	KASSERT(dvp != vp); /* XXX When can this be false? */
739ec933656Sjmmv 
740ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
741ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
742ec933656Sjmmv 
743a748ea88Syamt 	/* Lock vp because we will need to run tmpfs_update over it, which
744ec933656Sjmmv 	 * needs the vnode to be locked. */
745ec933656Sjmmv 	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
746ec933656Sjmmv 	if (error != 0)
747ec933656Sjmmv 		goto out;
748ec933656Sjmmv 
749ec933656Sjmmv 	/* XXX: Why aren't the following two tests done by the caller? */
750ec933656Sjmmv 
751ec933656Sjmmv 	/* Hard links of directories are forbidden. */
752ec933656Sjmmv 	if (vp->v_type == VDIR) {
753ec933656Sjmmv 		error = EPERM;
754ec933656Sjmmv 		goto out;
755ec933656Sjmmv 	}
756ec933656Sjmmv 
757ec933656Sjmmv 	/* Cannot create cross-device links. */
758ec933656Sjmmv 	if (dvp->v_mount != vp->v_mount) {
759ec933656Sjmmv 		error = EXDEV;
760ec933656Sjmmv 		goto out;
761ec933656Sjmmv 	}
762ec933656Sjmmv 
763ec933656Sjmmv 	/* Ensure that we do not overflow the maximum number of links imposed
764ec933656Sjmmv 	 * by the system. */
765ec933656Sjmmv 	KASSERT(node->tn_links <= LINK_MAX);
766ec933656Sjmmv 	if (node->tn_links == LINK_MAX) {
767ec933656Sjmmv 		error = EMLINK;
768ec933656Sjmmv 		goto out;
769ec933656Sjmmv 	}
770ec933656Sjmmv 
771ec933656Sjmmv 	/* We cannot create links of files marked immutable or append-only. */
772ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
773ec933656Sjmmv 		error = EPERM;
774ec933656Sjmmv 		goto out;
775ec933656Sjmmv 	}
776ec933656Sjmmv 
777ec933656Sjmmv 	/* Allocate a new directory entry to represent the node. */
778ec933656Sjmmv 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
779ec933656Sjmmv 	    cnp->cn_nameptr, cnp->cn_namelen, &de);
780ec933656Sjmmv 	if (error != 0)
781ec933656Sjmmv 		goto out;
782ec933656Sjmmv 
783ec933656Sjmmv 	/* Insert the new directory entry into the appropriate directory. */
784ec933656Sjmmv 	tmpfs_dir_attach(dvp, de);
785ec933656Sjmmv 
786ec933656Sjmmv 	/* vp link count has changed, so update node times. */
787ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_CHANGED;
788a748ea88Syamt 	tmpfs_update(vp, NULL, NULL, 0);
789ec933656Sjmmv 
790ec933656Sjmmv 	error = 0;
791ec933656Sjmmv 
792ec933656Sjmmv out:
793ec933656Sjmmv 	if (VOP_ISLOCKED(vp))
794ec933656Sjmmv 		VOP_UNLOCK(vp, 0);
795ec933656Sjmmv 
796ec933656Sjmmv 	PNBUF_PUT(cnp->cn_pnbuf);
797ec933656Sjmmv 
798ec933656Sjmmv 	vput(dvp);
799ec933656Sjmmv 
800ec933656Sjmmv 	/* XXX Locking status of dvp does not match manual page. */
801ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(dvp));
802ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
803ec933656Sjmmv 
804ec933656Sjmmv 	return error;
805ec933656Sjmmv }
806ec933656Sjmmv 
807ec933656Sjmmv /* --------------------------------------------------------------------- */
808ec933656Sjmmv 
809ec933656Sjmmv int
810ec933656Sjmmv tmpfs_rename(void *v)
811ec933656Sjmmv {
812ec933656Sjmmv 	struct vnode *fdvp = ((struct vop_rename_args *)v)->a_fdvp;
813ec933656Sjmmv 	struct vnode *fvp = ((struct vop_rename_args *)v)->a_fvp;
814ec933656Sjmmv 	struct componentname *fcnp = ((struct vop_rename_args *)v)->a_fcnp;
815ec933656Sjmmv 	struct vnode *tdvp = ((struct vop_rename_args *)v)->a_tdvp;
816ec933656Sjmmv 	struct vnode *tvp = ((struct vop_rename_args *)v)->a_tvp;
817ec933656Sjmmv 	struct componentname *tcnp = ((struct vop_rename_args *)v)->a_tcnp;
818ec933656Sjmmv 
819ec933656Sjmmv 	char *newname;
820ec933656Sjmmv 	int error;
821ec933656Sjmmv 	struct tmpfs_dirent *de;
822ec933656Sjmmv 	struct tmpfs_mount *tmp;
823ec933656Sjmmv 	struct tmpfs_node *fdnode;
824ec933656Sjmmv 	struct tmpfs_node *fnode;
825ec933656Sjmmv 	struct tmpfs_node *tdnode;
826ec933656Sjmmv 
827ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(tdvp));
828ec933656Sjmmv 	KASSERT(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
829ec933656Sjmmv 	KASSERT(fcnp->cn_flags & HASBUF);
830ec933656Sjmmv 	KASSERT(tcnp->cn_flags & HASBUF);
831ec933656Sjmmv 
832ec933656Sjmmv 	fdnode = VP_TO_TMPFS_DIR(fdvp);
833ec933656Sjmmv 	fnode = VP_TO_TMPFS_NODE(fvp);
834ec933656Sjmmv 	de = fnode->tn_lookup_dirent;
835ec933656Sjmmv 
836ec933656Sjmmv 	/* Disallow cross-device renames.
837ec933656Sjmmv 	 * XXX Why isn't this done by the caller? */
838ec933656Sjmmv 	if (fvp->v_mount != tdvp->v_mount ||
839ec933656Sjmmv 	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
840ec933656Sjmmv 		error = EXDEV;
841ec933656Sjmmv 		goto out;
842ec933656Sjmmv 	}
843ec933656Sjmmv 
844ec933656Sjmmv 	tmp = VFS_TO_TMPFS(tdvp->v_mount);
845ec933656Sjmmv 	tdnode = VP_TO_TMPFS_DIR(tdvp);
846ec933656Sjmmv 
847ec933656Sjmmv 	/* If source and target are the same file, there is nothing to do. */
848ec933656Sjmmv 	if (fvp == tvp) {
849ec933656Sjmmv 		error = 0;
850ec933656Sjmmv 		goto out;
851ec933656Sjmmv 	}
852ec933656Sjmmv 
853ec933656Sjmmv 	/* Avoid manipulating '.' and '..' entries. */
854ec933656Sjmmv 	if (de == NULL) {
855ec933656Sjmmv 		KASSERT(fvp->v_type == VDIR);
856ec933656Sjmmv 		error = EINVAL;
857ec933656Sjmmv 		goto out;
858ec933656Sjmmv 	}
859ec933656Sjmmv 	KASSERT(de->td_node == fnode);
860ec933656Sjmmv 
861f63b58d1Sjmmv 	/* If we need to move the directory between entries, lock the
862f63b58d1Sjmmv 	 * source so that we can safely operate on it. */
863f63b58d1Sjmmv 	if (fdnode != tdnode) {
864f63b58d1Sjmmv 		error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
865f63b58d1Sjmmv 		if (error != 0)
86658e607b9Syamt 			goto out;
867f63b58d1Sjmmv 	}
868f63b58d1Sjmmv 
869ec933656Sjmmv 	/* Ensure that we have enough memory to hold the new name, if it
870ec933656Sjmmv 	 * has to be changed. */
871ec933656Sjmmv 	if (fcnp->cn_namelen != tcnp->cn_namelen ||
872ec933656Sjmmv 	    memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
873ec933656Sjmmv 		newname = tmpfs_str_pool_get(&tmp->tm_str_pool,
874ec933656Sjmmv 		    tcnp->cn_namelen, 0);
875ec933656Sjmmv 		if (newname == NULL) {
876ec933656Sjmmv 			error = ENOSPC;
87758e607b9Syamt 			goto out_locked;
878ec933656Sjmmv 		}
879ec933656Sjmmv 	} else
880ec933656Sjmmv 		newname = NULL;
881ec933656Sjmmv 
882ec933656Sjmmv 	/* If the node is being moved to another directory, we have to do
883ec933656Sjmmv 	 * the move. */
884ec933656Sjmmv 	if (fdnode != tdnode) {
885ec933656Sjmmv 		/* In case we are moving a directory, we have to adjust its
886ec933656Sjmmv 		 * parent to point to the new parent. */
887ec933656Sjmmv 		if (de->td_node->tn_type == VDIR) {
888ec933656Sjmmv 			struct tmpfs_node *n;
889ec933656Sjmmv 
890ec933656Sjmmv 			/* Ensure the target directory is not a child of the
891ec933656Sjmmv 			 * directory being moved.  Otherwise, we'd end up
892ec933656Sjmmv 			 * with stale nodes. */
893ec933656Sjmmv 			n = tdnode;
894064fbe7eSjmmv 			while (n != n->tn_spec.tn_dir.tn_parent) {
895ec933656Sjmmv 				if (n == fnode) {
896ec933656Sjmmv 					error = EINVAL;
89758e607b9Syamt 					goto out_locked;
898ec933656Sjmmv 				}
899064fbe7eSjmmv 				n = n->tn_spec.tn_dir.tn_parent;
900ec933656Sjmmv 			}
901ec933656Sjmmv 
902ec933656Sjmmv 			/* Adjust the parent pointer. */
903ec933656Sjmmv 			TMPFS_VALIDATE_DIR(fnode);
904064fbe7eSjmmv 			de->td_node->tn_spec.tn_dir.tn_parent = tdnode;
905ec933656Sjmmv 
906ec933656Sjmmv 			/* As a result of changing the target of the '..'
907ec933656Sjmmv 			 * entry, the link count of the source and target
908ec933656Sjmmv 			 * directories has to be adjusted. */
909ec933656Sjmmv 			fdnode->tn_links--;
910ec933656Sjmmv 			tdnode->tn_links++;
911ec933656Sjmmv 		}
912ec933656Sjmmv 
913ec933656Sjmmv 		/* Do the move: just remove the entry from the source directory
914ec933656Sjmmv 		 * and insert it into the target one. */
915ec933656Sjmmv 		tmpfs_dir_detach(fdvp, de);
916ec933656Sjmmv 		tmpfs_dir_attach(tdvp, de);
917ec933656Sjmmv 
918ec933656Sjmmv 		/* Notify listeners of fdvp about the change in the directory.
919ec933656Sjmmv 		 * We can do it at this point because we aren't touching fdvp
920ec933656Sjmmv 		 * any more below. */
921ec933656Sjmmv 		VN_KNOTE(fdvp, NOTE_WRITE);
922ec933656Sjmmv 	}
923ec933656Sjmmv 
924ec933656Sjmmv 	/* If the name has changed, we need to make it effective by changing
925ec933656Sjmmv 	 * it in the directory entry. */
926ec933656Sjmmv 	if (newname != NULL) {
927ec933656Sjmmv 		KASSERT(tcnp->cn_namelen < MAXNAMLEN);
928ec933656Sjmmv 		KASSERT(tcnp->cn_namelen < 0xffff);
929ec933656Sjmmv 
930ec933656Sjmmv 		tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name,
931ec933656Sjmmv 		    de->td_namelen);
932ec933656Sjmmv 		de->td_namelen = (uint16_t)tcnp->cn_namelen;
933ec933656Sjmmv 		memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
934ec933656Sjmmv 		de->td_name = newname;
935ec933656Sjmmv 
93671f9a077Sjmmv 		fnode->tn_status |= TMPFS_NODE_CHANGED;
9372185c988Sjmmv 		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
938ec933656Sjmmv 	}
939ec933656Sjmmv 
940ec933656Sjmmv 	/* If we are overwriting an entry, we have to remove the old one
941ec933656Sjmmv 	 * from the target directory. */
942ec933656Sjmmv 	if (tvp != NULL) {
943ec933656Sjmmv 		struct tmpfs_node *tnode;
944ec933656Sjmmv 
945ec933656Sjmmv 		tnode = VP_TO_TMPFS_NODE(tvp);
946ec933656Sjmmv 
947ec933656Sjmmv 		/* The source node cannot be a directory in this case. */
948ec933656Sjmmv 		KASSERT(fnode->tn_type != VDIR);
949ec933656Sjmmv 
950ec933656Sjmmv 		/* Remove the old entry from the target directory. */
951ec933656Sjmmv 		de = tnode->tn_lookup_dirent;
952ec933656Sjmmv 		tmpfs_dir_detach(tdvp, de);
953ec933656Sjmmv 
954ec933656Sjmmv 		/* Free the directory entry we just deleted.  Note that the
955ec933656Sjmmv 		 * node referred by it will not be removed until the vnode is
956ec933656Sjmmv 		 * really reclaimed. */
957ec933656Sjmmv 		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE);
958ec933656Sjmmv 	}
959ec933656Sjmmv 
960ec933656Sjmmv 	/* Notify listeners of tdvp about the change in the directory (either
961117b5f51Sjmmv 	 * because a new entry was added or because one was removed) and
962117b5f51Sjmmv 	 * listeners of fvp about the rename. */
963ec933656Sjmmv 	VN_KNOTE(tdvp, NOTE_WRITE);
964117b5f51Sjmmv 	VN_KNOTE(fvp, NOTE_RENAME);
965ec933656Sjmmv 
966ec933656Sjmmv 	error = 0;
967ec933656Sjmmv 
968f63b58d1Sjmmv out_locked:
969f63b58d1Sjmmv 	if (fdnode != tdnode)
970f63b58d1Sjmmv 		VOP_UNLOCK(fdvp, 0);
971f63b58d1Sjmmv 
972ec933656Sjmmv out:
973ec933656Sjmmv 	/* Release target nodes. */
974ec933656Sjmmv 	/* XXX: I don't understand when tdvp can be the same as tvp, but
975ec933656Sjmmv 	 * other code takes care of this... */
976ec933656Sjmmv 	if (tdvp == tvp)
977ec933656Sjmmv 		vrele(tdvp);
978ec933656Sjmmv 	else
979ec933656Sjmmv 		vput(tdvp);
980ec933656Sjmmv 	if (tvp != NULL)
981ec933656Sjmmv 		vput(tvp);
982ec933656Sjmmv 
983ec933656Sjmmv 	/* Release source nodes. */
984ec933656Sjmmv 	vrele(fdvp);
985ec933656Sjmmv 	vrele(fvp);
986ec933656Sjmmv 
987ec933656Sjmmv 	return error;
988ec933656Sjmmv }
989ec933656Sjmmv 
990ec933656Sjmmv /* --------------------------------------------------------------------- */
991ec933656Sjmmv 
992ec933656Sjmmv int
993ec933656Sjmmv tmpfs_mkdir(void *v)
994ec933656Sjmmv {
995ec933656Sjmmv 	struct vnode *dvp = ((struct vop_mkdir_args *)v)->a_dvp;
996ec933656Sjmmv 	struct vnode **vpp = ((struct vop_mkdir_args *)v)->a_vpp;
997ec933656Sjmmv 	struct componentname *cnp = ((struct vop_mkdir_args *)v)->a_cnp;
998ec933656Sjmmv 	struct vattr *vap = ((struct vop_mkdir_args *)v)->a_vap;
999ec933656Sjmmv 
1000ec933656Sjmmv 	KASSERT(vap->va_type == VDIR);
1001ec933656Sjmmv 
1002ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
1003ec933656Sjmmv }
1004ec933656Sjmmv 
1005ec933656Sjmmv /* --------------------------------------------------------------------- */
1006ec933656Sjmmv 
1007ec933656Sjmmv int
1008ec933656Sjmmv tmpfs_rmdir(void *v)
1009ec933656Sjmmv {
1010ec933656Sjmmv 	struct vnode *dvp = ((struct vop_rmdir_args *)v)->a_dvp;
1011ec933656Sjmmv 	struct vnode *vp = ((struct vop_rmdir_args *)v)->a_vp;
1012ec933656Sjmmv 
1013ec933656Sjmmv 	int error;
1014ec933656Sjmmv 	struct tmpfs_dirent *de;
1015ec933656Sjmmv 	struct tmpfs_mount *tmp;
1016ec933656Sjmmv 	struct tmpfs_node *dnode;
1017ec933656Sjmmv 	struct tmpfs_node *node;
1018ec933656Sjmmv 
1019ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
1020ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1021ec933656Sjmmv 
1022ec933656Sjmmv 	tmp = VFS_TO_TMPFS(dvp->v_mount);
1023ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
1024ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
1025064fbe7eSjmmv 	KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
1026ec933656Sjmmv 
1027ec933656Sjmmv 	/* Get the directory entry associated with node (vp).  This was
1028ec933656Sjmmv 	 * filled by tmpfs_lookup while looking up the entry. */
1029ec933656Sjmmv 	de = node->tn_lookup_dirent;
1030ec933656Sjmmv 	KASSERT(TMPFS_DIRENT_MATCHES(de,
1031ec933656Sjmmv 	    ((struct vop_rmdir_args *)v)->a_cnp->cn_nameptr,
1032ec933656Sjmmv 	    ((struct vop_rmdir_args *)v)->a_cnp->cn_namelen));
1033ec933656Sjmmv 
1034ec933656Sjmmv 	/* Directories with more than two entries ('.' and '..') cannot be
1035ec933656Sjmmv 	 * removed. */
1036ec933656Sjmmv 	if (node->tn_size > 0) {
1037ec933656Sjmmv 		error = ENOTEMPTY;
1038ec933656Sjmmv 		goto out;
1039ec933656Sjmmv 	}
1040ec933656Sjmmv 
1041ec933656Sjmmv 	/* Check flags to see if we are allowed to remove the directory. */
1042ec933656Sjmmv 	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
1043ec933656Sjmmv 		error = EPERM;
1044ec933656Sjmmv 		goto out;
1045ec933656Sjmmv 	}
1046ec933656Sjmmv 
1047ec933656Sjmmv 	/* Detach the directory entry from the directory (dnode). */
1048ec933656Sjmmv 	tmpfs_dir_detach(dvp, de);
1049ec933656Sjmmv 
1050ec933656Sjmmv 	node->tn_links--;
1051ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
1052ec933656Sjmmv 	    TMPFS_NODE_MODIFIED;
1053064fbe7eSjmmv 	node->tn_spec.tn_dir.tn_parent->tn_links--;
1054064fbe7eSjmmv 	node->tn_spec.tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \
1055ec933656Sjmmv 	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1056ec933656Sjmmv 
1057117b5f51Sjmmv 	/* Release the parent. */
1058ec933656Sjmmv 	cache_purge(dvp); /* XXX Is this needed? */
1059ec933656Sjmmv 	vput(dvp);
1060ec933656Sjmmv 
1061ec933656Sjmmv 	/* Free the directory entry we just deleted.  Note that the node
1062ec933656Sjmmv 	 * referred by it will not be removed until the vnode is really
1063ec933656Sjmmv 	 * reclaimed. */
1064ec933656Sjmmv 	tmpfs_free_dirent(tmp, de, TRUE);
1065ec933656Sjmmv 
1066ec933656Sjmmv 	/* Release the deleted vnode (will destroy the node, notify
1067ec933656Sjmmv 	 * interested parties and clean it from the cache). */
1068ec933656Sjmmv 	vput(vp);
1069ec933656Sjmmv 
1070ec933656Sjmmv 	error = 0;
1071ec933656Sjmmv 
1072ec933656Sjmmv out:
1073ec933656Sjmmv 	if (error != 0) {
1074ec933656Sjmmv 		vput(dvp);
1075ec933656Sjmmv 		vput(vp);
1076ec933656Sjmmv 	}
1077ec933656Sjmmv 
1078ec933656Sjmmv 	return error;
1079ec933656Sjmmv }
1080ec933656Sjmmv 
1081ec933656Sjmmv /* --------------------------------------------------------------------- */
1082ec933656Sjmmv 
1083ec933656Sjmmv int
1084ec933656Sjmmv tmpfs_symlink(void *v)
1085ec933656Sjmmv {
1086ec933656Sjmmv 	struct vnode *dvp = ((struct vop_symlink_args *)v)->a_dvp;
1087ec933656Sjmmv 	struct vnode **vpp = ((struct vop_symlink_args *)v)->a_vpp;
1088ec933656Sjmmv 	struct componentname *cnp = ((struct vop_symlink_args *)v)->a_cnp;
1089ec933656Sjmmv 	struct vattr *vap = ((struct vop_symlink_args *)v)->a_vap;
1090ec933656Sjmmv 	char *target = ((struct vop_symlink_args *)v)->a_target;
1091ec933656Sjmmv 
1092ec933656Sjmmv 	KASSERT(vap->va_type == VLNK);
1093ec933656Sjmmv 
1094ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1095ec933656Sjmmv }
1096ec933656Sjmmv 
1097ec933656Sjmmv /* --------------------------------------------------------------------- */
1098ec933656Sjmmv 
1099ec933656Sjmmv int
1100ec933656Sjmmv tmpfs_readdir(void *v)
1101ec933656Sjmmv {
1102ec933656Sjmmv 	struct vnode *vp = ((struct vop_readdir_args *)v)->a_vp;
1103ec933656Sjmmv 	struct uio *uio = ((struct vop_readdir_args *)v)->a_uio;
1104ec933656Sjmmv 	int *eofflag = ((struct vop_readdir_args *)v)->a_eofflag;
1105ec933656Sjmmv 	off_t **cookies = ((struct vop_readdir_args *)v)->a_cookies;
1106ec933656Sjmmv 	int *ncookies = ((struct vop_readdir_args *)v)->a_ncookies;
1107ec933656Sjmmv 
1108ec933656Sjmmv 	int error;
11098e6209cfSyamt 	off_t startoff;
11108e6209cfSyamt 	off_t cnt;
1111ec933656Sjmmv 	struct tmpfs_node *node;
1112ec933656Sjmmv 
1113ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1114ec933656Sjmmv 
1115ec933656Sjmmv 	/* This operation only makes sense on directory nodes. */
1116ec933656Sjmmv 	if (vp->v_type != VDIR) {
1117ec933656Sjmmv 		error = ENOTDIR;
1118ec933656Sjmmv 		goto out;
1119ec933656Sjmmv 	}
1120ec933656Sjmmv 
1121ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
1122ec933656Sjmmv 
1123ec933656Sjmmv 	startoff = uio->uio_offset;
1124ec933656Sjmmv 
11258e6209cfSyamt 	cnt = 0;
11268e6209cfSyamt 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
1127ec933656Sjmmv 		error = tmpfs_dir_getdotdent(node, uio);
1128ec933656Sjmmv 		if (error == -1) {
1129ec933656Sjmmv 			error = 0;
1130ec933656Sjmmv 			goto outok;
1131ec933656Sjmmv 		} else if (error != 0)
1132ec933656Sjmmv 			goto outok;
11338e6209cfSyamt 		cnt++;
1134ec933656Sjmmv 	}
1135ec933656Sjmmv 
11368e6209cfSyamt 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1137ec933656Sjmmv 		error = tmpfs_dir_getdotdotdent(node, uio);
1138ec933656Sjmmv 		if (error == -1) {
1139ec933656Sjmmv 			error = 0;
1140ec933656Sjmmv 			goto outok;
1141ec933656Sjmmv 		} else if (error != 0)
1142ec933656Sjmmv 			goto outok;
11438e6209cfSyamt 		cnt++;
1144ec933656Sjmmv 	}
1145ec933656Sjmmv 
11468e6209cfSyamt 	error = tmpfs_dir_getdents(node, uio, &cnt);
1147ec933656Sjmmv 	if (error == -1)
1148ec933656Sjmmv 		error = 0;
1149ec933656Sjmmv 	KASSERT(error >= 0);
1150ec933656Sjmmv 
1151ec933656Sjmmv outok:
11528e6209cfSyamt 	/* This label assumes that startoff has been
1153ec933656Sjmmv 	 * initialized.  If the compiler didn't spit out warnings, we'd
1154ec933656Sjmmv 	 * simply make this one be 'out' and drop 'outok'. */
1155ec933656Sjmmv 
1156ec933656Sjmmv 	if (eofflag != NULL)
1157ec933656Sjmmv 		*eofflag =
11588e6209cfSyamt 		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1159ec933656Sjmmv 
1160ec933656Sjmmv 	/* Update NFS-related variables. */
1161ec933656Sjmmv 	if (error == 0 && cookies != NULL && ncookies != NULL) {
1162ec933656Sjmmv 		off_t i;
11638e6209cfSyamt 		off_t off = startoff;
11648e6209cfSyamt 		struct tmpfs_dirent *de = NULL;
1165ec933656Sjmmv 
11668e6209cfSyamt 		*ncookies = cnt;
11678e6209cfSyamt 		*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
1168ec933656Sjmmv 
11698e6209cfSyamt 		for (i = 0; i < cnt; i++) {
11708e6209cfSyamt 			KASSERT(off != TMPFS_DIRCOOKIE_EOF);
11718e6209cfSyamt 			if (off == TMPFS_DIRCOOKIE_DOT) {
11728e6209cfSyamt 				off = TMPFS_DIRCOOKIE_DOTDOT;
11738e6209cfSyamt 			} else {
11748e6209cfSyamt 				if (off == TMPFS_DIRCOOKIE_DOTDOT) {
1175064fbe7eSjmmv 					de = TAILQ_FIRST(&node->tn_spec.
1176064fbe7eSjmmv 					    tn_dir.tn_dir);
11778e6209cfSyamt 				} else if (de != NULL) {
11788e6209cfSyamt 					de = TAILQ_NEXT(de, td_entries);
11798e6209cfSyamt 				} else {
11808e6209cfSyamt 					de = tmpfs_dir_lookupbycookie(node,
11818e6209cfSyamt 					    off);
11828e6209cfSyamt 					KASSERT(de != NULL);
11838e6209cfSyamt 					de = TAILQ_NEXT(de, td_entries);
11848e6209cfSyamt 				}
11858e6209cfSyamt 				if (de == NULL) {
11868e6209cfSyamt 					off = TMPFS_DIRCOOKIE_EOF;
11878e6209cfSyamt 				} else {
1188b2603104Sjmmv 					off = tmpfs_dircookie(de);
11898e6209cfSyamt 				}
11908e6209cfSyamt 			}
11918e6209cfSyamt 
11928e6209cfSyamt 			(*cookies)[i] = off;
11938e6209cfSyamt 		}
11948e6209cfSyamt 		KASSERT(uio->uio_offset == off);
1195ec933656Sjmmv 	}
1196ec933656Sjmmv 
1197ec933656Sjmmv out:
1198ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1199ec933656Sjmmv 
1200ec933656Sjmmv 	return error;
1201ec933656Sjmmv }
1202ec933656Sjmmv 
1203ec933656Sjmmv /* --------------------------------------------------------------------- */
1204ec933656Sjmmv 
1205ec933656Sjmmv int
1206ec933656Sjmmv tmpfs_readlink(void *v)
1207ec933656Sjmmv {
1208ec933656Sjmmv 	struct vnode *vp = ((struct vop_readlink_args *)v)->a_vp;
1209ec933656Sjmmv 	struct uio *uio = ((struct vop_readlink_args *)v)->a_uio;
1210ec933656Sjmmv 
1211ec933656Sjmmv 	int error;
1212ec933656Sjmmv 	struct tmpfs_node *node;
1213ec933656Sjmmv 
1214ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1215ec933656Sjmmv 	KASSERT(uio->uio_offset == 0);
1216ec933656Sjmmv 	KASSERT(vp->v_type == VLNK);
1217ec933656Sjmmv 
1218ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1219ec933656Sjmmv 
1220064fbe7eSjmmv 	error = uiomove(node->tn_spec.tn_lnk.tn_link,
1221064fbe7eSjmmv 	    MIN(node->tn_size, uio->uio_resid), uio);
1222ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED;
1223ec933656Sjmmv 
1224ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1225ec933656Sjmmv 
1226ec933656Sjmmv 	return error;
1227ec933656Sjmmv }
1228ec933656Sjmmv 
1229ec933656Sjmmv /* --------------------------------------------------------------------- */
1230ec933656Sjmmv 
1231ec933656Sjmmv int
1232ec933656Sjmmv tmpfs_inactive(void *v)
1233ec933656Sjmmv {
1234ec933656Sjmmv 	struct vnode *vp = ((struct vop_inactive_args *)v)->a_vp;
123595e1ffb1Schristos 	struct lwp *l = ((struct vop_inactive_args *)v)->a_l;
1236ec933656Sjmmv 
1237ec933656Sjmmv 	struct tmpfs_node *node;
1238ec933656Sjmmv 
1239ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1240ec933656Sjmmv 
1241ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1242ec933656Sjmmv 
1243ec933656Sjmmv 	VOP_UNLOCK(vp, 0);
1244ec933656Sjmmv 
1245ec933656Sjmmv 	if (node->tn_links == 0)
124695e1ffb1Schristos 		vrecycle(vp, NULL, l);
1247ec933656Sjmmv 
1248ec933656Sjmmv 	return 0;
1249ec933656Sjmmv }
1250ec933656Sjmmv 
1251ec933656Sjmmv /* --------------------------------------------------------------------- */
1252ec933656Sjmmv 
1253ec933656Sjmmv int
1254ec933656Sjmmv tmpfs_reclaim(void *v)
1255ec933656Sjmmv {
1256ec933656Sjmmv 	struct vnode *vp = ((struct vop_reclaim_args *)v)->a_vp;
1257ec933656Sjmmv 
1258ec933656Sjmmv 	struct tmpfs_mount *tmp;
1259ec933656Sjmmv 	struct tmpfs_node *node;
1260ec933656Sjmmv 
1261ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
1262ec933656Sjmmv 
1263ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1264ec933656Sjmmv 	tmp = VFS_TO_TMPFS(vp->v_mount);
1265ec933656Sjmmv 
1266ec933656Sjmmv 	cache_purge(vp);
1267ec933656Sjmmv 	tmpfs_free_vp(vp);
1268ec933656Sjmmv 
1269ec933656Sjmmv 	/* If the node referenced by this vnode was deleted by the user,
1270ec933656Sjmmv 	 * we must free its associated data structures (now that the vnode
1271ec933656Sjmmv 	 * is being reclaimed). */
1272ec933656Sjmmv 	if (node->tn_links == 0)
1273ec933656Sjmmv 		tmpfs_free_node(tmp, node);
1274ec933656Sjmmv 
1275ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
1276ec933656Sjmmv 	KASSERT(vp->v_data == NULL);
1277ec933656Sjmmv 
1278ec933656Sjmmv 	return 0;
1279ec933656Sjmmv }
1280ec933656Sjmmv 
1281ec933656Sjmmv /* --------------------------------------------------------------------- */
1282ec933656Sjmmv 
1283ec933656Sjmmv int
1284ec933656Sjmmv tmpfs_print(void *v)
1285ec933656Sjmmv {
1286ec933656Sjmmv 	struct vnode *vp = ((struct vop_print_args *)v)->a_vp;
1287ec933656Sjmmv 
1288ec933656Sjmmv 	struct tmpfs_node *node;
1289ec933656Sjmmv 
1290ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1291ec933656Sjmmv 
1292ec933656Sjmmv 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
1293ec933656Sjmmv 	    node, node->tn_flags, node->tn_links);
1294ec933656Sjmmv 	printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX
1295ec933656Sjmmv 	    ", status 0x%x\n",
1296ec933656Sjmmv 	    node->tn_mode, node->tn_uid, node->tn_gid,
1297ec933656Sjmmv 	    (uintmax_t)node->tn_size, node->tn_status);
1298ec933656Sjmmv 
1299ec933656Sjmmv 	if (vp->v_type == VFIFO)
1300ec933656Sjmmv 		fifo_printinfo(vp);
1301ec933656Sjmmv 	lockmgr_printinfo(&vp->v_lock);
1302ec933656Sjmmv 
1303ec933656Sjmmv 	printf("\n");
1304ec933656Sjmmv 
1305ec933656Sjmmv 	return 0;
1306ec933656Sjmmv }
1307ec933656Sjmmv 
1308ec933656Sjmmv /* --------------------------------------------------------------------- */
1309ec933656Sjmmv 
1310ec933656Sjmmv int
1311ec933656Sjmmv tmpfs_pathconf(void *v)
1312ec933656Sjmmv {
1313ec933656Sjmmv 	int name = ((struct vop_pathconf_args *)v)->a_name;
1314ec933656Sjmmv 	register_t *retval = ((struct vop_pathconf_args *)v)->a_retval;
1315ec933656Sjmmv 
1316ec933656Sjmmv 	int error;
1317ec933656Sjmmv 
1318ec933656Sjmmv 	error = 0;
1319ec933656Sjmmv 
1320ec933656Sjmmv 	switch (name) {
1321ec933656Sjmmv 	case _PC_LINK_MAX:
1322ec933656Sjmmv 		*retval = LINK_MAX;
1323ec933656Sjmmv 		break;
1324ec933656Sjmmv 
1325ec933656Sjmmv 	case _PC_NAME_MAX:
1326ec933656Sjmmv 		*retval = NAME_MAX;
1327ec933656Sjmmv 		break;
1328ec933656Sjmmv 
1329ec933656Sjmmv 	case _PC_PATH_MAX:
1330ec933656Sjmmv 		*retval = PATH_MAX;
1331ec933656Sjmmv 		break;
1332ec933656Sjmmv 
1333ec933656Sjmmv 	case _PC_PIPE_BUF:
1334ec933656Sjmmv 		*retval = PIPE_BUF;
1335ec933656Sjmmv 		break;
1336ec933656Sjmmv 
1337ec933656Sjmmv 	case _PC_CHOWN_RESTRICTED:
1338ec933656Sjmmv 		*retval = 1;
1339ec933656Sjmmv 		break;
1340ec933656Sjmmv 
1341ec933656Sjmmv 	case _PC_NO_TRUNC:
1342ec933656Sjmmv 		*retval = 1;
1343ec933656Sjmmv 		break;
1344ec933656Sjmmv 
1345ec933656Sjmmv 	case _PC_SYNC_IO:
1346ec933656Sjmmv 		*retval = 1;
1347ec933656Sjmmv 		break;
1348ec933656Sjmmv 
1349ec933656Sjmmv 	case _PC_FILESIZEBITS:
1350ec933656Sjmmv 		*retval = 0; /* XXX Don't know which value should I return. */
1351ec933656Sjmmv 		break;
1352ec933656Sjmmv 
1353ec933656Sjmmv 	default:
1354ec933656Sjmmv 		error = EINVAL;
1355ec933656Sjmmv 	}
1356ec933656Sjmmv 
1357ec933656Sjmmv 	return error;
1358ec933656Sjmmv }
1359ec933656Sjmmv 
1360ec933656Sjmmv /* --------------------------------------------------------------------- */
1361ec933656Sjmmv 
1362ec933656Sjmmv int
1363b6d141c7Sjmmv tmpfs_advlock(void *v)
1364b6d141c7Sjmmv {
1365b6d141c7Sjmmv 	struct vnode *vp = ((struct vop_advlock_args *)v)->a_vp;
1366b6d141c7Sjmmv 
1367b6d141c7Sjmmv 	struct tmpfs_node *node;
1368b6d141c7Sjmmv 
1369b6d141c7Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1370b6d141c7Sjmmv 
1371b6d141c7Sjmmv 	return lf_advlock(v, &node->tn_lockf, node->tn_size);
1372b6d141c7Sjmmv }
1373b6d141c7Sjmmv 
1374b6d141c7Sjmmv /* --------------------------------------------------------------------- */
1375b6d141c7Sjmmv 
1376b6d141c7Sjmmv int
1377ec933656Sjmmv tmpfs_getpages(void *v)
1378ec933656Sjmmv {
13795f4b660eSjmmv 	struct vnode *vp = ((struct vop_getpages_args *)v)->a_vp;
13805f4b660eSjmmv 	voff_t offset = ((struct vop_getpages_args *)v)->a_offset;
13815f4b660eSjmmv 	struct vm_page **m = ((struct vop_getpages_args *)v)->a_m;
13825f4b660eSjmmv 	int *count = ((struct vop_getpages_args *)v)->a_count;
13835f4b660eSjmmv 	int centeridx = ((struct vop_getpages_args *)v)->a_centeridx;
13845f4b660eSjmmv 	vm_prot_t access_type = ((struct vop_getpages_args *)v)->a_access_type;
13855f4b660eSjmmv 	int advice = ((struct vop_getpages_args *)v)->a_advice;
13865f4b660eSjmmv 	int flags = ((struct vop_getpages_args *)v)->a_flags;
13875f4b660eSjmmv 
1388ec933656Sjmmv 	int error;
138951634dfdSjmmv 	int i;
13905f4b660eSjmmv 	struct tmpfs_node *node;
13915f4b660eSjmmv 	struct uvm_object *uobj;
139220bb9654Syamt 	int npages = *count;
1393ec933656Sjmmv 
1394647aa775Syamt 	KASSERT(vp->v_type == VREG);
1395647aa775Syamt 	LOCK_ASSERT(simple_lock_held(&vp->v_interlock));
1396ec933656Sjmmv 
13975f4b660eSjmmv 	node = VP_TO_TMPFS_NODE(vp);
1398064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
13995f4b660eSjmmv 
140020bb9654Syamt 	/* We currently don't rely on PGO_PASTEOF. */
140120bb9654Syamt 
140220bb9654Syamt 	if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
140320bb9654Syamt 		if ((flags & PGO_LOCKED) == 0)
140420bb9654Syamt 			simple_unlock(&vp->v_interlock);
140520bb9654Syamt 		return EINVAL;
140620bb9654Syamt 	}
140720bb9654Syamt 
140820bb9654Syamt 	if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
140920bb9654Syamt 		npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
141020bb9654Syamt 	}
141120bb9654Syamt 
14125f4b660eSjmmv 	if ((flags & PGO_LOCKED) != 0)
1413647aa775Syamt 		return EBUSY;
1414ec933656Sjmmv 
1415647aa775Syamt 	if ((flags & PGO_NOTIMESTAMP) == 0) {
14165f4b660eSjmmv 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1417ec933656Sjmmv 			node->tn_status |= TMPFS_NODE_ACCESSED;
14185f4b660eSjmmv 
14195f4b660eSjmmv 		if ((access_type & VM_PROT_WRITE) != 0)
1420647aa775Syamt 			node->tn_status |= TMPFS_NODE_MODIFIED;
1421647aa775Syamt 	}
1422ec933656Sjmmv 
1423647aa775Syamt 	simple_unlock(&vp->v_interlock);
1424647aa775Syamt 
142551634dfdSjmmv 	/*
142651634dfdSjmmv 	 * Make sure that the array on which we will store the
142751634dfdSjmmv 	 * gotten pages is clean.  Otherwise uao_get (pointed to by
142851634dfdSjmmv 	 * the pgo_get below) gets confused and does not return the
142951634dfdSjmmv 	 * appropriate pages.
143051634dfdSjmmv 	 *
143151634dfdSjmmv 	 * XXX This shall be revisited when kern/32166 is addressed
143251634dfdSjmmv 	 * because the loop to clean m[i] will most likely be redundant
143351634dfdSjmmv 	 * as well as the PGO_ALLPAGES flag.
143451634dfdSjmmv 	 */
143551634dfdSjmmv 	if (m != NULL)
143651634dfdSjmmv 		for (i = 0; i < npages; i++)
143751634dfdSjmmv 			m[i] = NULL;
1438647aa775Syamt 	simple_lock(&uobj->vmobjlock);
143920bb9654Syamt 	error = (*uobj->pgops->pgo_get)(uobj, offset, m, &npages, centeridx,
144051634dfdSjmmv 	    access_type, advice, flags | PGO_ALLPAGES);
144151634dfdSjmmv #if defined(DEBUG)
144251634dfdSjmmv 	{
144351634dfdSjmmv 		/* Make sure that all the pages we return are valid. */
144451634dfdSjmmv 		int dbgi;
144551634dfdSjmmv 		if (error == 0 && m != NULL)
144651634dfdSjmmv 			for (dbgi = 0; dbgi < npages; dbgi++)
144751634dfdSjmmv 				KASSERT(m[dbgi] != NULL);
144851634dfdSjmmv 	}
144951634dfdSjmmv #endif
1450647aa775Syamt 
1451647aa775Syamt 	return error;
1452647aa775Syamt }
1453647aa775Syamt 
1454647aa775Syamt /* --------------------------------------------------------------------- */
1455647aa775Syamt 
1456647aa775Syamt int
1457647aa775Syamt tmpfs_putpages(void *v)
1458647aa775Syamt {
14595f4b660eSjmmv 	struct vnode *vp = ((struct vop_putpages_args *)v)->a_vp;
14605f4b660eSjmmv 	voff_t offlo = ((struct vop_putpages_args *)v)->a_offlo;
14615f4b660eSjmmv 	voff_t offhi = ((struct vop_putpages_args *)v)->a_offhi;
14625f4b660eSjmmv 	int flags = ((struct vop_putpages_args *)v)->a_flags;
14635f4b660eSjmmv 
1464647aa775Syamt 	int error;
14655f4b660eSjmmv 	struct tmpfs_node *node;
14665f4b660eSjmmv 	struct uvm_object *uobj;
1467647aa775Syamt 
1468647aa775Syamt 	LOCK_ASSERT(simple_lock_held(&vp->v_interlock));
1469647aa775Syamt 
14705f4b660eSjmmv 	node = VP_TO_TMPFS_NODE(vp);
14715f4b660eSjmmv 
1472647aa775Syamt 	if (vp->v_type != VREG) {
1473647aa775Syamt 		simple_unlock(&vp->v_interlock);
1474647aa775Syamt 		return 0;
1475647aa775Syamt 	}
1476647aa775Syamt 
1477064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
1478647aa775Syamt 	simple_unlock(&vp->v_interlock);
1479647aa775Syamt 
1480647aa775Syamt 	simple_lock(&uobj->vmobjlock);
14815f4b660eSjmmv 	error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags);
1482647aa775Syamt 
1483647aa775Syamt 	/* XXX mtime */
1484647aa775Syamt 
1485ec933656Sjmmv 	return error;
1486ec933656Sjmmv }
1487