xref: /netbsd-src/sys/fs/tmpfs/tmpfs_vnops.c (revision f63b58d19d13c6b3d4e16962af8c2bf66e1760ae)
1*f63b58d1Sjmmv /*	$NetBSD: tmpfs_vnops.c,v 1.11 2005/09/23 14:27:55 jmmv Exp $	*/
2ec933656Sjmmv 
3ec933656Sjmmv /*
4ec933656Sjmmv  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5ec933656Sjmmv  * All rights reserved.
6ec933656Sjmmv  *
7ec933656Sjmmv  * This code is derived from software contributed to The NetBSD Foundation
8ec933656Sjmmv  * by Julio M. Merino Vidal.
9ec933656Sjmmv  *
10ec933656Sjmmv  * Redistribution and use in source and binary forms, with or without
11ec933656Sjmmv  * modification, are permitted provided that the following conditions
12ec933656Sjmmv  * are met:
13ec933656Sjmmv  * 1. Redistributions of source code must retain the above copyright
14ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer.
15ec933656Sjmmv  * 2. Redistributions in binary form must reproduce the above copyright
16ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer in the
17ec933656Sjmmv  *    documentation and/or other materials provided with the distribution.
18ec933656Sjmmv  * 3. All advertising materials mentioning features or use of this software
19ec933656Sjmmv  *    must display the following acknowledgement:
20ec933656Sjmmv  *        This product includes software developed by the NetBSD
21ec933656Sjmmv  *        Foundation, Inc. and its contributors.
22ec933656Sjmmv  * 4. Neither the name of The NetBSD Foundation nor the names of its
23ec933656Sjmmv  *    contributors may be used to endorse or promote products derived
24ec933656Sjmmv  *    from this software without specific prior written permission.
25ec933656Sjmmv  *
26ec933656Sjmmv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27ec933656Sjmmv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28ec933656Sjmmv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29ec933656Sjmmv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30ec933656Sjmmv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31ec933656Sjmmv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32ec933656Sjmmv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33ec933656Sjmmv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34ec933656Sjmmv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35ec933656Sjmmv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36ec933656Sjmmv  * POSSIBILITY OF SUCH DAMAGE.
37ec933656Sjmmv  */
38ec933656Sjmmv 
39ec933656Sjmmv /*
40ec933656Sjmmv  * tmpfs vnode interface.
41ec933656Sjmmv  */
42ec933656Sjmmv 
43ec933656Sjmmv #include <sys/cdefs.h>
44*f63b58d1Sjmmv __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.11 2005/09/23 14:27:55 jmmv Exp $");
45ec933656Sjmmv 
46ec933656Sjmmv #include <sys/param.h>
47ec933656Sjmmv #include <sys/dirent.h>
48ec933656Sjmmv #include <sys/fcntl.h>
49ec933656Sjmmv #include <sys/event.h>
50ec933656Sjmmv #include <sys/malloc.h>
51ec933656Sjmmv #include <sys/namei.h>
52ec933656Sjmmv #include <sys/proc.h>
53ec933656Sjmmv #include <sys/stat.h>
54ec933656Sjmmv #include <sys/uio.h>
55ec933656Sjmmv #include <sys/unistd.h>
56ec933656Sjmmv #include <sys/vnode.h>
57ec933656Sjmmv 
58ec933656Sjmmv #include <uvm/uvm.h>
59ec933656Sjmmv 
60ec933656Sjmmv #include <miscfs/fifofs/fifo.h>
61ec933656Sjmmv #include <fs/tmpfs/tmpfs_vnops.h>
62ec933656Sjmmv #include <fs/tmpfs/tmpfs.h>
63ec933656Sjmmv 
64ec933656Sjmmv /* --------------------------------------------------------------------- */
65ec933656Sjmmv 
66ec933656Sjmmv /*
678e0a777aSjmmv  * vnode operations vector used for files stored in a tmpfs file system.
68ec933656Sjmmv  */
69ec933656Sjmmv int (**tmpfs_vnodeop_p)(void *);
70ec933656Sjmmv const struct vnodeopv_entry_desc tmpfs_vnodeop_entries[] = {
71ec933656Sjmmv 	{ &vop_default_desc,		vn_default_error },
72ec933656Sjmmv 	{ &vop_lookup_desc,		tmpfs_lookup },
73ec933656Sjmmv 	{ &vop_create_desc,		tmpfs_create },
74ec933656Sjmmv 	{ &vop_mknod_desc,		tmpfs_mknod },
75ec933656Sjmmv 	{ &vop_open_desc,		tmpfs_open },
76ec933656Sjmmv 	{ &vop_close_desc,		tmpfs_close },
77ec933656Sjmmv 	{ &vop_access_desc,		tmpfs_access },
78ec933656Sjmmv 	{ &vop_getattr_desc,		tmpfs_getattr },
79ec933656Sjmmv 	{ &vop_setattr_desc,		tmpfs_setattr },
80ec933656Sjmmv 	{ &vop_read_desc,		tmpfs_read },
81ec933656Sjmmv 	{ &vop_write_desc,		tmpfs_write },
82ec933656Sjmmv 	{ &vop_ioctl_desc,		tmpfs_ioctl },
83ec933656Sjmmv 	{ &vop_fcntl_desc,		tmpfs_fcntl },
84ec933656Sjmmv 	{ &vop_poll_desc,		tmpfs_poll },
85ec933656Sjmmv 	{ &vop_kqfilter_desc,		tmpfs_kqfilter },
86ec933656Sjmmv 	{ &vop_revoke_desc,		tmpfs_revoke },
87ec933656Sjmmv 	{ &vop_mmap_desc,		tmpfs_mmap },
88ec933656Sjmmv 	{ &vop_fsync_desc,		tmpfs_fsync },
89ec933656Sjmmv 	{ &vop_seek_desc,		tmpfs_seek },
90ec933656Sjmmv 	{ &vop_remove_desc,		tmpfs_remove },
91ec933656Sjmmv 	{ &vop_link_desc,		tmpfs_link },
92ec933656Sjmmv 	{ &vop_rename_desc,		tmpfs_rename },
93ec933656Sjmmv 	{ &vop_mkdir_desc,		tmpfs_mkdir },
94ec933656Sjmmv 	{ &vop_rmdir_desc,		tmpfs_rmdir },
95ec933656Sjmmv 	{ &vop_symlink_desc,		tmpfs_symlink },
96ec933656Sjmmv 	{ &vop_readdir_desc,		tmpfs_readdir },
97ec933656Sjmmv 	{ &vop_readlink_desc,		tmpfs_readlink },
98ec933656Sjmmv 	{ &vop_abortop_desc,		tmpfs_abortop },
99ec933656Sjmmv 	{ &vop_inactive_desc,		tmpfs_inactive },
100ec933656Sjmmv 	{ &vop_reclaim_desc,		tmpfs_reclaim },
101ec933656Sjmmv 	{ &vop_lock_desc,		tmpfs_lock },
102ec933656Sjmmv 	{ &vop_unlock_desc,		tmpfs_unlock },
103ec933656Sjmmv 	{ &vop_bmap_desc,		tmpfs_bmap },
104ec933656Sjmmv 	{ &vop_strategy_desc,		tmpfs_strategy },
105ec933656Sjmmv 	{ &vop_print_desc,		tmpfs_print },
106ec933656Sjmmv 	{ &vop_pathconf_desc,		tmpfs_pathconf },
107ec933656Sjmmv 	{ &vop_islocked_desc,		tmpfs_islocked },
108ec933656Sjmmv 	{ &vop_advlock_desc,		tmpfs_advlock },
109ec933656Sjmmv 	{ &vop_blkatoff_desc,		tmpfs_blkatoff },
110ec933656Sjmmv 	{ &vop_valloc_desc,		tmpfs_valloc },
111ec933656Sjmmv 	{ &vop_reallocblks_desc,	tmpfs_reallocblks },
112ec933656Sjmmv 	{ &vop_vfree_desc,		tmpfs_vfree },
113ec933656Sjmmv 	{ &vop_truncate_desc,		tmpfs_truncate },
114ec933656Sjmmv 	{ &vop_update_desc,		tmpfs_update },
115ec933656Sjmmv 	{ &vop_lease_desc,		tmpfs_lease },
116ec933656Sjmmv 	{ &vop_bwrite_desc,		tmpfs_bwrite },
117ec933656Sjmmv 	{ &vop_getpages_desc,		tmpfs_getpages },
118ec933656Sjmmv 	{ &vop_putpages_desc,		tmpfs_putpages },
119ec933656Sjmmv 	{ NULL, NULL }
120ec933656Sjmmv };
121ec933656Sjmmv const struct vnodeopv_desc tmpfs_vnodeop_opv_desc =
122ec933656Sjmmv 	{ &tmpfs_vnodeop_p, tmpfs_vnodeop_entries };
123ec933656Sjmmv 
124ec933656Sjmmv /* --------------------------------------------------------------------- */
125ec933656Sjmmv 
126ec933656Sjmmv int
127ec933656Sjmmv tmpfs_lookup(void *v)
128ec933656Sjmmv {
129ec933656Sjmmv 	struct vnode *dvp = ((struct vop_lookup_args *)v)->a_dvp;
130ec933656Sjmmv 	struct vnode **vpp = ((struct vop_lookup_args *)v)->a_vpp;
131ec933656Sjmmv 	struct componentname *cnp = ((struct vop_lookup_args *)v)->a_cnp;
132ec933656Sjmmv 
133ec933656Sjmmv 	int error;
134ec933656Sjmmv 	struct tmpfs_dirent *de;
135ec933656Sjmmv 	struct tmpfs_node *dnode;
136ec933656Sjmmv 
137ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
138ec933656Sjmmv 
139ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
140ec933656Sjmmv 	*vpp = NULL;
141ec933656Sjmmv 
142ec933656Sjmmv 	/* Check accessibility of requested node as a first step. */
143ec933656Sjmmv 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_proc);
144ec933656Sjmmv 	if (error != 0)
145ec933656Sjmmv 		goto out;
146ec933656Sjmmv 
1478e0a777aSjmmv 	/* If requesting the last path component on a read-only file system
148ec933656Sjmmv 	 * with a write operation, deny it. */
149ec933656Sjmmv 	if ((cnp->cn_flags & ISLASTCN) &&
150ec933656Sjmmv 	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
151ec933656Sjmmv 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
152ec933656Sjmmv 		error = EROFS;
153ec933656Sjmmv 		goto out;
154ec933656Sjmmv 	}
155ec933656Sjmmv 
156ec933656Sjmmv 	/* Avoid doing a linear scan of the directory if the requested
157ec933656Sjmmv 	 * directory/name couple is already in the cache. */
158ec933656Sjmmv 	error = cache_lookup(dvp, vpp, cnp);
159ec933656Sjmmv 	if (error >= 0)
160ec933656Sjmmv 		goto out;
161ec933656Sjmmv 
162ec933656Sjmmv 	/* We cannot be requesting the parent directory of the root node. */
163ec933656Sjmmv 	KASSERT(IMPLIES(dnode->tn_type == VDIR &&
164ec933656Sjmmv 	    dnode->tn_parent == dnode, !(cnp->cn_flags & ISDOTDOT)));
165ec933656Sjmmv 
166ec933656Sjmmv 	if (cnp->cn_flags & ISDOTDOT) {
167ec933656Sjmmv 		VOP_UNLOCK(dvp, 0);
168ec933656Sjmmv 
169ec933656Sjmmv 		/* Allocate a new vnode on the matching entry. */
170ec933656Sjmmv 		error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_parent, vpp);
171ec933656Sjmmv 
172ec933656Sjmmv 		if (cnp->cn_flags & LOCKPARENT &&
173ec933656Sjmmv 		    cnp->cn_flags & ISLASTCN) {
174ec933656Sjmmv 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
175ec933656Sjmmv 				cnp->cn_flags |= PDIRUNLOCK;
176ec933656Sjmmv 		}
177ec933656Sjmmv 		dnode->tn_parent->tn_lookup_dirent = NULL;
178ec933656Sjmmv 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
179ec933656Sjmmv 		VREF(dvp);
180ec933656Sjmmv 		*vpp = dvp;
181ec933656Sjmmv 		dnode->tn_lookup_dirent = NULL;
182ec933656Sjmmv 		error = 0;
183ec933656Sjmmv 	} else {
184ec933656Sjmmv 		de = tmpfs_dir_lookup(dnode, cnp);
185ec933656Sjmmv 		if (de == NULL) {
186ec933656Sjmmv 			/* The entry was not found in the directory.
187ec933656Sjmmv 			 * This is OK iff we are creating or renaming an
188ec933656Sjmmv 			 * entry and are working on the last component of
189ec933656Sjmmv 			 * the path name. */
190ec933656Sjmmv 			if ((cnp->cn_flags & ISLASTCN) &&
191ec933656Sjmmv 			    (cnp->cn_nameiop == CREATE || \
192ec933656Sjmmv 			    cnp->cn_nameiop == RENAME)) {
193ec933656Sjmmv 				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
194ec933656Sjmmv 				    cnp->cn_proc);
195ec933656Sjmmv 				if (error != 0)
196ec933656Sjmmv 					goto out;
197ec933656Sjmmv 
198ec933656Sjmmv 				/* Keep the component name in the buffer for
199ec933656Sjmmv 				 * future uses. */
200ec933656Sjmmv 				cnp->cn_flags |= SAVENAME;
201ec933656Sjmmv 
202ec933656Sjmmv 				error = EJUSTRETURN;
203ec933656Sjmmv 			} else
204ec933656Sjmmv 				error = ENOENT;
205ec933656Sjmmv 		} else {
206ec933656Sjmmv 			struct tmpfs_node *tnode;
207ec933656Sjmmv 
208ec933656Sjmmv 			/* The entry was found, so get its associated
209ec933656Sjmmv 			 * tmpfs_node. */
210ec933656Sjmmv 			tnode = de->td_node;
211ec933656Sjmmv 
212ec933656Sjmmv 			/* If we are not at the last path component and
213ec933656Sjmmv 			 * found a non-directory entry, raise an error. */
214ec933656Sjmmv 			if ((tnode->tn_type != VDIR) &&
215ec933656Sjmmv 			    !(cnp->cn_flags & ISLASTCN)) {
216ec933656Sjmmv 				error = ENOTDIR;
217ec933656Sjmmv 				goto out;
218ec933656Sjmmv 			}
219ec933656Sjmmv 
220ec933656Sjmmv 			/* If we are deleting or renaming the entry, keep
221ec933656Sjmmv 			 * track of its tmpfs_dirent so that it can be
222ec933656Sjmmv 			 * easily deleted later. */
223ec933656Sjmmv 			if ((cnp->cn_flags & ISLASTCN) &&
224ec933656Sjmmv 			    (cnp->cn_nameiop == DELETE ||
225ec933656Sjmmv 			    cnp->cn_nameiop == RENAME)) {
226ec933656Sjmmv 				error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
227ec933656Sjmmv 				    cnp->cn_proc);
228ec933656Sjmmv 				if (error != 0)
229ec933656Sjmmv 					goto out;
230ec933656Sjmmv 				/* TODO: Check sticky bit. */
231ec933656Sjmmv 				tnode->tn_lookup_dirent = de;
232ec933656Sjmmv 			}
233ec933656Sjmmv 
234ec933656Sjmmv 			/* Allocate a new vnode on the matching entry. */
235ec933656Sjmmv 			error = tmpfs_alloc_vp(dvp->v_mount, tnode, vpp);
236ec933656Sjmmv 
237ec933656Sjmmv 			if (error == 0 && (!(cnp->cn_flags & LOCKPARENT) ||
238ec933656Sjmmv 			    !(cnp->cn_flags & ISLASTCN)))
239ec933656Sjmmv 				VOP_UNLOCK(dvp, 0);
240ec933656Sjmmv 		}
241ec933656Sjmmv 	}
242ec933656Sjmmv 
243ec933656Sjmmv 	/* Store the result of this lookup in the cache.  Avoid this if the
244ec933656Sjmmv 	 * request was for creation, as it does not improve timings on
245ec933656Sjmmv 	 * emprical tests. */
246ec933656Sjmmv 	if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
247ec933656Sjmmv 		cache_enter(dvp, *vpp, cnp);
248ec933656Sjmmv 
249ec933656Sjmmv out:
250ec933656Sjmmv 	/* If there were no errors, *vpp cannot be null and it must be
251ec933656Sjmmv 	 * locked. */
252ec933656Sjmmv 	KASSERT(IFF(error == 0, *vpp != NULL && VOP_ISLOCKED(*vpp)));
253ec933656Sjmmv 
254ec933656Sjmmv 	/* dvp has to be locked if:
255ec933656Sjmmv 	 * - There were errors and relocking of dvp did not fail.
256ec933656Sjmmv 	 * - We are doing a '..' lookup, relocking of dvp did not fail
257ec933656Sjmmv 	 *   (PDIRUNLOCK is unset) and LOCKPARENT or ISLASTCN are not set.
258ec933656Sjmmv 	 * - LOCKPARENT and ISLASTCN are set. */
259ec933656Sjmmv 	KASSERT(IMPLIES(
260ec933656Sjmmv 	    (error != 0 && !(cnp->cn_flags & PDIRUNLOCK)) ||
261ec933656Sjmmv 	    (cnp->cn_flags & ISDOTDOT && !(cnp->cn_flags & PDIRUNLOCK) &&
262ec933656Sjmmv 	     ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))) ||
263ec933656Sjmmv 	    (cnp->cn_flags & LOCKPARENT && cnp->cn_flags & ISLASTCN)
264ec933656Sjmmv 	    ,
265ec933656Sjmmv 	    VOP_ISLOCKED(dvp)));
266ec933656Sjmmv 
267ec933656Sjmmv 	return error;
268ec933656Sjmmv }
269ec933656Sjmmv 
270ec933656Sjmmv /* --------------------------------------------------------------------- */
271ec933656Sjmmv 
272ec933656Sjmmv int
273ec933656Sjmmv tmpfs_create(void *v)
274ec933656Sjmmv {
275ec933656Sjmmv 	struct vnode *dvp = ((struct vop_create_args *)v)->a_dvp;
276ec933656Sjmmv 	struct vnode **vpp = ((struct vop_create_args *)v)->a_vpp;
277ec933656Sjmmv 	struct componentname *cnp = ((struct vop_create_args *)v)->a_cnp;
278ec933656Sjmmv 	struct vattr *vap = ((struct vop_create_args *)v)->a_vap;
279ec933656Sjmmv 
280ec933656Sjmmv 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
281ec933656Sjmmv 
282ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
283ec933656Sjmmv }
284ec933656Sjmmv /* --------------------------------------------------------------------- */
285ec933656Sjmmv 
286ec933656Sjmmv int
287ec933656Sjmmv tmpfs_mknod(void *v)
288ec933656Sjmmv {
289ec933656Sjmmv 	struct vnode *dvp = ((struct vop_mknod_args *)v)->a_dvp;
290ec933656Sjmmv 	struct vnode **vpp = ((struct vop_mknod_args *)v)->a_vpp;
291ec933656Sjmmv 	struct componentname *cnp = ((struct vop_mknod_args *)v)->a_cnp;
292ec933656Sjmmv 	struct vattr *vap = ((struct vop_mknod_args *)v)->a_vap;
293ec933656Sjmmv 
294ec933656Sjmmv 	if (vap->va_type != VBLK && vap->va_type != VCHR &&
295ec933656Sjmmv 	    vap->va_type != VFIFO)
296ec933656Sjmmv 		return EINVAL;
297ec933656Sjmmv 
298ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
299ec933656Sjmmv }
300ec933656Sjmmv 
301ec933656Sjmmv /* --------------------------------------------------------------------- */
302ec933656Sjmmv 
303ec933656Sjmmv int
304ec933656Sjmmv tmpfs_open(void *v)
305ec933656Sjmmv {
306ec933656Sjmmv 	struct vnode *vp = ((struct vop_open_args *)v)->a_vp;
307ec933656Sjmmv 	int mode = ((struct vop_open_args *)v)->a_mode;
308ec933656Sjmmv 
309ec933656Sjmmv 	int error;
310ec933656Sjmmv 	struct tmpfs_node *node;
311ec933656Sjmmv 
312ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
313ec933656Sjmmv 
314ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
315ec933656Sjmmv 	KASSERT(node->tn_links > 0);
316ec933656Sjmmv 
317ec933656Sjmmv 	/* If the file is marked append-only, deny write requests. */
318ec933656Sjmmv 	if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
319ec933656Sjmmv 		error = EPERM;
320ec933656Sjmmv 	else
321ec933656Sjmmv 		error = 0;
322ec933656Sjmmv 
323ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
324ec933656Sjmmv 
325ec933656Sjmmv 	return error;
326ec933656Sjmmv }
327ec933656Sjmmv 
328ec933656Sjmmv /* --------------------------------------------------------------------- */
329ec933656Sjmmv 
330ec933656Sjmmv int
331ec933656Sjmmv tmpfs_close(void *v)
332ec933656Sjmmv {
333ec933656Sjmmv 	struct vnode *vp = ((struct vop_close_args *)v)->a_vp;
334ec933656Sjmmv 
335ec933656Sjmmv 	int error;
336ec933656Sjmmv 	struct tmpfs_node *node;
337ec933656Sjmmv 
338ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
339ec933656Sjmmv 
340ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
341ec933656Sjmmv 
342ec933656Sjmmv 	if (node->tn_links > 0) {
343ec933656Sjmmv 		/* Update node times.  No need to do it if the node has
344ec933656Sjmmv 		 * been deleted, because it will vanish after we return. */
345ec933656Sjmmv 		error = VOP_UPDATE(vp, NULL, NULL, UPDATE_CLOSE);
346ec933656Sjmmv 	} else
347ec933656Sjmmv 		error = 0;
348ec933656Sjmmv 
349ec933656Sjmmv 	return error;
350ec933656Sjmmv }
351ec933656Sjmmv 
352ec933656Sjmmv /* --------------------------------------------------------------------- */
353ec933656Sjmmv 
354ec933656Sjmmv int
355ec933656Sjmmv tmpfs_access(void *v)
356ec933656Sjmmv {
357ec933656Sjmmv 	struct vnode *vp = ((struct vop_access_args *)v)->a_vp;
358ec933656Sjmmv 	int mode = ((struct vop_access_args *)v)->a_mode;
359ec933656Sjmmv 	struct ucred *cred = ((struct vop_access_args *)v)->a_cred;
360ec933656Sjmmv 
361ec933656Sjmmv 	int error;
362ec933656Sjmmv 	struct tmpfs_node *node;
363ec933656Sjmmv 
364ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
365ec933656Sjmmv 
366ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
367ec933656Sjmmv 
368ec933656Sjmmv 	switch (vp->v_type) {
369ec933656Sjmmv 	case VDIR:
370ec933656Sjmmv 		/* FALLTHROUGH */
371ec933656Sjmmv 	case VLNK:
372ec933656Sjmmv 		/* FALLTHROUGH */
373ec933656Sjmmv 	case VREG:
374ec933656Sjmmv 		if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
375ec933656Sjmmv 			error = EROFS;
376ec933656Sjmmv 			goto out;
377ec933656Sjmmv 		}
378ec933656Sjmmv 		break;
379ec933656Sjmmv 
380ec933656Sjmmv 	case VBLK:
381ec933656Sjmmv 		/* FALLTHROUGH */
382ec933656Sjmmv 	case VCHR:
383ec933656Sjmmv 		/* FALLTHROUGH */
384ec933656Sjmmv 	case VSOCK:
385ec933656Sjmmv 		/* FALLTHROUGH */
386ec933656Sjmmv 	case VFIFO:
387ec933656Sjmmv 		break;
388ec933656Sjmmv 
389ec933656Sjmmv 	default:
390ec933656Sjmmv 		error = EINVAL;
391ec933656Sjmmv 		goto out;
392ec933656Sjmmv 	}
393ec933656Sjmmv 
394ec933656Sjmmv 	if (mode & VWRITE && node->tn_flags & IMMUTABLE) {
395ec933656Sjmmv 		error = EPERM;
396ec933656Sjmmv 		goto out;
397ec933656Sjmmv 	}
398ec933656Sjmmv 
399ec933656Sjmmv 	error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
400ec933656Sjmmv 	    node->tn_gid, mode, cred);
401ec933656Sjmmv 
402ec933656Sjmmv out:
403ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
404ec933656Sjmmv 
405ec933656Sjmmv 	return error;
406ec933656Sjmmv }
407ec933656Sjmmv 
408ec933656Sjmmv /* --------------------------------------------------------------------- */
409ec933656Sjmmv 
410ec933656Sjmmv int
411ec933656Sjmmv tmpfs_getattr(void *v)
412ec933656Sjmmv {
413ec933656Sjmmv 	struct vnode *vp = ((struct vop_getattr_args *)v)->a_vp;
414ec933656Sjmmv 	struct vattr *vap = ((struct vop_getattr_args *)v)->a_vap;
415ec933656Sjmmv 
416ec933656Sjmmv 	struct tmpfs_node *node;
417ec933656Sjmmv 
418ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
419ec933656Sjmmv 
420ec933656Sjmmv 	VATTR_NULL(vap);
421ec933656Sjmmv 
422ec933656Sjmmv 	vap->va_type = vp->v_type;
423ec933656Sjmmv 	vap->va_mode = node->tn_mode;
424ec933656Sjmmv 	vap->va_nlink = node->tn_links;
425ec933656Sjmmv 	vap->va_uid = node->tn_uid;
426ec933656Sjmmv 	vap->va_gid = node->tn_gid;
427ec933656Sjmmv 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
428ec933656Sjmmv 	vap->va_fileid = node->tn_id;
429ec933656Sjmmv 	vap->va_size = node->tn_size;
430ec933656Sjmmv 	vap->va_blocksize = PAGE_SIZE;
431ec933656Sjmmv 	vap->va_atime = node->tn_atime;
432ec933656Sjmmv 	vap->va_mtime = node->tn_mtime;
433ec933656Sjmmv 	vap->va_ctime = node->tn_ctime;
434ec933656Sjmmv 	vap->va_birthtime = node->tn_birthtime;
435ec933656Sjmmv 	vap->va_gen = node->tn_gen;
436ec933656Sjmmv 	vap->va_flags = node->tn_flags;
437ec933656Sjmmv 	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
438ec933656Sjmmv 		node->tn_rdev : VNOVAL;
439ec933656Sjmmv 	vap->va_bytes = round_page(node->tn_size);
440ec933656Sjmmv 	vap->va_filerev = VNOVAL;
441ec933656Sjmmv 	vap->va_vaflags = 0;
442ec933656Sjmmv 	vap->va_spare = VNOVAL; /* XXX */
443ec933656Sjmmv 
444ec933656Sjmmv 	return 0;
445ec933656Sjmmv }
446ec933656Sjmmv 
447ec933656Sjmmv /* --------------------------------------------------------------------- */
448ec933656Sjmmv 
449ec933656Sjmmv /* XXX Should this operation be atomic?  I think it should, but code in
450ec933656Sjmmv  * XXX other places (e.g., ufs) doesn't seem to be... */
451ec933656Sjmmv int
452ec933656Sjmmv tmpfs_setattr(void *v)
453ec933656Sjmmv {
454ec933656Sjmmv 	struct vnode *vp = ((struct vop_setattr_args *)v)->a_vp;
455ec933656Sjmmv 	struct vattr *vap = ((struct vop_setattr_args *)v)->a_vap;
456ec933656Sjmmv 	struct ucred *cred = ((struct vop_setattr_args *)v)->a_cred;
457ec933656Sjmmv 	struct proc *p = ((struct vop_setattr_args *)v)->a_p;
458ec933656Sjmmv 
459ec933656Sjmmv 	int error, error2;
460ec933656Sjmmv 
461ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
462ec933656Sjmmv 
463ec933656Sjmmv 	error = 0;
464ec933656Sjmmv 
465ec933656Sjmmv 	/* Abort if any unsettable attribute is given. */
466ec933656Sjmmv 	if (vap->va_type != VNON ||
467ec933656Sjmmv 	    vap->va_nlink != VNOVAL ||
468ec933656Sjmmv 	    vap->va_fsid != VNOVAL ||
469ec933656Sjmmv 	    vap->va_fileid != VNOVAL ||
470ec933656Sjmmv 	    vap->va_blocksize != VNOVAL ||
471ec933656Sjmmv 	    vap->va_ctime.tv_sec != VNOVAL ||
472ec933656Sjmmv 	    vap->va_ctime.tv_nsec != VNOVAL ||
473ec933656Sjmmv 	    vap->va_birthtime.tv_sec != VNOVAL ||
474ec933656Sjmmv 	    vap->va_birthtime.tv_nsec != VNOVAL ||
475ec933656Sjmmv 	    vap->va_gen != VNOVAL ||
476ec933656Sjmmv 	    vap->va_rdev != VNOVAL ||
477ec933656Sjmmv 	    vap->va_bytes != VNOVAL)
478ec933656Sjmmv 		error = EINVAL;
479ec933656Sjmmv 
480ec933656Sjmmv 	if (error == 0 && (vap->va_flags != VNOVAL))
481ec933656Sjmmv 		error = tmpfs_chflags(vp, vap->va_flags, cred, p);
482ec933656Sjmmv 
483ec933656Sjmmv 	if (error == 0 && (vap->va_size != VNOVAL))
484ec933656Sjmmv 		error = tmpfs_chsize(vp, vap->va_size, cred, p);
485ec933656Sjmmv 
486ec933656Sjmmv 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
487ec933656Sjmmv 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
488ec933656Sjmmv 
489ec933656Sjmmv 	if (error == 0 && (vap->va_mode != VNOVAL))
490ec933656Sjmmv 		error = tmpfs_chmod(vp, vap->va_mode, cred, p);
491ec933656Sjmmv 
492ec933656Sjmmv 	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
493ec933656Sjmmv 	    vap->va_atime.tv_nsec != VNOVAL) ||
494ec933656Sjmmv 	    (vap->va_mtime.tv_sec != VNOVAL &&
495ec933656Sjmmv 	    vap->va_mtime.tv_nsec != VNOVAL)))
496ec933656Sjmmv 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
497ec933656Sjmmv 		    vap->va_vaflags, cred, p);
498ec933656Sjmmv 
499ec933656Sjmmv 	/* Update the node times.  We give preference to the error codes
500ec933656Sjmmv 	 * generated by this function rather than the ones that may arise
501ec933656Sjmmv 	 * from tmpfs_update. */
502ec933656Sjmmv 	error2 = VOP_UPDATE(vp, NULL, NULL, 0);
503ec933656Sjmmv 	if (error == 0)
504ec933656Sjmmv 		error = error2;
505ec933656Sjmmv 
506ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
507ec933656Sjmmv 
508ec933656Sjmmv 	return error;
509ec933656Sjmmv }
510ec933656Sjmmv 
511ec933656Sjmmv /* --------------------------------------------------------------------- */
512ec933656Sjmmv 
513ec933656Sjmmv int
514ec933656Sjmmv tmpfs_read(void *v)
515ec933656Sjmmv {
516ec933656Sjmmv 	struct vnode *vp = ((struct vop_read_args *)v)->a_vp;
517ec933656Sjmmv 	struct uio *uio = ((struct vop_read_args *)v)->a_uio;
518ec933656Sjmmv 
5195f4b660eSjmmv 	int error;
5205f4b660eSjmmv 	int flags;
521ec933656Sjmmv 	struct tmpfs_node *node;
522647aa775Syamt 	struct uvm_object *uobj;
523ec933656Sjmmv 
524ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
525ec933656Sjmmv 
526ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
527ec933656Sjmmv 
528a7ca1cc6Syamt 	if (vp->v_type != VREG) {
529a7ca1cc6Syamt 		error = EISDIR;
530a7ca1cc6Syamt 		goto out;
531a7ca1cc6Syamt 	}
532a7ca1cc6Syamt 
533a7ca1cc6Syamt 	if (uio->uio_offset < 0) {
534ec933656Sjmmv 		error = EINVAL;
535ec933656Sjmmv 		goto out;
536ec933656Sjmmv 	}
537ec933656Sjmmv 
538ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED;
539ec933656Sjmmv 
540647aa775Syamt 	uobj = node->tn_aobj;
541647aa775Syamt 	flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;
542ec933656Sjmmv 	error = 0;
5435f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
544647aa775Syamt 		vsize_t len;
545647aa775Syamt 		void *win;
546ec933656Sjmmv 
5477720dda1Syamt 		if (node->tn_size <= uio->uio_offset)
5487720dda1Syamt 			break;
5497720dda1Syamt 
550ec933656Sjmmv 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
5515f4b660eSjmmv 		if (len == 0)
552647aa775Syamt 			break;
5535f4b660eSjmmv 
554647aa775Syamt 		win = ubc_alloc(uobj, uio->uio_offset, &len, UBC_READ);
555647aa775Syamt 		error = uiomove(win, len, uio);
556647aa775Syamt 		ubc_release(win, flags);
557647aa775Syamt 	}
558ec933656Sjmmv 
559ec933656Sjmmv out:
560ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
561ec933656Sjmmv 
562ec933656Sjmmv 	return error;
563ec933656Sjmmv }
564ec933656Sjmmv 
565ec933656Sjmmv /* --------------------------------------------------------------------- */
566ec933656Sjmmv 
567ec933656Sjmmv int
568ec933656Sjmmv tmpfs_write(void *v)
569ec933656Sjmmv {
570ec933656Sjmmv 	struct vnode *vp = ((struct vop_write_args *)v)->a_vp;
571ec933656Sjmmv 	struct uio *uio = ((struct vop_write_args *)v)->a_uio;
572ec933656Sjmmv 	int ioflag = ((struct vop_write_args *)v)->a_ioflag;
573ec933656Sjmmv 
574ec933656Sjmmv 	boolean_t extended;
575ec933656Sjmmv 	int error;
5765f4b660eSjmmv 	int flags;
577ec933656Sjmmv 	off_t oldsize;
578ec933656Sjmmv 	struct tmpfs_node *node;
579647aa775Syamt 	struct uvm_object *uobj;
580ec933656Sjmmv 
581ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
582ec933656Sjmmv 
583ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
584ec933656Sjmmv 	oldsize = node->tn_size;
585ec933656Sjmmv 
586ec933656Sjmmv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
587ec933656Sjmmv 		error = EINVAL;
588ec933656Sjmmv 		goto out;
589ec933656Sjmmv 	}
590ec933656Sjmmv 
591ec933656Sjmmv 	if (uio->uio_resid == 0) {
592ec933656Sjmmv 		error = 0;
593ec933656Sjmmv 		goto out;
594ec933656Sjmmv 	}
595ec933656Sjmmv 
596ec933656Sjmmv 	if (ioflag & IO_APPEND)
597ec933656Sjmmv 		uio->uio_offset = node->tn_size;
598ec933656Sjmmv 
599ec933656Sjmmv 	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
600ec933656Sjmmv 	if (extended) {
601ec933656Sjmmv 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
602ec933656Sjmmv 		if (error != 0)
603ec933656Sjmmv 			goto out;
604ec933656Sjmmv 	}
605ec933656Sjmmv 
606647aa775Syamt 	uobj = node->tn_aobj;
607647aa775Syamt 	flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;
608647aa775Syamt 	error = 0;
6095f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
610647aa775Syamt 		vsize_t len;
611647aa775Syamt 		void *win;
612647aa775Syamt 
613647aa775Syamt 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
6145f4b660eSjmmv 		if (len == 0)
615647aa775Syamt 			break;
6165f4b660eSjmmv 
617647aa775Syamt 		win = ubc_alloc(uobj, uio->uio_offset, &len, UBC_WRITE);
618647aa775Syamt 		error = uiomove(win, len, uio);
619647aa775Syamt 		ubc_release(win, flags);
620647aa775Syamt 	}
621647aa775Syamt 
622ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
623ec933656Sjmmv 	    (extended ? TMPFS_NODE_CHANGED : 0);
624ec933656Sjmmv 
6255f4b660eSjmmv 	if (error != 0)
626647aa775Syamt 		(void)tmpfs_reg_resize(vp, oldsize);
627647aa775Syamt 
628ec933656Sjmmv out:
629ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
630ec933656Sjmmv 	KASSERT(IMPLIES(error == 0, uio->uio_resid == 0));
631ec933656Sjmmv 	KASSERT(IMPLIES(error != 0, oldsize == node->tn_size));
632ec933656Sjmmv 
633ec933656Sjmmv 	return error;
634ec933656Sjmmv }
635ec933656Sjmmv 
636ec933656Sjmmv /* --------------------------------------------------------------------- */
637ec933656Sjmmv 
638ec933656Sjmmv int
639ec933656Sjmmv tmpfs_fsync(void *v)
640ec933656Sjmmv {
641ec933656Sjmmv 	struct vnode *vp = ((struct vop_fsync_args *)v)->a_vp;
642ec933656Sjmmv 
643ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
644ec933656Sjmmv 
645ec933656Sjmmv 	return VOP_UPDATE(vp, NULL, NULL, 0);
646ec933656Sjmmv }
647ec933656Sjmmv 
648ec933656Sjmmv /* --------------------------------------------------------------------- */
649ec933656Sjmmv 
650ec933656Sjmmv int
651ec933656Sjmmv tmpfs_remove(void *v)
652ec933656Sjmmv {
653ec933656Sjmmv 	struct vnode *dvp = ((struct vop_remove_args *)v)->a_dvp;
654ec933656Sjmmv 	struct vnode *vp = ((struct vop_remove_args *)v)->a_vp;
655ec933656Sjmmv 
656ec933656Sjmmv 	int error;
657ec933656Sjmmv 	struct tmpfs_dirent *de;
658ec933656Sjmmv 	struct tmpfs_mount *tmp;
659ec933656Sjmmv 	struct tmpfs_node *dnode;
660ec933656Sjmmv 	struct tmpfs_node *node;
661ec933656Sjmmv 
662ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
663ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
664ec933656Sjmmv 
665ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
666ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
667ec933656Sjmmv 	tmp = VFS_TO_TMPFS(vp->v_mount);
668ec933656Sjmmv 	de = node->tn_lookup_dirent;
669ec933656Sjmmv 	KASSERT(de != NULL);
670ec933656Sjmmv 
671ec933656Sjmmv 	/* XXX: Why isn't this done by the caller? */
672ec933656Sjmmv 	if (vp->v_type == VDIR) {
673ec933656Sjmmv 		error = EISDIR;
674ec933656Sjmmv 		goto out;
675ec933656Sjmmv 	}
676ec933656Sjmmv 
677ec933656Sjmmv 	/* Files marked as immutable or append-only cannot be deleted. */
678ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
679ec933656Sjmmv 		error = EPERM;
680ec933656Sjmmv 		goto out;
681ec933656Sjmmv 	}
682ec933656Sjmmv 
683ec933656Sjmmv 	/* Remove the entry from the directory; as it is a file, we do not
684ec933656Sjmmv 	 * have to change the number of hard links of the directory. */
685ec933656Sjmmv 	tmpfs_dir_detach(dvp, de);
686ec933656Sjmmv 
687ec933656Sjmmv 	/* Notify interested parties about the modification of dvp.
688ec933656Sjmmv 	 * The removal of vp is notified when it is reclaimed. */
689ec933656Sjmmv 	VN_KNOTE(dvp, NOTE_WRITE);
690ec933656Sjmmv 
691ec933656Sjmmv 	/* Free the directory entry we just deleted.  Note that the node
692ec933656Sjmmv 	 * referred by it will not be removed until the vnode is really
693ec933656Sjmmv 	 * reclaimed. */
694ec933656Sjmmv 	tmpfs_free_dirent(tmp, de, TRUE);
695ec933656Sjmmv 
696ec933656Sjmmv 	error = 0;
697ec933656Sjmmv 
698ec933656Sjmmv out:
699ec933656Sjmmv 	vput(dvp);
700ec933656Sjmmv 	vput(vp);
701ec933656Sjmmv 
702ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(dvp));
703ec933656Sjmmv 
704ec933656Sjmmv 	return error;
705ec933656Sjmmv }
706ec933656Sjmmv 
707ec933656Sjmmv /* --------------------------------------------------------------------- */
708ec933656Sjmmv 
709ec933656Sjmmv int
710ec933656Sjmmv tmpfs_link(void *v)
711ec933656Sjmmv {
712ec933656Sjmmv 	struct vnode *dvp = ((struct vop_link_args *)v)->a_dvp;
713ec933656Sjmmv 	struct vnode *vp = ((struct vop_link_args *)v)->a_vp;
714ec933656Sjmmv 	struct componentname *cnp = ((struct vop_link_args *)v)->a_cnp;
715ec933656Sjmmv 
716ec933656Sjmmv 	int error;
717ec933656Sjmmv 	struct tmpfs_dirent *de;
718ec933656Sjmmv 	struct tmpfs_node *dnode;
719ec933656Sjmmv 	struct tmpfs_node *node;
720ec933656Sjmmv 
721ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
722ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
723ec933656Sjmmv 	KASSERT(cnp->cn_flags & HASBUF);
724ec933656Sjmmv 	KASSERT(dvp != vp); /* XXX When can this be false? */
725ec933656Sjmmv 
726ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
727ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
728ec933656Sjmmv 
729ec933656Sjmmv 	/* Lock vp because we will need to run VOP_UPDATE over it, which
730ec933656Sjmmv 	 * needs the vnode to be locked. */
731ec933656Sjmmv 	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
732ec933656Sjmmv 	if (error != 0)
733ec933656Sjmmv 		goto out;
734ec933656Sjmmv 
735ec933656Sjmmv 	/* XXX: Why aren't the following two tests done by the caller? */
736ec933656Sjmmv 
737ec933656Sjmmv 	/* Hard links of directories are forbidden. */
738ec933656Sjmmv 	if (vp->v_type == VDIR) {
739ec933656Sjmmv 		error = EPERM;
740ec933656Sjmmv 		goto out;
741ec933656Sjmmv 	}
742ec933656Sjmmv 
743ec933656Sjmmv 	/* Cannot create cross-device links. */
744ec933656Sjmmv 	if (dvp->v_mount != vp->v_mount) {
745ec933656Sjmmv 		error = EXDEV;
746ec933656Sjmmv 		goto out;
747ec933656Sjmmv 	}
748ec933656Sjmmv 
749ec933656Sjmmv 	/* Ensure that we do not overflow the maximum number of links imposed
750ec933656Sjmmv 	 * by the system. */
751ec933656Sjmmv 	KASSERT(node->tn_links <= LINK_MAX);
752ec933656Sjmmv 	if (node->tn_links == LINK_MAX) {
753ec933656Sjmmv 		error = EMLINK;
754ec933656Sjmmv 		goto out;
755ec933656Sjmmv 	}
756ec933656Sjmmv 
757ec933656Sjmmv 	/* We cannot create links of files marked immutable or append-only. */
758ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
759ec933656Sjmmv 		error = EPERM;
760ec933656Sjmmv 		goto out;
761ec933656Sjmmv 	}
762ec933656Sjmmv 
763ec933656Sjmmv 	/* Allocate a new directory entry to represent the node. */
764ec933656Sjmmv 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
765ec933656Sjmmv 	    cnp->cn_nameptr, cnp->cn_namelen, &de);
766ec933656Sjmmv 	if (error != 0)
767ec933656Sjmmv 		goto out;
768ec933656Sjmmv 
769ec933656Sjmmv 	/* Insert the new directory entry into the appropriate directory. */
770ec933656Sjmmv 	tmpfs_dir_attach(dvp, de);
771ec933656Sjmmv 	VN_KNOTE(dvp, NOTE_WRITE);
772ec933656Sjmmv 
773ec933656Sjmmv 	/* vp link count has changed, so update node times. */
774ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_CHANGED;
775ec933656Sjmmv 	(void)VOP_UPDATE(vp, NULL, NULL, 0);
776ec933656Sjmmv 
777ec933656Sjmmv 	error = 0;
778ec933656Sjmmv 
779ec933656Sjmmv out:
780ec933656Sjmmv 	if (VOP_ISLOCKED(vp))
781ec933656Sjmmv 		VOP_UNLOCK(vp, 0);
782ec933656Sjmmv 
783ec933656Sjmmv 	PNBUF_PUT(cnp->cn_pnbuf);
784ec933656Sjmmv 
785ec933656Sjmmv 	vput(dvp);
786ec933656Sjmmv 
787ec933656Sjmmv 	/* XXX Locking status of dvp does not match manual page. */
788ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(dvp));
789ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
790ec933656Sjmmv 
791ec933656Sjmmv 	return error;
792ec933656Sjmmv }
793ec933656Sjmmv 
794ec933656Sjmmv /* --------------------------------------------------------------------- */
795ec933656Sjmmv 
796ec933656Sjmmv int
797ec933656Sjmmv tmpfs_rename(void *v)
798ec933656Sjmmv {
799ec933656Sjmmv 	struct vnode *fdvp = ((struct vop_rename_args *)v)->a_fdvp;
800ec933656Sjmmv 	struct vnode *fvp = ((struct vop_rename_args *)v)->a_fvp;
801ec933656Sjmmv 	struct componentname *fcnp = ((struct vop_rename_args *)v)->a_fcnp;
802ec933656Sjmmv 	struct vnode *tdvp = ((struct vop_rename_args *)v)->a_tdvp;
803ec933656Sjmmv 	struct vnode *tvp = ((struct vop_rename_args *)v)->a_tvp;
804ec933656Sjmmv 	struct componentname *tcnp = ((struct vop_rename_args *)v)->a_tcnp;
805ec933656Sjmmv 
806ec933656Sjmmv 	char *newname;
807ec933656Sjmmv 	int error;
808ec933656Sjmmv 	struct tmpfs_dirent *de;
809ec933656Sjmmv 	struct tmpfs_mount *tmp;
810ec933656Sjmmv 	struct tmpfs_node *fdnode;
811ec933656Sjmmv 	struct tmpfs_node *fnode;
812ec933656Sjmmv 	struct tmpfs_node *tdnode;
813ec933656Sjmmv 
814ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(tdvp));
815ec933656Sjmmv 	KASSERT(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
816ec933656Sjmmv 	KASSERT(fcnp->cn_flags & HASBUF);
817ec933656Sjmmv 	KASSERT(tcnp->cn_flags & HASBUF);
818ec933656Sjmmv 
819ec933656Sjmmv 	fdnode = VP_TO_TMPFS_DIR(fdvp);
820ec933656Sjmmv 	fnode = VP_TO_TMPFS_NODE(fvp);
821ec933656Sjmmv 	de = fnode->tn_lookup_dirent;
822ec933656Sjmmv 
823ec933656Sjmmv 	/* Disallow cross-device renames.
824ec933656Sjmmv 	 * XXX Why isn't this done by the caller? */
825ec933656Sjmmv 	if (fvp->v_mount != tdvp->v_mount ||
826ec933656Sjmmv 	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
827ec933656Sjmmv 		error = EXDEV;
828ec933656Sjmmv 		goto out;
829ec933656Sjmmv 	}
830ec933656Sjmmv 
831ec933656Sjmmv 	tmp = VFS_TO_TMPFS(tdvp->v_mount);
832ec933656Sjmmv 	tdnode = VP_TO_TMPFS_DIR(tdvp);
833ec933656Sjmmv 
834ec933656Sjmmv 	/* If source and target are the same file, there is nothing to do. */
835ec933656Sjmmv 	if (fvp == tvp) {
836ec933656Sjmmv 		error = 0;
837ec933656Sjmmv 		goto out;
838ec933656Sjmmv 	}
839ec933656Sjmmv 
840ec933656Sjmmv 	/* Avoid manipulating '.' and '..' entries. */
841ec933656Sjmmv 	if (de == NULL) {
842ec933656Sjmmv 		KASSERT(fvp->v_type == VDIR);
843ec933656Sjmmv 		error = EINVAL;
844ec933656Sjmmv 		goto out;
845ec933656Sjmmv 	}
846ec933656Sjmmv 	KASSERT(de->td_node == fnode);
847ec933656Sjmmv 
848*f63b58d1Sjmmv 	/* If we need to move the directory between entries, lock the
849*f63b58d1Sjmmv 	 * source so that we can safely operate on it. */
850*f63b58d1Sjmmv 	if (fdnode != tdnode) {
851*f63b58d1Sjmmv 		error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
852*f63b58d1Sjmmv 		if (error != 0)
853*f63b58d1Sjmmv 			goto out_locked;
854*f63b58d1Sjmmv 	}
855*f63b58d1Sjmmv 
856ec933656Sjmmv 	/* Ensure that we have enough memory to hold the new name, if it
857ec933656Sjmmv 	 * has to be changed. */
858ec933656Sjmmv 	if (fcnp->cn_namelen != tcnp->cn_namelen ||
859ec933656Sjmmv 	    memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
860ec933656Sjmmv 		newname = tmpfs_str_pool_get(&tmp->tm_str_pool,
861ec933656Sjmmv 		    tcnp->cn_namelen, 0);
862ec933656Sjmmv 		if (newname == NULL) {
863ec933656Sjmmv 			error = ENOSPC;
864ec933656Sjmmv 			goto out;
865ec933656Sjmmv 		}
866ec933656Sjmmv 	} else
867ec933656Sjmmv 		newname = NULL;
868ec933656Sjmmv 
869ec933656Sjmmv 	/* If the node is being moved to another directory, we have to do
870ec933656Sjmmv 	 * the move. */
871ec933656Sjmmv 	if (fdnode != tdnode) {
872ec933656Sjmmv 		/* In case we are moving a directory, we have to adjust its
873ec933656Sjmmv 		 * parent to point to the new parent. */
874ec933656Sjmmv 		if (de->td_node->tn_type == VDIR) {
875ec933656Sjmmv 			struct tmpfs_node *n;
876ec933656Sjmmv 
877ec933656Sjmmv 			/* Ensure the target directory is not a child of the
878ec933656Sjmmv 			 * directory being moved.  Otherwise, we'd end up
879ec933656Sjmmv 			 * with stale nodes. */
880ec933656Sjmmv 			n = tdnode;
881ec933656Sjmmv 			while (n != n->tn_parent) {
882ec933656Sjmmv 				if (n == fnode) {
883ec933656Sjmmv 					error = EINVAL;
884ec933656Sjmmv 					goto out;
885ec933656Sjmmv 				}
886ec933656Sjmmv 				n = n->tn_parent;
887ec933656Sjmmv 			}
888ec933656Sjmmv 
889ec933656Sjmmv 			/* Adjust the parent pointer. */
890ec933656Sjmmv 			TMPFS_VALIDATE_DIR(fnode);
891ec933656Sjmmv 			de->td_node->tn_parent = tdnode;
892ec933656Sjmmv 
893ec933656Sjmmv 			/* As a result of changing the target of the '..'
894ec933656Sjmmv 			 * entry, the link count of the source and target
895ec933656Sjmmv 			 * directories has to be adjusted. */
896ec933656Sjmmv 			fdnode->tn_links--;
897ec933656Sjmmv 			tdnode->tn_links++;
898ec933656Sjmmv 		}
899ec933656Sjmmv 
900ec933656Sjmmv 		/* Do the move: just remove the entry from the source directory
901ec933656Sjmmv 		 * and insert it into the target one. */
902ec933656Sjmmv 		tmpfs_dir_detach(fdvp, de);
903ec933656Sjmmv 		tmpfs_dir_attach(tdvp, de);
904ec933656Sjmmv 
905ec933656Sjmmv 		/* Notify listeners of fdvp about the change in the directory.
906ec933656Sjmmv 		 * We can do it at this point because we aren't touching fdvp
907ec933656Sjmmv 		 * any more below. */
908ec933656Sjmmv 		VN_KNOTE(fdvp, NOTE_WRITE);
909ec933656Sjmmv 	}
910ec933656Sjmmv 
911ec933656Sjmmv 	/* If the name has changed, we need to make it effective by changing
912ec933656Sjmmv 	 * it in the directory entry. */
913ec933656Sjmmv 	if (newname != NULL) {
914ec933656Sjmmv 		KASSERT(tcnp->cn_namelen < MAXNAMLEN);
915ec933656Sjmmv 		KASSERT(tcnp->cn_namelen < 0xffff);
916ec933656Sjmmv 
917ec933656Sjmmv 		tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name,
918ec933656Sjmmv 		    de->td_namelen);
919ec933656Sjmmv 		de->td_namelen = (uint16_t)tcnp->cn_namelen;
920ec933656Sjmmv 		memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
921ec933656Sjmmv 		de->td_name = newname;
922ec933656Sjmmv 
923ec933656Sjmmv 		fnode->tn_status |= TMPFS_NODE_MODIFIED;
924ec933656Sjmmv 	}
925ec933656Sjmmv 
926ec933656Sjmmv 	/* If we are overwriting an entry, we have to remove the old one
927ec933656Sjmmv 	 * from the target directory. */
928ec933656Sjmmv 	if (tvp != NULL) {
929ec933656Sjmmv 		struct tmpfs_node *tnode;
930ec933656Sjmmv 
931ec933656Sjmmv 		tnode = VP_TO_TMPFS_NODE(tvp);
932ec933656Sjmmv 
933ec933656Sjmmv 		/* The source node cannot be a directory in this case. */
934ec933656Sjmmv 		KASSERT(fnode->tn_type != VDIR);
935ec933656Sjmmv 
936ec933656Sjmmv 		/* Remove the old entry from the target directory. */
937ec933656Sjmmv 		de = tnode->tn_lookup_dirent;
938ec933656Sjmmv 		tmpfs_dir_detach(tdvp, de);
939ec933656Sjmmv 
940ec933656Sjmmv 		/* Free the directory entry we just deleted.  Note that the
941ec933656Sjmmv 		 * node referred by it will not be removed until the vnode is
942ec933656Sjmmv 		 * really reclaimed. */
943ec933656Sjmmv 		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE);
944ec933656Sjmmv 	}
945ec933656Sjmmv 
946ec933656Sjmmv 	/* Notify listeners of tdvp about the change in the directory (either
947ec933656Sjmmv 	 * because a new entry was added or because one was removed). */
948ec933656Sjmmv 	VN_KNOTE(tdvp, NOTE_WRITE);
949ec933656Sjmmv 
950ec933656Sjmmv 	error = 0;
951ec933656Sjmmv 
952*f63b58d1Sjmmv out_locked:
953*f63b58d1Sjmmv 	if (fdnode != tdnode)
954*f63b58d1Sjmmv 		VOP_UNLOCK(fdvp, 0);
955*f63b58d1Sjmmv 
956ec933656Sjmmv out:
957ec933656Sjmmv 	/* Release target nodes. */
958ec933656Sjmmv 	/* XXX: I don't understand when tdvp can be the same as tvp, but
959ec933656Sjmmv 	 * other code takes care of this... */
960ec933656Sjmmv 	if (tdvp == tvp)
961ec933656Sjmmv 		vrele(tdvp);
962ec933656Sjmmv 	else
963ec933656Sjmmv 		vput(tdvp);
964ec933656Sjmmv 	if (tvp != NULL)
965ec933656Sjmmv 		vput(tvp);
966ec933656Sjmmv 
967ec933656Sjmmv 	/* Release source nodes. */
968ec933656Sjmmv 	vrele(fdvp);
969ec933656Sjmmv 	vrele(fvp);
970ec933656Sjmmv 
971ec933656Sjmmv 	return error;
972ec933656Sjmmv }
973ec933656Sjmmv 
974ec933656Sjmmv /* --------------------------------------------------------------------- */
975ec933656Sjmmv 
976ec933656Sjmmv int
977ec933656Sjmmv tmpfs_mkdir(void *v)
978ec933656Sjmmv {
979ec933656Sjmmv 	struct vnode *dvp = ((struct vop_mkdir_args *)v)->a_dvp;
980ec933656Sjmmv 	struct vnode **vpp = ((struct vop_mkdir_args *)v)->a_vpp;
981ec933656Sjmmv 	struct componentname *cnp = ((struct vop_mkdir_args *)v)->a_cnp;
982ec933656Sjmmv 	struct vattr *vap = ((struct vop_mkdir_args *)v)->a_vap;
983ec933656Sjmmv 
984ec933656Sjmmv 	KASSERT(vap->va_type == VDIR);
985ec933656Sjmmv 
986ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
987ec933656Sjmmv }
988ec933656Sjmmv 
989ec933656Sjmmv /* --------------------------------------------------------------------- */
990ec933656Sjmmv 
991ec933656Sjmmv int
992ec933656Sjmmv tmpfs_rmdir(void *v)
993ec933656Sjmmv {
994ec933656Sjmmv 	struct vnode *dvp = ((struct vop_rmdir_args *)v)->a_dvp;
995ec933656Sjmmv 	struct vnode *vp = ((struct vop_rmdir_args *)v)->a_vp;
996ec933656Sjmmv 
997ec933656Sjmmv 	int error;
998ec933656Sjmmv 	struct tmpfs_dirent *de;
999ec933656Sjmmv 	struct tmpfs_mount *tmp;
1000ec933656Sjmmv 	struct tmpfs_node *dnode;
1001ec933656Sjmmv 	struct tmpfs_node *node;
1002ec933656Sjmmv 
1003ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
1004ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1005ec933656Sjmmv 
1006ec933656Sjmmv 	tmp = VFS_TO_TMPFS(dvp->v_mount);
1007ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
1008ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
1009ec933656Sjmmv 	KASSERT(node->tn_parent == dnode);
1010ec933656Sjmmv 
1011ec933656Sjmmv 	/* Get the directory entry associated with node (vp).  This was
1012ec933656Sjmmv 	 * filled by tmpfs_lookup while looking up the entry. */
1013ec933656Sjmmv 	de = node->tn_lookup_dirent;
1014ec933656Sjmmv 	KASSERT(TMPFS_DIRENT_MATCHES(de,
1015ec933656Sjmmv 	    ((struct vop_rmdir_args *)v)->a_cnp->cn_nameptr,
1016ec933656Sjmmv 	    ((struct vop_rmdir_args *)v)->a_cnp->cn_namelen));
1017ec933656Sjmmv 
1018ec933656Sjmmv 	/* Directories with more than two entries ('.' and '..') cannot be
1019ec933656Sjmmv 	 * removed. */
1020ec933656Sjmmv 	if (node->tn_size > 0) {
1021ec933656Sjmmv 		error = ENOTEMPTY;
1022ec933656Sjmmv 		goto out;
1023ec933656Sjmmv 	}
1024ec933656Sjmmv 
1025ec933656Sjmmv 	/* Check flags to see if we are allowed to remove the directory. */
1026ec933656Sjmmv 	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
1027ec933656Sjmmv 		error = EPERM;
1028ec933656Sjmmv 		goto out;
1029ec933656Sjmmv 	}
1030ec933656Sjmmv 
1031ec933656Sjmmv 	/* Detach the directory entry from the directory (dnode). */
1032ec933656Sjmmv 	tmpfs_dir_detach(dvp, de);
1033ec933656Sjmmv 
1034ec933656Sjmmv 	node->tn_links--;
1035ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
1036ec933656Sjmmv 	    TMPFS_NODE_MODIFIED;
1037ec933656Sjmmv 	node->tn_parent->tn_links--;
1038ec933656Sjmmv 	node->tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \
1039ec933656Sjmmv 	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1040ec933656Sjmmv 
1041ec933656Sjmmv 	/* Notify modification of parent directory and release it. */
1042ec933656Sjmmv 	VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1043ec933656Sjmmv 	cache_purge(dvp); /* XXX Is this needed? */
1044ec933656Sjmmv 	vput(dvp);
1045ec933656Sjmmv 
1046ec933656Sjmmv 	/* Free the directory entry we just deleted.  Note that the node
1047ec933656Sjmmv 	 * referred by it will not be removed until the vnode is really
1048ec933656Sjmmv 	 * reclaimed. */
1049ec933656Sjmmv 	tmpfs_free_dirent(tmp, de, TRUE);
1050ec933656Sjmmv 
1051ec933656Sjmmv 	/* Release the deleted vnode (will destroy the node, notify
1052ec933656Sjmmv 	 * interested parties and clean it from the cache). */
1053ec933656Sjmmv 	vput(vp);
1054ec933656Sjmmv 
1055ec933656Sjmmv 	error = 0;
1056ec933656Sjmmv 
1057ec933656Sjmmv out:
1058ec933656Sjmmv 	if (error != 0) {
1059ec933656Sjmmv 		vput(dvp);
1060ec933656Sjmmv 		vput(vp);
1061ec933656Sjmmv 	}
1062ec933656Sjmmv 
1063ec933656Sjmmv 	return error;
1064ec933656Sjmmv }
1065ec933656Sjmmv 
1066ec933656Sjmmv /* --------------------------------------------------------------------- */
1067ec933656Sjmmv 
1068ec933656Sjmmv int
1069ec933656Sjmmv tmpfs_symlink(void *v)
1070ec933656Sjmmv {
1071ec933656Sjmmv 	struct vnode *dvp = ((struct vop_symlink_args *)v)->a_dvp;
1072ec933656Sjmmv 	struct vnode **vpp = ((struct vop_symlink_args *)v)->a_vpp;
1073ec933656Sjmmv 	struct componentname *cnp = ((struct vop_symlink_args *)v)->a_cnp;
1074ec933656Sjmmv 	struct vattr *vap = ((struct vop_symlink_args *)v)->a_vap;
1075ec933656Sjmmv 	char *target = ((struct vop_symlink_args *)v)->a_target;
1076ec933656Sjmmv 
1077ec933656Sjmmv 	KASSERT(vap->va_type == VLNK);
1078ec933656Sjmmv 
1079ec933656Sjmmv 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1080ec933656Sjmmv }
1081ec933656Sjmmv 
1082ec933656Sjmmv /* --------------------------------------------------------------------- */
1083ec933656Sjmmv 
1084ec933656Sjmmv int
1085ec933656Sjmmv tmpfs_readdir(void *v)
1086ec933656Sjmmv {
1087ec933656Sjmmv 	struct vnode *vp = ((struct vop_readdir_args *)v)->a_vp;
1088ec933656Sjmmv 	struct uio *uio = ((struct vop_readdir_args *)v)->a_uio;
1089ec933656Sjmmv 	int *eofflag = ((struct vop_readdir_args *)v)->a_eofflag;
1090ec933656Sjmmv 	off_t **cookies = ((struct vop_readdir_args *)v)->a_cookies;
1091ec933656Sjmmv 	int *ncookies = ((struct vop_readdir_args *)v)->a_ncookies;
1092ec933656Sjmmv 
1093ec933656Sjmmv 	int error;
10948e6209cfSyamt 	off_t startoff;
10958e6209cfSyamt 	off_t cnt;
1096ec933656Sjmmv 	struct tmpfs_node *node;
1097ec933656Sjmmv 
1098ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1099ec933656Sjmmv 
1100ec933656Sjmmv 	/* This operation only makes sense on directory nodes. */
1101ec933656Sjmmv 	if (vp->v_type != VDIR) {
1102ec933656Sjmmv 		error = ENOTDIR;
1103ec933656Sjmmv 		goto out;
1104ec933656Sjmmv 	}
1105ec933656Sjmmv 
1106ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
1107ec933656Sjmmv 
1108ec933656Sjmmv 	startoff = uio->uio_offset;
1109ec933656Sjmmv 
11108e6209cfSyamt 	cnt = 0;
11118e6209cfSyamt 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
1112ec933656Sjmmv 		error = tmpfs_dir_getdotdent(node, uio);
1113ec933656Sjmmv 		if (error == -1) {
1114ec933656Sjmmv 			error = 0;
1115ec933656Sjmmv 			goto outok;
1116ec933656Sjmmv 		} else if (error != 0)
1117ec933656Sjmmv 			goto outok;
11188e6209cfSyamt 		cnt++;
1119ec933656Sjmmv 	}
1120ec933656Sjmmv 
11218e6209cfSyamt 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1122ec933656Sjmmv 		error = tmpfs_dir_getdotdotdent(node, uio);
1123ec933656Sjmmv 		if (error == -1) {
1124ec933656Sjmmv 			error = 0;
1125ec933656Sjmmv 			goto outok;
1126ec933656Sjmmv 		} else if (error != 0)
1127ec933656Sjmmv 			goto outok;
11288e6209cfSyamt 		cnt++;
1129ec933656Sjmmv 	}
1130ec933656Sjmmv 
11318e6209cfSyamt 	error = tmpfs_dir_getdents(node, uio, &cnt);
1132ec933656Sjmmv 	if (error == -1)
1133ec933656Sjmmv 		error = 0;
1134ec933656Sjmmv 	KASSERT(error >= 0);
1135ec933656Sjmmv 
1136ec933656Sjmmv outok:
11378e6209cfSyamt 	/* This label assumes that startoff has been
1138ec933656Sjmmv 	 * initialized.  If the compiler didn't spit out warnings, we'd
1139ec933656Sjmmv 	 * simply make this one be 'out' and drop 'outok'. */
1140ec933656Sjmmv 
1141ec933656Sjmmv 	if (eofflag != NULL)
1142ec933656Sjmmv 		*eofflag =
11438e6209cfSyamt 		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1144ec933656Sjmmv 
1145ec933656Sjmmv 	/* Update NFS-related variables. */
1146ec933656Sjmmv 	if (error == 0 && cookies != NULL && ncookies != NULL) {
1147ec933656Sjmmv 		off_t i;
11488e6209cfSyamt 		off_t off = startoff;
11498e6209cfSyamt 		struct tmpfs_dirent *de = NULL;
1150ec933656Sjmmv 
11518e6209cfSyamt 		*ncookies = cnt;
11528e6209cfSyamt 		*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
1153ec933656Sjmmv 
11548e6209cfSyamt 		for (i = 0; i < cnt; i++) {
11558e6209cfSyamt 			KASSERT(off != TMPFS_DIRCOOKIE_EOF);
11568e6209cfSyamt 			if (off == TMPFS_DIRCOOKIE_DOT) {
11578e6209cfSyamt 				off = TMPFS_DIRCOOKIE_DOTDOT;
11588e6209cfSyamt 			} else {
11598e6209cfSyamt 				if (off == TMPFS_DIRCOOKIE_DOTDOT) {
11608e6209cfSyamt 					de = TAILQ_FIRST(&node->tn_dir);
11618e6209cfSyamt 				} else if (de != NULL) {
11628e6209cfSyamt 					de = TAILQ_NEXT(de, td_entries);
11638e6209cfSyamt 				} else {
11648e6209cfSyamt 					de = tmpfs_dir_lookupbycookie(node,
11658e6209cfSyamt 					    off);
11668e6209cfSyamt 					KASSERT(de != NULL);
11678e6209cfSyamt 					de = TAILQ_NEXT(de, td_entries);
11688e6209cfSyamt 				}
11698e6209cfSyamt 				if (de == NULL) {
11708e6209cfSyamt 					off = TMPFS_DIRCOOKIE_EOF;
11718e6209cfSyamt 				} else {
11728e6209cfSyamt 					off = TMPFS_DIRCOOKIE(de);
11738e6209cfSyamt 				}
11748e6209cfSyamt 			}
11758e6209cfSyamt 
11768e6209cfSyamt 			(*cookies)[i] = off;
11778e6209cfSyamt 		}
11788e6209cfSyamt 		KASSERT(uio->uio_offset == off);
1179ec933656Sjmmv 	}
1180ec933656Sjmmv 
1181ec933656Sjmmv out:
1182ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1183ec933656Sjmmv 
1184ec933656Sjmmv 	return error;
1185ec933656Sjmmv }
1186ec933656Sjmmv 
1187ec933656Sjmmv /* --------------------------------------------------------------------- */
1188ec933656Sjmmv 
1189ec933656Sjmmv int
1190ec933656Sjmmv tmpfs_readlink(void *v)
1191ec933656Sjmmv {
1192ec933656Sjmmv 	struct vnode *vp = ((struct vop_readlink_args *)v)->a_vp;
1193ec933656Sjmmv 	struct uio *uio = ((struct vop_readlink_args *)v)->a_uio;
1194ec933656Sjmmv 
1195ec933656Sjmmv 	int error;
1196ec933656Sjmmv 	struct tmpfs_node *node;
1197ec933656Sjmmv 
1198ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1199ec933656Sjmmv 	KASSERT(uio->uio_offset == 0);
1200ec933656Sjmmv 	KASSERT(vp->v_type == VLNK);
1201ec933656Sjmmv 
1202ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1203ec933656Sjmmv 
1204ec933656Sjmmv 	error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
1205ec933656Sjmmv 	    uio);
1206ec933656Sjmmv 	node->tn_status |= TMPFS_NODE_ACCESSED;
1207ec933656Sjmmv 
1208ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1209ec933656Sjmmv 
1210ec933656Sjmmv 	return error;
1211ec933656Sjmmv }
1212ec933656Sjmmv 
1213ec933656Sjmmv /* --------------------------------------------------------------------- */
1214ec933656Sjmmv 
1215ec933656Sjmmv int
1216ec933656Sjmmv tmpfs_inactive(void *v)
1217ec933656Sjmmv {
1218ec933656Sjmmv 	struct vnode *vp = ((struct vop_inactive_args *)v)->a_vp;
1219ec933656Sjmmv 	struct proc *p = ((struct vop_inactive_args *)v)->a_p;
1220ec933656Sjmmv 
1221ec933656Sjmmv 	struct tmpfs_node *node;
1222ec933656Sjmmv 
1223ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1224ec933656Sjmmv 
1225ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1226ec933656Sjmmv 
1227ec933656Sjmmv 	VOP_UNLOCK(vp, 0);
1228ec933656Sjmmv 
1229ec933656Sjmmv 	if (node->tn_links == 0)
1230ec933656Sjmmv 		vrecycle(vp, NULL, p);
1231ec933656Sjmmv 
1232ec933656Sjmmv 	return 0;
1233ec933656Sjmmv }
1234ec933656Sjmmv 
1235ec933656Sjmmv /* --------------------------------------------------------------------- */
1236ec933656Sjmmv 
1237ec933656Sjmmv int
1238ec933656Sjmmv tmpfs_reclaim(void *v)
1239ec933656Sjmmv {
1240ec933656Sjmmv 	struct vnode *vp = ((struct vop_reclaim_args *)v)->a_vp;
1241ec933656Sjmmv 
1242ec933656Sjmmv 	struct tmpfs_mount *tmp;
1243ec933656Sjmmv 	struct tmpfs_node *node;
1244ec933656Sjmmv 
1245ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
1246ec933656Sjmmv 
1247ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1248ec933656Sjmmv 	tmp = VFS_TO_TMPFS(vp->v_mount);
1249ec933656Sjmmv 
1250ec933656Sjmmv 	if (node->tn_links == 0)
1251ec933656Sjmmv 		VN_KNOTE(vp, NOTE_DELETE);
1252ec933656Sjmmv 
1253ec933656Sjmmv 	cache_purge(vp);
1254ec933656Sjmmv 	tmpfs_free_vp(vp);
1255ec933656Sjmmv 
1256ec933656Sjmmv 	/* If the node referenced by this vnode was deleted by the user,
1257ec933656Sjmmv 	 * we must free its associated data structures (now that the vnode
1258ec933656Sjmmv 	 * is being reclaimed). */
1259ec933656Sjmmv 	if (node->tn_links == 0)
1260ec933656Sjmmv 		tmpfs_free_node(tmp, node);
1261ec933656Sjmmv 
1262ec933656Sjmmv 	KASSERT(!VOP_ISLOCKED(vp));
1263ec933656Sjmmv 	KASSERT(vp->v_data == NULL);
1264ec933656Sjmmv 
1265ec933656Sjmmv 	return 0;
1266ec933656Sjmmv }
1267ec933656Sjmmv 
1268ec933656Sjmmv /* --------------------------------------------------------------------- */
1269ec933656Sjmmv 
1270ec933656Sjmmv int
1271ec933656Sjmmv tmpfs_print(void *v)
1272ec933656Sjmmv {
1273ec933656Sjmmv 	struct vnode *vp = ((struct vop_print_args *)v)->a_vp;
1274ec933656Sjmmv 
1275ec933656Sjmmv 	struct tmpfs_node *node;
1276ec933656Sjmmv 
1277ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1278ec933656Sjmmv 
1279ec933656Sjmmv 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
1280ec933656Sjmmv 	    node, node->tn_flags, node->tn_links);
1281ec933656Sjmmv 	printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX
1282ec933656Sjmmv 	    ", status 0x%x\n",
1283ec933656Sjmmv 	    node->tn_mode, node->tn_uid, node->tn_gid,
1284ec933656Sjmmv 	    (uintmax_t)node->tn_size, node->tn_status);
1285ec933656Sjmmv 
1286ec933656Sjmmv 	if (vp->v_type == VFIFO)
1287ec933656Sjmmv 		fifo_printinfo(vp);
1288ec933656Sjmmv 	lockmgr_printinfo(&vp->v_lock);
1289ec933656Sjmmv 
1290ec933656Sjmmv 	printf("\n");
1291ec933656Sjmmv 
1292ec933656Sjmmv 	return 0;
1293ec933656Sjmmv }
1294ec933656Sjmmv 
1295ec933656Sjmmv /* --------------------------------------------------------------------- */
1296ec933656Sjmmv 
1297ec933656Sjmmv int
1298ec933656Sjmmv tmpfs_pathconf(void *v)
1299ec933656Sjmmv {
1300ec933656Sjmmv 	int name = ((struct vop_pathconf_args *)v)->a_name;
1301ec933656Sjmmv 	register_t *retval = ((struct vop_pathconf_args *)v)->a_retval;
1302ec933656Sjmmv 
1303ec933656Sjmmv 	int error;
1304ec933656Sjmmv 
1305ec933656Sjmmv 	error = 0;
1306ec933656Sjmmv 
1307ec933656Sjmmv 	switch (name) {
1308ec933656Sjmmv 	case _PC_LINK_MAX:
1309ec933656Sjmmv 		*retval = LINK_MAX;
1310ec933656Sjmmv 		break;
1311ec933656Sjmmv 
1312ec933656Sjmmv 	case _PC_NAME_MAX:
1313ec933656Sjmmv 		*retval = NAME_MAX;
1314ec933656Sjmmv 		break;
1315ec933656Sjmmv 
1316ec933656Sjmmv 	case _PC_PATH_MAX:
1317ec933656Sjmmv 		*retval = PATH_MAX;
1318ec933656Sjmmv 		break;
1319ec933656Sjmmv 
1320ec933656Sjmmv 	case _PC_PIPE_BUF:
1321ec933656Sjmmv 		*retval = PIPE_BUF;
1322ec933656Sjmmv 		break;
1323ec933656Sjmmv 
1324ec933656Sjmmv 	case _PC_CHOWN_RESTRICTED:
1325ec933656Sjmmv 		*retval = 1;
1326ec933656Sjmmv 		break;
1327ec933656Sjmmv 
1328ec933656Sjmmv 	case _PC_NO_TRUNC:
1329ec933656Sjmmv 		*retval = 1;
1330ec933656Sjmmv 		break;
1331ec933656Sjmmv 
1332ec933656Sjmmv 	case _PC_SYNC_IO:
1333ec933656Sjmmv 		*retval = 1;
1334ec933656Sjmmv 		break;
1335ec933656Sjmmv 
1336ec933656Sjmmv 	case _PC_FILESIZEBITS:
1337ec933656Sjmmv 		*retval = 0; /* XXX Don't know which value should I return. */
1338ec933656Sjmmv 		break;
1339ec933656Sjmmv 
1340ec933656Sjmmv 	default:
1341ec933656Sjmmv 		error = EINVAL;
1342ec933656Sjmmv 	}
1343ec933656Sjmmv 
1344ec933656Sjmmv 	return error;
1345ec933656Sjmmv }
1346ec933656Sjmmv 
1347ec933656Sjmmv /* --------------------------------------------------------------------- */
1348ec933656Sjmmv 
1349ec933656Sjmmv int
1350ec933656Sjmmv tmpfs_truncate(void *v)
1351ec933656Sjmmv {
1352ec933656Sjmmv 	struct vnode *vp = ((struct vop_truncate_args *)v)->a_vp;
1353ec933656Sjmmv 	off_t length = ((struct vop_truncate_args *)v)->a_length;
1354ec933656Sjmmv 
1355ec933656Sjmmv 	boolean_t extended;
1356ec933656Sjmmv 	int error;
1357ec933656Sjmmv 	struct tmpfs_node *node;
1358ec933656Sjmmv 
1359ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1360ec933656Sjmmv 	extended = length > node->tn_size;
1361ec933656Sjmmv 
1362ec933656Sjmmv 	if (length < 0) {
1363ec933656Sjmmv 		error = EINVAL;
1364ec933656Sjmmv 		goto out;
1365ec933656Sjmmv 	}
1366ec933656Sjmmv 
1367ec933656Sjmmv 	if (node->tn_size == length) {
1368ec933656Sjmmv 		error = 0;
1369ec933656Sjmmv 		goto out;
1370ec933656Sjmmv 	}
1371ec933656Sjmmv 
1372ec933656Sjmmv 	error = tmpfs_reg_resize(vp, length);
1373ec933656Sjmmv 	if (error == 0) {
1374ec933656Sjmmv 		VN_KNOTE(vp, NOTE_ATTRIB | (extended ? NOTE_EXTEND : 0));
1375ec933656Sjmmv 		node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1376ec933656Sjmmv 	}
1377ec933656Sjmmv 
1378ec933656Sjmmv out:
1379ec933656Sjmmv 	(void)VOP_UPDATE(vp, NULL, NULL, 0);
1380ec933656Sjmmv 
1381ec933656Sjmmv 	return error;
1382ec933656Sjmmv }
1383ec933656Sjmmv 
1384ec933656Sjmmv /* --------------------------------------------------------------------- */
1385ec933656Sjmmv 
1386ec933656Sjmmv int
1387ec933656Sjmmv tmpfs_update(void *v)
1388ec933656Sjmmv {
1389ec933656Sjmmv 	struct vnode *vp = ((struct vop_update_args *)v)->a_vp;
1390b9c29c4dSchristos 	struct timespec *acc = ((struct vop_update_args *)v)->a_access;
1391b9c29c4dSchristos 	struct timespec *mod = ((struct vop_update_args *)v)->a_modify;
1392ec933656Sjmmv 	int flags = ((struct vop_update_args *)v)->a_flags;
1393ec933656Sjmmv 
1394b9c29c4dSchristos 	struct timespec *ts = NULL, tsb;
1395ec933656Sjmmv 	struct tmpfs_node *node;
1396ec933656Sjmmv 
1397ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1398ec933656Sjmmv 
1399ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
1400ec933656Sjmmv 
1401ec933656Sjmmv 	if (flags & UPDATE_CLOSE)
1402ec933656Sjmmv 		; /* XXX Need to do anything special? */
1403ec933656Sjmmv 
1404ec933656Sjmmv 	if (node->tn_status != 0) {
1405b9c29c4dSchristos 		if (node->tn_status & TMPFS_NODE_ACCESSED) {
1406b9c29c4dSchristos 			if (acc == NULL)
1407b9c29c4dSchristos 				acc = ts == NULL ? (ts = nanotime(&tsb)) : ts;
1408b9c29c4dSchristos 			node->tn_atime = *acc;
1409b9c29c4dSchristos 		}
1410b9c29c4dSchristos 		if (node->tn_status & TMPFS_NODE_MODIFIED) {
1411b9c29c4dSchristos 			if (mod == NULL)
1412b9c29c4dSchristos 				mod = ts == NULL ? (ts = nanotime(&tsb)) : ts;
1413b9c29c4dSchristos 			node->tn_mtime = *mod;
1414b9c29c4dSchristos 		}
1415b9c29c4dSchristos 		if (node->tn_status & TMPFS_NODE_CHANGED) {
1416b9c29c4dSchristos 			if (ts == NULL)
1417b9c29c4dSchristos 				ts = nanotime(&tsb);
1418b9c29c4dSchristos 			node->tn_ctime = *ts;
1419b9c29c4dSchristos 		}
1420ec933656Sjmmv 		node->tn_status &=
1421ec933656Sjmmv 		    ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
1422ec933656Sjmmv 		    TMPFS_NODE_CHANGED);
1423ec933656Sjmmv 	}
1424ec933656Sjmmv 
1425ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1426ec933656Sjmmv 
1427ec933656Sjmmv 	return 0;
1428ec933656Sjmmv }
1429ec933656Sjmmv 
1430ec933656Sjmmv /* --------------------------------------------------------------------- */
1431ec933656Sjmmv 
1432ec933656Sjmmv int
1433ec933656Sjmmv tmpfs_getpages(void *v)
1434ec933656Sjmmv {
14355f4b660eSjmmv 	struct vnode *vp = ((struct vop_getpages_args *)v)->a_vp;
14365f4b660eSjmmv 	voff_t offset = ((struct vop_getpages_args *)v)->a_offset;
14375f4b660eSjmmv 	struct vm_page **m = ((struct vop_getpages_args *)v)->a_m;
14385f4b660eSjmmv 	int *count = ((struct vop_getpages_args *)v)->a_count;
14395f4b660eSjmmv 	int centeridx = ((struct vop_getpages_args *)v)->a_centeridx;
14405f4b660eSjmmv 	vm_prot_t access_type = ((struct vop_getpages_args *)v)->a_access_type;
14415f4b660eSjmmv 	int advice = ((struct vop_getpages_args *)v)->a_advice;
14425f4b660eSjmmv 	int flags = ((struct vop_getpages_args *)v)->a_flags;
14435f4b660eSjmmv 
1444ec933656Sjmmv 	int error;
14455f4b660eSjmmv 	struct tmpfs_node *node;
14465f4b660eSjmmv 	struct uvm_object *uobj;
144720bb9654Syamt 	int npages = *count;
1448ec933656Sjmmv 
1449647aa775Syamt 	KASSERT(vp->v_type == VREG);
1450647aa775Syamt 	LOCK_ASSERT(simple_lock_held(&vp->v_interlock));
1451ec933656Sjmmv 
14525f4b660eSjmmv 	node = VP_TO_TMPFS_NODE(vp);
14535f4b660eSjmmv 	uobj = node->tn_aobj;
14545f4b660eSjmmv 
145520bb9654Syamt 	/* We currently don't rely on PGO_PASTEOF. */
145620bb9654Syamt 
145720bb9654Syamt 	if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
145820bb9654Syamt 		if ((flags & PGO_LOCKED) == 0)
145920bb9654Syamt 			simple_unlock(&vp->v_interlock);
146020bb9654Syamt 		return EINVAL;
146120bb9654Syamt 	}
146220bb9654Syamt 
146320bb9654Syamt 	if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
146420bb9654Syamt 		npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
146520bb9654Syamt 	}
146620bb9654Syamt 
14675f4b660eSjmmv 	if ((flags & PGO_LOCKED) != 0)
1468647aa775Syamt 		return EBUSY;
1469ec933656Sjmmv 
1470647aa775Syamt 	if ((flags & PGO_NOTIMESTAMP) == 0) {
14715f4b660eSjmmv 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1472ec933656Sjmmv 			node->tn_status |= TMPFS_NODE_ACCESSED;
14735f4b660eSjmmv 
14745f4b660eSjmmv 		if ((access_type & VM_PROT_WRITE) != 0)
1475647aa775Syamt 			node->tn_status |= TMPFS_NODE_MODIFIED;
1476647aa775Syamt 	}
1477ec933656Sjmmv 
1478647aa775Syamt 	simple_unlock(&vp->v_interlock);
1479647aa775Syamt 
1480647aa775Syamt 	simple_lock(&uobj->vmobjlock);
148120bb9654Syamt 	error = (*uobj->pgops->pgo_get)(uobj, offset, m, &npages, centeridx,
14825f4b660eSjmmv 	    access_type, advice, flags);
1483647aa775Syamt 
1484647aa775Syamt 	return error;
1485647aa775Syamt }
1486647aa775Syamt 
1487647aa775Syamt /* --------------------------------------------------------------------- */
1488647aa775Syamt 
1489647aa775Syamt int
1490647aa775Syamt tmpfs_putpages(void *v)
1491647aa775Syamt {
14925f4b660eSjmmv 	struct vnode *vp = ((struct vop_putpages_args *)v)->a_vp;
14935f4b660eSjmmv 	voff_t offlo = ((struct vop_putpages_args *)v)->a_offlo;
14945f4b660eSjmmv 	voff_t offhi = ((struct vop_putpages_args *)v)->a_offhi;
14955f4b660eSjmmv 	int flags = ((struct vop_putpages_args *)v)->a_flags;
14965f4b660eSjmmv 
1497647aa775Syamt 	int error;
14985f4b660eSjmmv 	struct tmpfs_node *node;
14995f4b660eSjmmv 	struct uvm_object *uobj;
1500647aa775Syamt 
1501647aa775Syamt 	LOCK_ASSERT(simple_lock_held(&vp->v_interlock));
1502647aa775Syamt 
15035f4b660eSjmmv 	node = VP_TO_TMPFS_NODE(vp);
15045f4b660eSjmmv 
1505647aa775Syamt 	if (vp->v_type != VREG) {
1506647aa775Syamt 		simple_unlock(&vp->v_interlock);
1507647aa775Syamt 		return 0;
1508647aa775Syamt 	}
1509647aa775Syamt 
1510647aa775Syamt 	uobj = node->tn_aobj;
1511647aa775Syamt 	simple_unlock(&vp->v_interlock);
1512647aa775Syamt 
1513647aa775Syamt 	simple_lock(&uobj->vmobjlock);
15145f4b660eSjmmv 	error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags);
1515647aa775Syamt 
1516647aa775Syamt 	/* XXX mtime */
1517647aa775Syamt 
1518ec933656Sjmmv 	return error;
1519ec933656Sjmmv }
1520