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