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