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