]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/tmpfs/tmpfs_vnops.c
MFV illumos
[FreeBSD/FreeBSD.git] / sys / fs / tmpfs / tmpfs_vnops.c
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$");
38
39 #include <sys/param.h>
40 #include <sys/fcntl.h>
41 #include <sys/lockf.h>
42 #include <sys/lock.h>
43 #include <sys/namei.h>
44 #include <sys/priv.h>
45 #include <sys/proc.h>
46 #include <sys/rwlock.h>
47 #include <sys/sched.h>
48 #include <sys/sf_buf.h>
49 #include <sys/stat.h>
50 #include <sys/systm.h>
51 #include <sys/sysctl.h>
52 #include <sys/unistd.h>
53 #include <sys/vnode.h>
54
55 #include <vm/vm.h>
56 #include <vm/vm_param.h>
57 #include <vm/vm_object.h>
58 #include <vm/vm_page.h>
59 #include <vm/vm_pager.h>
60
61 #include <fs/tmpfs/tmpfs_vnops.h>
62 #include <fs/tmpfs/tmpfs.h>
63
64 SYSCTL_DECL(_vfs_tmpfs);
65
66 static volatile int tmpfs_rename_restarts;
67 SYSCTL_INT(_vfs_tmpfs, OID_AUTO, rename_restarts, CTLFLAG_RD,
68     __DEVOLATILE(int *, &tmpfs_rename_restarts), 0,
69     "Times rename had to restart due to lock contention");
70
71 /* --------------------------------------------------------------------- */
72
73 static int
74 tmpfs_lookup(struct vop_cachedlookup_args *v)
75 {
76         struct vnode *dvp = v->a_dvp;
77         struct vnode **vpp = v->a_vpp;
78         struct componentname *cnp = v->a_cnp;
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, cnp->cn_thread);
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         TMPFS_ASSERT_LOCKED(dnode);
98         if (dnode->tn_dir.tn_parent == NULL) {
99                 error = ENOENT;
100                 goto out;
101         }
102         if (cnp->cn_flags & ISDOTDOT) {
103                 int ltype = 0;
104
105                 ltype = VOP_ISLOCKED(dvp);
106                 vhold(dvp);
107                 VOP_UNLOCK(dvp, 0);
108                 /* Allocate a new vnode on the matching entry. */
109                 error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent,
110                     cnp->cn_lkflags, vpp);
111
112                 vn_lock(dvp, ltype | LK_RETRY);
113                 vdrop(dvp);
114         } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
115                 VREF(dvp);
116                 *vpp = dvp;
117                 error = 0;
118         } else {
119                 de = tmpfs_dir_lookup(dnode, NULL, cnp);
120                 if (de != NULL && de->td_node == NULL)
121                         cnp->cn_flags |= ISWHITEOUT;
122                 if (de == NULL || de->td_node == NULL) {
123                         /* The entry was not found in the directory.
124                          * This is OK if we are creating or renaming an
125                          * entry and are working on the last component of
126                          * the path name. */
127                         if ((cnp->cn_flags & ISLASTCN) &&
128                             (cnp->cn_nameiop == CREATE || \
129                             cnp->cn_nameiop == RENAME ||
130                             (cnp->cn_nameiop == DELETE &&
131                             cnp->cn_flags & DOWHITEOUT &&
132                             cnp->cn_flags & ISWHITEOUT))) {
133                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
134                                     cnp->cn_thread);
135                                 if (error != 0)
136                                         goto out;
137
138                                 /* Keep the component name in the buffer for
139                                  * future uses. */
140                                 cnp->cn_flags |= SAVENAME;
141
142                                 error = EJUSTRETURN;
143                         } else
144                                 error = ENOENT;
145                 } else {
146                         struct tmpfs_node *tnode;
147
148                         /* The entry was found, so get its associated
149                          * tmpfs_node. */
150                         tnode = de->td_node;
151
152                         /* If we are not at the last path component and
153                          * found a non-directory or non-link entry (which
154                          * may itself be pointing to a directory), raise
155                          * an error. */
156                         if ((tnode->tn_type != VDIR &&
157                             tnode->tn_type != VLNK) &&
158                             !(cnp->cn_flags & ISLASTCN)) {
159                                 error = ENOTDIR;
160                                 goto out;
161                         }
162
163                         /* If we are deleting or renaming the entry, keep
164                          * track of its tmpfs_dirent so that it can be
165                          * easily deleted later. */
166                         if ((cnp->cn_flags & ISLASTCN) &&
167                             (cnp->cn_nameiop == DELETE ||
168                             cnp->cn_nameiop == RENAME)) {
169                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
170                                     cnp->cn_thread);
171                                 if (error != 0)
172                                         goto out;
173
174                                 /* Allocate a new vnode on the matching entry. */
175                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
176                                                 cnp->cn_lkflags, vpp);
177                                 if (error != 0)
178                                         goto out;
179
180                                 if ((dnode->tn_mode & S_ISTXT) &&
181                                   VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) &&
182                                   VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) {
183                                         error = EPERM;
184                                         vput(*vpp);
185                                         *vpp = NULL;
186                                         goto out;
187                                 }
188                                 cnp->cn_flags |= SAVENAME;
189                         } else {
190                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
191                                                 cnp->cn_lkflags, vpp);
192                         }
193                 }
194         }
195
196         /* Store the result of this lookup in the cache.  Avoid this if the
197          * request was for creation, as it does not improve timings on
198          * emprical tests. */
199         if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
200                 cache_enter(dvp, *vpp, cnp);
201
202 out:
203         /* If there were no errors, *vpp cannot be null and it must be
204          * locked. */
205         MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp)));
206
207         return error;
208 }
209
210 /* --------------------------------------------------------------------- */
211
212 static int
213 tmpfs_create(struct vop_create_args *v)
214 {
215         struct vnode *dvp = v->a_dvp;
216         struct vnode **vpp = v->a_vpp;
217         struct componentname *cnp = v->a_cnp;
218         struct vattr *vap = v->a_vap;
219
220         MPASS(vap->va_type == VREG || vap->va_type == VSOCK);
221
222         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
223 }
224 /* --------------------------------------------------------------------- */
225
226 static int
227 tmpfs_mknod(struct vop_mknod_args *v)
228 {
229         struct vnode *dvp = v->a_dvp;
230         struct vnode **vpp = v->a_vpp;
231         struct componentname *cnp = v->a_cnp;
232         struct vattr *vap = v->a_vap;
233
234         if (vap->va_type != VBLK && vap->va_type != VCHR &&
235             vap->va_type != VFIFO)
236                 return EINVAL;
237
238         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
239 }
240
241 /* --------------------------------------------------------------------- */
242
243 static int
244 tmpfs_open(struct vop_open_args *v)
245 {
246         struct vnode *vp = v->a_vp;
247         int mode = v->a_mode;
248
249         int error;
250         struct tmpfs_node *node;
251
252         MPASS(VOP_ISLOCKED(vp));
253
254         node = VP_TO_TMPFS_NODE(vp);
255
256         /* The file is still active but all its names have been removed
257          * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
258          * it is about to die. */
259         if (node->tn_links < 1)
260                 return (ENOENT);
261
262         /* If the file is marked append-only, deny write requests. */
263         if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
264                 error = EPERM;
265         else {
266                 error = 0;
267                 /* For regular files, the call below is nop. */
268                 vnode_create_vobject(vp, node->tn_size, v->a_td);
269         }
270
271         MPASS(VOP_ISLOCKED(vp));
272         return error;
273 }
274
275 /* --------------------------------------------------------------------- */
276
277 static int
278 tmpfs_close(struct vop_close_args *v)
279 {
280         struct vnode *vp = v->a_vp;
281
282         /* Update node times. */
283         tmpfs_update(vp);
284
285         return (0);
286 }
287
288 /* --------------------------------------------------------------------- */
289
290 int
291 tmpfs_access(struct vop_access_args *v)
292 {
293         struct vnode *vp = v->a_vp;
294         accmode_t accmode = v->a_accmode;
295         struct ucred *cred = v->a_cred;
296
297         int error;
298         struct tmpfs_node *node;
299
300         MPASS(VOP_ISLOCKED(vp));
301
302         node = VP_TO_TMPFS_NODE(vp);
303
304         switch (vp->v_type) {
305         case VDIR:
306                 /* FALLTHROUGH */
307         case VLNK:
308                 /* FALLTHROUGH */
309         case VREG:
310                 if (accmode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
311                         error = EROFS;
312                         goto out;
313                 }
314                 break;
315
316         case VBLK:
317                 /* FALLTHROUGH */
318         case VCHR:
319                 /* FALLTHROUGH */
320         case VSOCK:
321                 /* FALLTHROUGH */
322         case VFIFO:
323                 break;
324
325         default:
326                 error = EINVAL;
327                 goto out;
328         }
329
330         if (accmode & VWRITE && node->tn_flags & IMMUTABLE) {
331                 error = EPERM;
332                 goto out;
333         }
334
335         error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
336             node->tn_gid, accmode, cred, NULL);
337
338 out:
339         MPASS(VOP_ISLOCKED(vp));
340
341         return error;
342 }
343
344 /* --------------------------------------------------------------------- */
345
346 int
347 tmpfs_getattr(struct vop_getattr_args *v)
348 {
349         struct vnode *vp = v->a_vp;
350         struct vattr *vap = v->a_vap;
351
352         struct tmpfs_node *node;
353
354         node = VP_TO_TMPFS_NODE(vp);
355
356         tmpfs_update(vp);
357
358         vap->va_type = vp->v_type;
359         vap->va_mode = node->tn_mode;
360         vap->va_nlink = node->tn_links;
361         vap->va_uid = node->tn_uid;
362         vap->va_gid = node->tn_gid;
363         vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
364         vap->va_fileid = node->tn_id;
365         vap->va_size = node->tn_size;
366         vap->va_blocksize = PAGE_SIZE;
367         vap->va_atime = node->tn_atime;
368         vap->va_mtime = node->tn_mtime;
369         vap->va_ctime = node->tn_ctime;
370         vap->va_birthtime = node->tn_birthtime;
371         vap->va_gen = node->tn_gen;
372         vap->va_flags = node->tn_flags;
373         vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
374                 node->tn_rdev : NODEV;
375         vap->va_bytes = round_page(node->tn_size);
376         vap->va_filerev = 0;
377
378         return 0;
379 }
380
381 int
382 tmpfs_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, cred, td);
425
426         /* Update the node times.  We give preference to the error codes
427          * generated by this function rather than the ones that may arise
428          * from tmpfs_update. */
429         tmpfs_update(vp);
430
431         MPASS(VOP_ISLOCKED(vp));
432
433         return error;
434 }
435
436 static int
437 tmpfs_read(struct vop_read_args *v)
438 {
439         struct vnode *vp;
440         struct uio *uio;
441         struct tmpfs_node *node;
442
443         vp = v->a_vp;
444         if (vp->v_type != VREG)
445                 return (EISDIR);
446         uio = v->a_uio;
447         if (uio->uio_offset < 0)
448                 return (EINVAL);
449         node = VP_TO_TMPFS_NODE(vp);
450         node->tn_status |= TMPFS_NODE_ACCESSED;
451         return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio));
452 }
453
454 static int
455 tmpfs_write(struct vop_write_args *v)
456 {
457         struct vnode *vp;
458         struct uio *uio;
459         struct tmpfs_node *node;
460         off_t oldsize;
461         int error, ioflag;
462         boolean_t extended;
463
464         vp = v->a_vp;
465         uio = v->a_uio;
466         ioflag = v->a_ioflag;
467         error = 0;
468         node = VP_TO_TMPFS_NODE(vp);
469         oldsize = node->tn_size;
470
471         if (uio->uio_offset < 0 || vp->v_type != VREG)
472                 return (EINVAL);
473         if (uio->uio_resid == 0)
474                 return (0);
475         if (ioflag & IO_APPEND)
476                 uio->uio_offset = node->tn_size;
477         if (uio->uio_offset + uio->uio_resid >
478           VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
479                 return (EFBIG);
480         if (vn_rlimit_fsize(vp, uio, uio->uio_td))
481                 return (EFBIG);
482         extended = uio->uio_offset + uio->uio_resid > node->tn_size;
483         if (extended) {
484                 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid,
485                     FALSE);
486                 if (error != 0)
487                         goto out;
488         }
489
490         error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio);
491         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
492             (extended ? TMPFS_NODE_CHANGED : 0);
493         if (node->tn_mode & (S_ISUID | S_ISGID)) {
494                 if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
495                         node->tn_mode &= ~(S_ISUID | S_ISGID);
496         }
497         if (error != 0)
498                 (void)tmpfs_reg_resize(vp, oldsize, TRUE);
499
500 out:
501         MPASS(IMPLIES(error == 0, uio->uio_resid == 0));
502         MPASS(IMPLIES(error != 0, oldsize == node->tn_size));
503
504         return (error);
505 }
506
507 /* --------------------------------------------------------------------- */
508
509 static int
510 tmpfs_fsync(struct vop_fsync_args *v)
511 {
512         struct vnode *vp = v->a_vp;
513
514         MPASS(VOP_ISLOCKED(vp));
515
516         tmpfs_update(vp);
517
518         return 0;
519 }
520
521 /* --------------------------------------------------------------------- */
522
523 static int
524 tmpfs_remove(struct vop_remove_args *v)
525 {
526         struct vnode *dvp = v->a_dvp;
527         struct vnode *vp = v->a_vp;
528
529         int error;
530         struct tmpfs_dirent *de;
531         struct tmpfs_mount *tmp;
532         struct tmpfs_node *dnode;
533         struct tmpfs_node *node;
534
535         MPASS(VOP_ISLOCKED(dvp));
536         MPASS(VOP_ISLOCKED(vp));
537
538         if (vp->v_type == VDIR) {
539                 error = EISDIR;
540                 goto out;
541         }
542
543         dnode = VP_TO_TMPFS_DIR(dvp);
544         node = VP_TO_TMPFS_NODE(vp);
545         tmp = VFS_TO_TMPFS(vp->v_mount);
546         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
547         MPASS(de != NULL);
548
549         /* Files marked as immutable or append-only cannot be deleted. */
550         if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
551             (dnode->tn_flags & APPEND)) {
552                 error = EPERM;
553                 goto out;
554         }
555
556         /* Remove the entry from the directory; as it is a file, we do not
557          * have to change the number of hard links of the directory. */
558         tmpfs_dir_detach(dvp, de);
559         if (v->a_cnp->cn_flags & DOWHITEOUT)
560                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
561
562         /* Free the directory entry we just deleted.  Note that the node
563          * referred by it will not be removed until the vnode is really
564          * reclaimed. */
565         tmpfs_free_dirent(tmp, de);
566
567         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED;
568         error = 0;
569
570 out:
571
572         return error;
573 }
574
575 /* --------------------------------------------------------------------- */
576
577 static int
578 tmpfs_link(struct vop_link_args *v)
579 {
580         struct vnode *dvp = v->a_tdvp;
581         struct vnode *vp = v->a_vp;
582         struct componentname *cnp = v->a_cnp;
583
584         int error;
585         struct tmpfs_dirent *de;
586         struct tmpfs_node *node;
587
588         MPASS(VOP_ISLOCKED(dvp));
589         MPASS(cnp->cn_flags & HASBUF);
590         MPASS(dvp != vp); /* XXX When can this be false? */
591
592         node = VP_TO_TMPFS_NODE(vp);
593
594         /* XXX: Why aren't the following two tests done by the caller? */
595
596         /* Hard links of directories are forbidden. */
597         if (vp->v_type == VDIR) {
598                 error = EPERM;
599                 goto out;
600         }
601
602         /* Cannot create cross-device links. */
603         if (dvp->v_mount != vp->v_mount) {
604                 error = EXDEV;
605                 goto out;
606         }
607
608         /* Ensure that we do not overflow the maximum number of links imposed
609          * by the system. */
610         MPASS(node->tn_links <= LINK_MAX);
611         if (node->tn_links == LINK_MAX) {
612                 error = EMLINK;
613                 goto out;
614         }
615
616         /* We cannot create links of files marked immutable or append-only. */
617         if (node->tn_flags & (IMMUTABLE | APPEND)) {
618                 error = EPERM;
619                 goto out;
620         }
621
622         /* Allocate a new directory entry to represent the node. */
623         error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
624             cnp->cn_nameptr, cnp->cn_namelen, &de);
625         if (error != 0)
626                 goto out;
627
628         /* Insert the new directory entry into the appropriate directory. */
629         if (cnp->cn_flags & ISWHITEOUT)
630                 tmpfs_dir_whiteout_remove(dvp, cnp);
631         tmpfs_dir_attach(dvp, de);
632
633         /* vp link count has changed, so update node times. */
634         node->tn_status |= TMPFS_NODE_CHANGED;
635         tmpfs_update(vp);
636
637         error = 0;
638
639 out:
640         return error;
641 }
642
643 /* --------------------------------------------------------------------- */
644
645 /*
646  * We acquire all but fdvp locks using non-blocking acquisitions.  If we
647  * fail to acquire any lock in the path we will drop all held locks,
648  * acquire the new lock in a blocking fashion, and then release it and
649  * restart the rename.  This acquire/release step ensures that we do not
650  * spin on a lock waiting for release.  On error release all vnode locks
651  * and decrement references the way tmpfs_rename() would do.
652  */
653 static int
654 tmpfs_rename_relock(struct vnode *fdvp, struct vnode **fvpp,
655     struct vnode *tdvp, struct vnode **tvpp,
656     struct componentname *fcnp, struct componentname *tcnp)
657 {
658         struct vnode *nvp;
659         struct mount *mp;
660         struct tmpfs_dirent *de;
661         int error, restarts = 0;
662
663         VOP_UNLOCK(tdvp, 0);
664         if (*tvpp != NULL && *tvpp != tdvp)
665                 VOP_UNLOCK(*tvpp, 0);
666         mp = fdvp->v_mount;
667
668 relock:
669         restarts += 1;
670         error = vn_lock(fdvp, LK_EXCLUSIVE);
671         if (error)
672                 goto releout;
673         if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
674                 VOP_UNLOCK(fdvp, 0);
675                 error = vn_lock(tdvp, LK_EXCLUSIVE);
676                 if (error)
677                         goto releout;
678                 VOP_UNLOCK(tdvp, 0);
679                 goto relock;
680         }
681         /*
682          * Re-resolve fvp to be certain it still exists and fetch the
683          * correct vnode.
684          */
685         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(fdvp), NULL, fcnp);
686         if (de == NULL) {
687                 VOP_UNLOCK(fdvp, 0);
688                 VOP_UNLOCK(tdvp, 0);
689                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
690                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
691                         error = EINVAL;
692                 else
693                         error = ENOENT;
694                 goto releout;
695         }
696         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE | LK_NOWAIT, &nvp);
697         if (error != 0) {
698                 VOP_UNLOCK(fdvp, 0);
699                 VOP_UNLOCK(tdvp, 0);
700                 if (error != EBUSY)
701                         goto releout;
702                 error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, &nvp);
703                 if (error != 0)
704                         goto releout;
705                 VOP_UNLOCK(nvp, 0);
706                 /*
707                  * Concurrent rename race.
708                  */
709                 if (nvp == tdvp) {
710                         vrele(nvp);
711                         error = EINVAL;
712                         goto releout;
713                 }
714                 vrele(*fvpp);
715                 *fvpp = nvp;
716                 goto relock;
717         }
718         vrele(*fvpp);
719         *fvpp = nvp;
720         VOP_UNLOCK(*fvpp, 0);
721         /*
722          * Re-resolve tvp and acquire the vnode lock if present.
723          */
724         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(tdvp), NULL, tcnp);
725         /*
726          * If tvp disappeared we just carry on.
727          */
728         if (de == NULL && *tvpp != NULL) {
729                 vrele(*tvpp);
730                 *tvpp = NULL;
731         }
732         /*
733          * Get the tvp ino if the lookup succeeded.  We may have to restart
734          * if the non-blocking acquire fails.
735          */
736         if (de != NULL) {
737                 nvp = NULL;
738                 error = tmpfs_alloc_vp(mp, de->td_node,
739                     LK_EXCLUSIVE | LK_NOWAIT, &nvp);
740                 if (*tvpp != NULL)
741                         vrele(*tvpp);
742                 *tvpp = nvp;
743                 if (error != 0) {
744                         VOP_UNLOCK(fdvp, 0);
745                         VOP_UNLOCK(tdvp, 0);
746                         if (error != EBUSY)
747                                 goto releout;
748                         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE,
749                             &nvp);
750                         if (error != 0)
751                                 goto releout;
752                         VOP_UNLOCK(nvp, 0);
753                         /*
754                          * fdvp contains fvp, thus tvp (=fdvp) is not empty.
755                          */
756                         if (nvp == fdvp) {
757                                 error = ENOTEMPTY;
758                                 goto releout;
759                         }
760                         goto relock;
761                 }
762         }
763         tmpfs_rename_restarts += restarts;
764
765         return (0);
766
767 releout:
768         vrele(fdvp);
769         vrele(*fvpp);
770         vrele(tdvp);
771         if (*tvpp != NULL)
772                 vrele(*tvpp);
773         tmpfs_rename_restarts += restarts;
774
775         return (error);
776 }
777
778 static int
779 tmpfs_rename(struct vop_rename_args *v)
780 {
781         struct vnode *fdvp = v->a_fdvp;
782         struct vnode *fvp = v->a_fvp;
783         struct componentname *fcnp = v->a_fcnp;
784         struct vnode *tdvp = v->a_tdvp;
785         struct vnode *tvp = v->a_tvp;
786         struct componentname *tcnp = v->a_tcnp;
787         struct mount *mp = NULL;
788
789         char *newname;
790         int error;
791         struct tmpfs_dirent *de;
792         struct tmpfs_mount *tmp;
793         struct tmpfs_node *fdnode;
794         struct tmpfs_node *fnode;
795         struct tmpfs_node *tnode;
796         struct tmpfs_node *tdnode;
797
798         MPASS(VOP_ISLOCKED(tdvp));
799         MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
800         MPASS(fcnp->cn_flags & HASBUF);
801         MPASS(tcnp->cn_flags & HASBUF);
802
803         /* Disallow cross-device renames.
804          * XXX Why isn't this done by the caller? */
805         if (fvp->v_mount != tdvp->v_mount ||
806             (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
807                 error = EXDEV;
808                 goto out;
809         }
810
811         /* If source and target are the same file, there is nothing to do. */
812         if (fvp == tvp) {
813                 error = 0;
814                 goto out;
815         }
816
817         /* If we need to move the directory between entries, lock the
818          * source so that we can safely operate on it. */
819         if (fdvp != tdvp && fdvp != tvp) {
820                 if (vn_lock(fdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
821                         mp = tdvp->v_mount;
822                         error = vfs_busy(mp, 0);
823                         if (error != 0) {
824                                 mp = NULL;
825                                 goto out;
826                         }
827                         error = tmpfs_rename_relock(fdvp, &fvp, tdvp, &tvp,
828                             fcnp, tcnp);
829                         if (error != 0) {
830                                 vfs_unbusy(mp);
831                                 return (error);
832                         }
833                         ASSERT_VOP_ELOCKED(fdvp,
834                             "tmpfs_rename: fdvp not locked");
835                         ASSERT_VOP_ELOCKED(tdvp,
836                             "tmpfs_rename: tdvp not locked");
837                         if (tvp != NULL)
838                                 ASSERT_VOP_ELOCKED(tvp,
839                                     "tmpfs_rename: tvp not locked");
840                         if (fvp == tvp) {
841                                 error = 0;
842                                 goto out_locked;
843                         }
844                 }
845         }
846
847         tmp = VFS_TO_TMPFS(tdvp->v_mount);
848         tdnode = VP_TO_TMPFS_DIR(tdvp);
849         tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
850         fdnode = VP_TO_TMPFS_DIR(fdvp);
851         fnode = VP_TO_TMPFS_NODE(fvp);
852         de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
853
854         /* Entry can disappear before we lock fdvp,
855          * also avoid manipulating '.' and '..' entries. */
856         if (de == NULL) {
857                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
858                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
859                         error = EINVAL;
860                 else
861                         error = ENOENT;
862                 goto out_locked;
863         }
864         MPASS(de->td_node == fnode);
865
866         /* If re-naming a directory to another preexisting directory
867          * ensure that the target directory is empty so that its
868          * removal causes no side effects.
869          * Kern_rename gurantees the destination to be a directory
870          * if the source is one. */
871         if (tvp != NULL) {
872                 MPASS(tnode != NULL);
873
874                 if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
875                     (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
876                         error = EPERM;
877                         goto out_locked;
878                 }
879
880                 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
881                         if (tnode->tn_size > 0) {
882                                 error = ENOTEMPTY;
883                                 goto out_locked;
884                         }
885                 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
886                         error = ENOTDIR;
887                         goto out_locked;
888                 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
889                         error = EISDIR;
890                         goto out_locked;
891                 } else {
892                         MPASS(fnode->tn_type != VDIR &&
893                                 tnode->tn_type != VDIR);
894                 }
895         }
896
897         if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
898             || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
899                 error = EPERM;
900                 goto out_locked;
901         }
902
903         /* Ensure that we have enough memory to hold the new name, if it
904          * has to be changed. */
905         if (fcnp->cn_namelen != tcnp->cn_namelen ||
906             bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
907                 newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
908         } else
909                 newname = NULL;
910
911         /* If the node is being moved to another directory, we have to do
912          * the move. */
913         if (fdnode != tdnode) {
914                 /* In case we are moving a directory, we have to adjust its
915                  * parent to point to the new parent. */
916                 if (de->td_node->tn_type == VDIR) {
917                         struct tmpfs_node *n;
918
919                         /* Ensure the target directory is not a child of the
920                          * directory being moved.  Otherwise, we'd end up
921                          * with stale nodes. */
922                         n = tdnode;
923                         /* TMPFS_LOCK garanties that no nodes are freed while
924                          * traversing the list. Nodes can only be marked as
925                          * removed: tn_parent == NULL. */
926                         TMPFS_LOCK(tmp);
927                         TMPFS_NODE_LOCK(n);
928                         while (n != n->tn_dir.tn_parent) {
929                                 struct tmpfs_node *parent;
930
931                                 if (n == fnode) {
932                                         TMPFS_NODE_UNLOCK(n);
933                                         TMPFS_UNLOCK(tmp);
934                                         error = EINVAL;
935                                         if (newname != NULL)
936                                                     free(newname, M_TMPFSNAME);
937                                         goto out_locked;
938                                 }
939                                 parent = n->tn_dir.tn_parent;
940                                 TMPFS_NODE_UNLOCK(n);
941                                 if (parent == NULL) {
942                                         n = NULL;
943                                         break;
944                                 }
945                                 TMPFS_NODE_LOCK(parent);
946                                 if (parent->tn_dir.tn_parent == NULL) {
947                                         TMPFS_NODE_UNLOCK(parent);
948                                         n = NULL;
949                                         break;
950                                 }
951                                 n = parent;
952                         }
953                         TMPFS_UNLOCK(tmp);
954                         if (n == NULL) {
955                                 error = EINVAL;
956                                 if (newname != NULL)
957                                             free(newname, M_TMPFSNAME);
958                                 goto out_locked;
959                         }
960                         TMPFS_NODE_UNLOCK(n);
961
962                         /* Adjust the parent pointer. */
963                         TMPFS_VALIDATE_DIR(fnode);
964                         TMPFS_NODE_LOCK(de->td_node);
965                         de->td_node->tn_dir.tn_parent = tdnode;
966                         TMPFS_NODE_UNLOCK(de->td_node);
967
968                         /* As a result of changing the target of the '..'
969                          * entry, the link count of the source and target
970                          * directories has to be adjusted. */
971                         TMPFS_NODE_LOCK(tdnode);
972                         TMPFS_ASSERT_LOCKED(tdnode);
973                         tdnode->tn_links++;
974                         TMPFS_NODE_UNLOCK(tdnode);
975
976                         TMPFS_NODE_LOCK(fdnode);
977                         TMPFS_ASSERT_LOCKED(fdnode);
978                         fdnode->tn_links--;
979                         TMPFS_NODE_UNLOCK(fdnode);
980                 }
981         }
982
983         /* Do the move: just remove the entry from the source directory
984          * and insert it into the target one. */
985         tmpfs_dir_detach(fdvp, de);
986
987         if (fcnp->cn_flags & DOWHITEOUT)
988                 tmpfs_dir_whiteout_add(fdvp, fcnp);
989         if (tcnp->cn_flags & ISWHITEOUT)
990                 tmpfs_dir_whiteout_remove(tdvp, tcnp);
991
992         /* If the name has changed, we need to make it effective by changing
993          * it in the directory entry. */
994         if (newname != NULL) {
995                 MPASS(tcnp->cn_namelen <= MAXNAMLEN);
996
997                 free(de->ud.td_name, M_TMPFSNAME);
998                 de->ud.td_name = newname;
999                 tmpfs_dirent_init(de, tcnp->cn_nameptr, tcnp->cn_namelen);
1000
1001                 fnode->tn_status |= TMPFS_NODE_CHANGED;
1002                 tdnode->tn_status |= TMPFS_NODE_MODIFIED;
1003         }
1004
1005         /* If we are overwriting an entry, we have to remove the old one
1006          * from the target directory. */
1007         if (tvp != NULL) {
1008                 struct tmpfs_dirent *tde;
1009
1010                 /* Remove the old entry from the target directory. */
1011                 tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
1012                 tmpfs_dir_detach(tdvp, tde);
1013
1014                 /* Free the directory entry we just deleted.  Note that the
1015                  * node referred by it will not be removed until the vnode is
1016                  * really reclaimed. */
1017                 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde);
1018         }
1019
1020         tmpfs_dir_attach(tdvp, de);
1021
1022         cache_purge(fvp);
1023         if (tvp != NULL)
1024                 cache_purge(tvp);
1025         cache_purge_negative(tdvp);
1026
1027         error = 0;
1028
1029 out_locked:
1030         if (fdvp != tdvp && fdvp != tvp)
1031                 VOP_UNLOCK(fdvp, 0);
1032
1033 out:
1034         /* Release target nodes. */
1035         /* XXX: I don't understand when tdvp can be the same as tvp, but
1036          * other code takes care of this... */
1037         if (tdvp == tvp)
1038                 vrele(tdvp);
1039         else
1040                 vput(tdvp);
1041         if (tvp != NULL)
1042                 vput(tvp);
1043
1044         /* Release source nodes. */
1045         vrele(fdvp);
1046         vrele(fvp);
1047
1048         if (mp != NULL)
1049                 vfs_unbusy(mp);
1050
1051         return error;
1052 }
1053
1054 /* --------------------------------------------------------------------- */
1055
1056 static int
1057 tmpfs_mkdir(struct vop_mkdir_args *v)
1058 {
1059         struct vnode *dvp = v->a_dvp;
1060         struct vnode **vpp = v->a_vpp;
1061         struct componentname *cnp = v->a_cnp;
1062         struct vattr *vap = v->a_vap;
1063
1064         MPASS(vap->va_type == VDIR);
1065
1066         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
1067 }
1068
1069 /* --------------------------------------------------------------------- */
1070
1071 static int
1072 tmpfs_rmdir(struct vop_rmdir_args *v)
1073 {
1074         struct vnode *dvp = v->a_dvp;
1075         struct vnode *vp = v->a_vp;
1076
1077         int error;
1078         struct tmpfs_dirent *de;
1079         struct tmpfs_mount *tmp;
1080         struct tmpfs_node *dnode;
1081         struct tmpfs_node *node;
1082
1083         MPASS(VOP_ISLOCKED(dvp));
1084         MPASS(VOP_ISLOCKED(vp));
1085
1086         tmp = VFS_TO_TMPFS(dvp->v_mount);
1087         dnode = VP_TO_TMPFS_DIR(dvp);
1088         node = VP_TO_TMPFS_DIR(vp);
1089
1090         /* Directories with more than two entries ('.' and '..') cannot be
1091          * removed. */
1092          if (node->tn_size > 0) {
1093                  error = ENOTEMPTY;
1094                  goto out;
1095          }
1096
1097         if ((dnode->tn_flags & APPEND)
1098             || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
1099                 error = EPERM;
1100                 goto out;
1101         }
1102
1103         /* This invariant holds only if we are not trying to remove "..".
1104           * We checked for that above so this is safe now. */
1105         MPASS(node->tn_dir.tn_parent == dnode);
1106
1107         /* Get the directory entry associated with node (vp).  This was
1108          * filled by tmpfs_lookup while looking up the entry. */
1109         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
1110         MPASS(TMPFS_DIRENT_MATCHES(de,
1111             v->a_cnp->cn_nameptr,
1112             v->a_cnp->cn_namelen));
1113
1114         /* Check flags to see if we are allowed to remove the directory. */
1115         if (dnode->tn_flags & APPEND
1116                 || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
1117                 error = EPERM;
1118                 goto out;
1119         }
1120
1121
1122         /* Detach the directory entry from the directory (dnode). */
1123         tmpfs_dir_detach(dvp, de);
1124         if (v->a_cnp->cn_flags & DOWHITEOUT)
1125                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
1126
1127         /* No vnode should be allocated for this entry from this point */
1128         TMPFS_NODE_LOCK(node);
1129         TMPFS_ASSERT_ELOCKED(node);
1130         node->tn_links--;
1131         node->tn_dir.tn_parent = NULL;
1132         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
1133             TMPFS_NODE_MODIFIED;
1134
1135         TMPFS_NODE_UNLOCK(node);
1136
1137         TMPFS_NODE_LOCK(dnode);
1138         TMPFS_ASSERT_ELOCKED(dnode);
1139         dnode->tn_links--;
1140         dnode->tn_status |= TMPFS_NODE_ACCESSED | \
1141             TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1142         TMPFS_NODE_UNLOCK(dnode);
1143
1144         cache_purge(dvp);
1145         cache_purge(vp);
1146
1147         /* Free the directory entry we just deleted.  Note that the node
1148          * referred by it will not be removed until the vnode is really
1149          * reclaimed. */
1150         tmpfs_free_dirent(tmp, de);
1151
1152         /* Release the deleted vnode (will destroy the node, notify
1153          * interested parties and clean it from the cache). */
1154
1155         dnode->tn_status |= TMPFS_NODE_CHANGED;
1156         tmpfs_update(dvp);
1157
1158         error = 0;
1159
1160 out:
1161         return error;
1162 }
1163
1164 /* --------------------------------------------------------------------- */
1165
1166 static int
1167 tmpfs_symlink(struct vop_symlink_args *v)
1168 {
1169         struct vnode *dvp = v->a_dvp;
1170         struct vnode **vpp = v->a_vpp;
1171         struct componentname *cnp = v->a_cnp;
1172         struct vattr *vap = v->a_vap;
1173         char *target = v->a_target;
1174
1175 #ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */
1176         MPASS(vap->va_type == VLNK);
1177 #else
1178         vap->va_type = VLNK;
1179 #endif
1180
1181         return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1182 }
1183
1184 /* --------------------------------------------------------------------- */
1185
1186 static int
1187 tmpfs_readdir(struct vop_readdir_args *v)
1188 {
1189         struct vnode *vp = v->a_vp;
1190         struct uio *uio = v->a_uio;
1191         int *eofflag = v->a_eofflag;
1192         u_long **cookies = v->a_cookies;
1193         int *ncookies = v->a_ncookies;
1194
1195         int error;
1196         ssize_t startresid;
1197         int maxcookies;
1198         struct tmpfs_node *node;
1199
1200         /* This operation only makes sense on directory nodes. */
1201         if (vp->v_type != VDIR)
1202                 return ENOTDIR;
1203
1204         maxcookies = 0;
1205         node = VP_TO_TMPFS_DIR(vp);
1206
1207         startresid = uio->uio_resid;
1208
1209         /* Allocate cookies for NFS and compat modules. */
1210         if (cookies != NULL && ncookies != NULL) {
1211                 maxcookies = howmany(node->tn_size,
1212                     sizeof(struct tmpfs_dirent)) + 2;
1213                 *cookies = malloc(maxcookies * sizeof(**cookies), M_TEMP,
1214                     M_WAITOK);
1215                 *ncookies = 0;
1216         }
1217
1218         if (cookies == NULL)
1219                 error = tmpfs_dir_getdents(node, uio, 0, NULL, NULL);
1220         else
1221                 error = tmpfs_dir_getdents(node, uio, maxcookies, *cookies,
1222                     ncookies);
1223
1224         /* Buffer was filled without hitting EOF. */
1225         if (error == EJUSTRETURN)
1226                 error = (uio->uio_resid != startresid) ? 0 : EINVAL;
1227
1228         if (error != 0 && cookies != NULL)
1229                 free(*cookies, M_TEMP);
1230
1231         if (eofflag != NULL)
1232                 *eofflag =
1233                     (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1234
1235         return error;
1236 }
1237
1238 /* --------------------------------------------------------------------- */
1239
1240 static int
1241 tmpfs_readlink(struct vop_readlink_args *v)
1242 {
1243         struct vnode *vp = v->a_vp;
1244         struct uio *uio = v->a_uio;
1245
1246         int error;
1247         struct tmpfs_node *node;
1248
1249         MPASS(uio->uio_offset == 0);
1250         MPASS(vp->v_type == VLNK);
1251
1252         node = VP_TO_TMPFS_NODE(vp);
1253
1254         error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
1255             uio);
1256         node->tn_status |= TMPFS_NODE_ACCESSED;
1257
1258         return error;
1259 }
1260
1261 /* --------------------------------------------------------------------- */
1262
1263 static int
1264 tmpfs_inactive(struct vop_inactive_args *v)
1265 {
1266         struct vnode *vp = v->a_vp;
1267
1268         struct tmpfs_node *node;
1269
1270         node = VP_TO_TMPFS_NODE(vp);
1271
1272         if (node->tn_links == 0)
1273                 vrecycle(vp);
1274
1275         return 0;
1276 }
1277
1278 /* --------------------------------------------------------------------- */
1279
1280 int
1281 tmpfs_reclaim(struct vop_reclaim_args *v)
1282 {
1283         struct vnode *vp = v->a_vp;
1284
1285         struct tmpfs_mount *tmp;
1286         struct tmpfs_node *node;
1287
1288         node = VP_TO_TMPFS_NODE(vp);
1289         tmp = VFS_TO_TMPFS(vp->v_mount);
1290
1291         if (vp->v_type == VREG)
1292                 tmpfs_destroy_vobject(vp, node->tn_reg.tn_aobj);
1293         else
1294                 vnode_destroy_vobject(vp);
1295         vp->v_object = NULL;
1296         cache_purge(vp);
1297
1298         TMPFS_NODE_LOCK(node);
1299         TMPFS_ASSERT_ELOCKED(node);
1300         tmpfs_free_vp(vp);
1301
1302         /* If the node referenced by this vnode was deleted by the user,
1303          * we must free its associated data structures (now that the vnode
1304          * is being reclaimed). */
1305         if (node->tn_links == 0 &&
1306             (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
1307                 node->tn_vpstate = TMPFS_VNODE_DOOMED;
1308                 TMPFS_NODE_UNLOCK(node);
1309                 tmpfs_free_node(tmp, node);
1310         } else
1311                 TMPFS_NODE_UNLOCK(node);
1312
1313         MPASS(vp->v_data == NULL);
1314         return 0;
1315 }
1316
1317 /* --------------------------------------------------------------------- */
1318
1319 static int
1320 tmpfs_print(struct vop_print_args *v)
1321 {
1322         struct vnode *vp = v->a_vp;
1323
1324         struct tmpfs_node *node;
1325
1326         node = VP_TO_TMPFS_NODE(vp);
1327
1328         printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%lx, links %d\n",
1329             node, node->tn_flags, node->tn_links);
1330         printf("\tmode 0%o, owner %d, group %d, size %jd, status 0x%x\n",
1331             node->tn_mode, node->tn_uid, node->tn_gid,
1332             (intmax_t)node->tn_size, node->tn_status);
1333
1334         if (vp->v_type == VFIFO)
1335                 fifo_printinfo(vp);
1336
1337         printf("\n");
1338
1339         return 0;
1340 }
1341
1342 /* --------------------------------------------------------------------- */
1343
1344 static int
1345 tmpfs_pathconf(struct vop_pathconf_args *v)
1346 {
1347         int name = v->a_name;
1348         register_t *retval = v->a_retval;
1349
1350         int error;
1351
1352         error = 0;
1353
1354         switch (name) {
1355         case _PC_LINK_MAX:
1356                 *retval = LINK_MAX;
1357                 break;
1358
1359         case _PC_NAME_MAX:
1360                 *retval = NAME_MAX;
1361                 break;
1362
1363         case _PC_PATH_MAX:
1364                 *retval = PATH_MAX;
1365                 break;
1366
1367         case _PC_PIPE_BUF:
1368                 *retval = PIPE_BUF;
1369                 break;
1370
1371         case _PC_CHOWN_RESTRICTED:
1372                 *retval = 1;
1373                 break;
1374
1375         case _PC_NO_TRUNC:
1376                 *retval = 1;
1377                 break;
1378
1379         case _PC_SYNC_IO:
1380                 *retval = 1;
1381                 break;
1382
1383         case _PC_FILESIZEBITS:
1384                 *retval = 0; /* XXX Don't know which value should I return. */
1385                 break;
1386
1387         default:
1388                 error = EINVAL;
1389         }
1390
1391         return error;
1392 }
1393
1394 static int
1395 tmpfs_vptofh(struct vop_vptofh_args *ap)
1396 {
1397         struct tmpfs_fid *tfhp;
1398         struct tmpfs_node *node;
1399
1400         tfhp = (struct tmpfs_fid *)ap->a_fhp;
1401         node = VP_TO_TMPFS_NODE(ap->a_vp);
1402
1403         tfhp->tf_len = sizeof(struct tmpfs_fid);
1404         tfhp->tf_id = node->tn_id;
1405         tfhp->tf_gen = node->tn_gen;
1406
1407         return (0);
1408 }
1409
1410 static int
1411 tmpfs_whiteout(struct vop_whiteout_args *ap)
1412 {
1413         struct vnode *dvp = ap->a_dvp;
1414         struct componentname *cnp = ap->a_cnp;
1415         struct tmpfs_dirent *de;
1416
1417         switch (ap->a_flags) {
1418         case LOOKUP:
1419                 return (0);
1420         case CREATE:
1421                 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
1422                 if (de != NULL)
1423                         return (de->td_node == NULL ? 0 : EEXIST);
1424                 return (tmpfs_dir_whiteout_add(dvp, cnp));
1425         case DELETE:
1426                 tmpfs_dir_whiteout_remove(dvp, cnp);
1427                 return (0);
1428         default:
1429                 panic("tmpfs_whiteout: unknown op");
1430         }
1431 }
1432
1433 /* --------------------------------------------------------------------- */
1434
1435 /*
1436  * vnode operations vector used for files stored in a tmpfs file system.
1437  */
1438 struct vop_vector tmpfs_vnodeop_entries = {
1439         .vop_default =                  &default_vnodeops,
1440         .vop_lookup =                   vfs_cache_lookup,
1441         .vop_cachedlookup =             tmpfs_lookup,
1442         .vop_create =                   tmpfs_create,
1443         .vop_mknod =                    tmpfs_mknod,
1444         .vop_open =                     tmpfs_open,
1445         .vop_close =                    tmpfs_close,
1446         .vop_access =                   tmpfs_access,
1447         .vop_getattr =                  tmpfs_getattr,
1448         .vop_setattr =                  tmpfs_setattr,
1449         .vop_read =                     tmpfs_read,
1450         .vop_write =                    tmpfs_write,
1451         .vop_fsync =                    tmpfs_fsync,
1452         .vop_remove =                   tmpfs_remove,
1453         .vop_link =                     tmpfs_link,
1454         .vop_rename =                   tmpfs_rename,
1455         .vop_mkdir =                    tmpfs_mkdir,
1456         .vop_rmdir =                    tmpfs_rmdir,
1457         .vop_symlink =                  tmpfs_symlink,
1458         .vop_readdir =                  tmpfs_readdir,
1459         .vop_readlink =                 tmpfs_readlink,
1460         .vop_inactive =                 tmpfs_inactive,
1461         .vop_reclaim =                  tmpfs_reclaim,
1462         .vop_print =                    tmpfs_print,
1463         .vop_pathconf =                 tmpfs_pathconf,
1464         .vop_vptofh =                   tmpfs_vptofh,
1465         .vop_whiteout =                 tmpfs_whiteout,
1466         .vop_bmap =                     VOP_EOPNOTSUPP,
1467 };
1468