tmpfs_vnops.c revision 1.87 1 /* $NetBSD: tmpfs_vnops.c,v 1.87 2011/06/12 03:35:54 rmind Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006, 2007 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
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.87 2011/06/12 03:35:54 rmind Exp $");
39
40 #include <sys/param.h>
41 #include <sys/dirent.h>
42 #include <sys/fcntl.h>
43 #include <sys/event.h>
44 #include <sys/malloc.h>
45 #include <sys/namei.h>
46 #include <sys/stat.h>
47 #include <sys/uio.h>
48 #include <sys/unistd.h>
49 #include <sys/vnode.h>
50 #include <sys/lockf.h>
51 #include <sys/kauth.h>
52
53 #include <uvm/uvm.h>
54
55 #include <miscfs/fifofs/fifo.h>
56 #include <miscfs/genfs/genfs.h>
57 #include <fs/tmpfs/tmpfs_vnops.h>
58 #include <fs/tmpfs/tmpfs.h>
59
60 /*
61 * vnode operations vector used for files stored in a tmpfs file system.
62 */
63 int (**tmpfs_vnodeop_p)(void *);
64 const struct vnodeopv_entry_desc tmpfs_vnodeop_entries[] = {
65 { &vop_default_desc, vn_default_error },
66 { &vop_lookup_desc, tmpfs_lookup },
67 { &vop_create_desc, tmpfs_create },
68 { &vop_mknod_desc, tmpfs_mknod },
69 { &vop_open_desc, tmpfs_open },
70 { &vop_close_desc, tmpfs_close },
71 { &vop_access_desc, tmpfs_access },
72 { &vop_getattr_desc, tmpfs_getattr },
73 { &vop_setattr_desc, tmpfs_setattr },
74 { &vop_read_desc, tmpfs_read },
75 { &vop_write_desc, tmpfs_write },
76 { &vop_ioctl_desc, tmpfs_ioctl },
77 { &vop_fcntl_desc, tmpfs_fcntl },
78 { &vop_poll_desc, tmpfs_poll },
79 { &vop_kqfilter_desc, tmpfs_kqfilter },
80 { &vop_revoke_desc, tmpfs_revoke },
81 { &vop_mmap_desc, tmpfs_mmap },
82 { &vop_fsync_desc, tmpfs_fsync },
83 { &vop_seek_desc, tmpfs_seek },
84 { &vop_remove_desc, tmpfs_remove },
85 { &vop_link_desc, tmpfs_link },
86 { &vop_rename_desc, tmpfs_rename },
87 { &vop_mkdir_desc, tmpfs_mkdir },
88 { &vop_rmdir_desc, tmpfs_rmdir },
89 { &vop_symlink_desc, tmpfs_symlink },
90 { &vop_readdir_desc, tmpfs_readdir },
91 { &vop_readlink_desc, tmpfs_readlink },
92 { &vop_abortop_desc, tmpfs_abortop },
93 { &vop_inactive_desc, tmpfs_inactive },
94 { &vop_reclaim_desc, tmpfs_reclaim },
95 { &vop_lock_desc, tmpfs_lock },
96 { &vop_unlock_desc, tmpfs_unlock },
97 { &vop_bmap_desc, tmpfs_bmap },
98 { &vop_strategy_desc, tmpfs_strategy },
99 { &vop_print_desc, tmpfs_print },
100 { &vop_pathconf_desc, tmpfs_pathconf },
101 { &vop_islocked_desc, tmpfs_islocked },
102 { &vop_advlock_desc, tmpfs_advlock },
103 { &vop_bwrite_desc, tmpfs_bwrite },
104 { &vop_getpages_desc, tmpfs_getpages },
105 { &vop_putpages_desc, tmpfs_putpages },
106 #if TMPFS_WHITEOUT
107 { &vop_whiteout_desc, tmpfs_whiteout },
108 #endif
109 { NULL, NULL }
110 };
111
112 const struct vnodeopv_desc tmpfs_vnodeop_opv_desc = {
113 &tmpfs_vnodeop_p, tmpfs_vnodeop_entries
114 };
115
116 /*
117 * tmpfs_lookup: path name traversal routine.
118 *
119 * Arguments: dvp (directory being searched), vpp (result),
120 * cnp (component name - path).
121 *
122 * => Caller holds a reference and lock on dvp.
123 * => We return looked-up vnode (vpp) locked, with a reference held.
124 */
125 int
126 tmpfs_lookup(void *v)
127 {
128 struct vop_lookup_args /* {
129 struct vnode *a_dvp;
130 struct vnode **a_vpp;
131 struct componentname *a_cnp;
132 } */ *ap = v;
133 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
134 struct componentname *cnp = ap->a_cnp;
135 const bool lastcn = (cnp->cn_flags & ISLASTCN) != 0;
136 tmpfs_node_t *dnode, *tnode;
137 tmpfs_dirent_t *de;
138 int error;
139
140 KASSERT(VOP_ISLOCKED(dvp));
141
142 dnode = VP_TO_TMPFS_DIR(dvp);
143 *vpp = NULL;
144
145 /* Check accessibility of directory. */
146 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
147 if (error) {
148 goto out;
149 }
150
151 /*
152 * If requesting the last path component on a read-only file system
153 * with a write operation, deny it.
154 */
155 if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 &&
156 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
157 error = EROFS;
158 goto out;
159 }
160
161 /*
162 * Avoid doing a linear scan of the directory if the requested
163 * directory/name couple is already in the cache.
164 */
165 error = cache_lookup(dvp, vpp, cnp);
166 if (error >= 0) {
167 /* Both cache-hit or an error case. */
168 goto out;
169 }
170
171 if (cnp->cn_flags & ISDOTDOT) {
172 tmpfs_node_t *pnode;
173
174 /*
175 * Lookup of ".." case.
176 */
177 if (lastcn && cnp->cn_nameiop == RENAME) {
178 error = EINVAL;
179 goto out;
180 }
181 KASSERT(dnode->tn_type == VDIR);
182 pnode = dnode->tn_spec.tn_dir.tn_parent;
183 if (pnode == NULL) {
184 error = ENOENT;
185 goto out;
186 }
187
188 /*
189 * Lock the parent tn_vlock before releasing the vnode lock,
190 * and thus prevents parent from disappearing.
191 */
192 mutex_enter(&pnode->tn_vlock);
193 VOP_UNLOCK(dvp);
194
195 /*
196 * Get a vnode of the '..' entry and re-acquire the lock.
197 * Release the tn_vlock.
198 */
199 error = tmpfs_vnode_get(dvp->v_mount, pnode, vpp);
200 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
201 goto out;
202
203 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
204 /*
205 * Lookup of "." case.
206 */
207 if (lastcn && cnp->cn_nameiop == RENAME) {
208 error = EISDIR;
209 goto out;
210 }
211 vref(dvp);
212 *vpp = dvp;
213 error = 0;
214 goto done;
215 }
216
217 /*
218 * Other lookup cases: perform directory scan.
219 */
220 de = tmpfs_dir_lookup(dnode, cnp);
221 if (de == NULL || de->td_node == TMPFS_NODE_WHITEOUT) {
222 /*
223 * The entry was not found in the directory. This is valid
224 * if we are creating or renaming an entry and are working
225 * on the last component of the path name.
226 */
227 if (lastcn && (cnp->cn_nameiop == CREATE ||
228 cnp->cn_nameiop == RENAME)) {
229 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
230 if (error) {
231 goto out;
232 }
233 error = EJUSTRETURN;
234 } else {
235 error = ENOENT;
236 }
237 if (de) {
238 KASSERT(de->td_node == TMPFS_NODE_WHITEOUT);
239 cnp->cn_flags |= ISWHITEOUT;
240 }
241 goto done;
242 }
243
244 tnode = de->td_node;
245
246 /*
247 * If it is not the last path component and found a non-directory
248 * or non-link entry (which may itself be pointing to a directory),
249 * raise an error.
250 */
251 if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) {
252 error = ENOTDIR;
253 goto out;
254 }
255
256 /* Check the permissions. */
257 if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
258 kauth_action_t action = 0;
259
260 /* This is the file-system's decision. */
261 if ((dnode->tn_mode & S_ISTXT) != 0 &&
262 kauth_cred_geteuid(cnp->cn_cred) != dnode->tn_uid &&
263 kauth_cred_geteuid(cnp->cn_cred) != tnode->tn_uid) {
264 error = EPERM;
265 } else {
266 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
267 }
268
269 if (cnp->cn_nameiop == DELETE) {
270 action |= KAUTH_VNODE_DELETE;
271 } else {
272 KASSERT(cnp->cn_nameiop == RENAME);
273 action |= KAUTH_VNODE_RENAME;
274 }
275 error = kauth_authorize_vnode(cnp->cn_cred,
276 action, *vpp, dvp, error);
277 if (error) {
278 goto out;
279 }
280 }
281
282 /* Get a vnode for the matching entry. */
283 mutex_enter(&tnode->tn_vlock);
284 error = tmpfs_vnode_get(dvp->v_mount, tnode, vpp);
285 done:
286 /*
287 * Cache the result, unless request was for creation (as it does
288 * not improve the performance).
289 */
290 if ((cnp->cn_flags & MAKEENTRY) != 0 && cnp->cn_nameiop != CREATE) {
291 cache_enter(dvp, *vpp, cnp);
292 }
293 out:
294 KASSERT((*vpp && VOP_ISLOCKED(*vpp)) || error);
295 KASSERT(VOP_ISLOCKED(dvp));
296
297 return error;
298 }
299
300 int
301 tmpfs_create(void *v)
302 {
303 struct vop_create_args /* {
304 struct vnode *a_dvp;
305 struct vnode **a_vpp;
306 struct componentname *a_cnp;
307 struct vattr *a_vap;
308 } */ *ap = v;
309 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
310 struct componentname *cnp = ap->a_cnp;
311 struct vattr *vap = ap->a_vap;
312
313 KASSERT(VOP_ISLOCKED(dvp));
314 KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
315 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
316 }
317
318 int
319 tmpfs_mknod(void *v)
320 {
321 struct vop_mknod_args /* {
322 struct vnode *a_dvp;
323 struct vnode **a_vpp;
324 struct componentname *a_cnp;
325 struct vattr *a_vap;
326 } */ *ap = v;
327 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
328 struct componentname *cnp = ap->a_cnp;
329 struct vattr *vap = ap->a_vap;
330 enum vtype vt = vap->va_type;
331
332 if (vt != VBLK && vt != VCHR && vt != VFIFO) {
333 vput(dvp);
334 return EINVAL;
335 }
336 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
337 }
338
339 int
340 tmpfs_open(void *v)
341 {
342 struct vop_open_args /* {
343 struct vnode *a_vp;
344 int a_mode;
345 kauth_cred_t a_cred;
346 } */ *ap = v;
347 vnode_t *vp = ap->a_vp;
348 mode_t mode = ap->a_mode;
349 tmpfs_node_t *node;
350
351 KASSERT(VOP_ISLOCKED(vp));
352
353 node = VP_TO_TMPFS_NODE(vp);
354 if (node->tn_links < 1) {
355 /*
356 * The file is still active, but all its names have been
357 * removed (e.g. by a "rmdir $(pwd)"). It cannot be opened
358 * any more, as it is about to be destroyed.
359 */
360 return ENOENT;
361 }
362
363 /* If the file is marked append-only, deny write requests. */
364 if ((node->tn_flags & APPEND) != 0 &&
365 (mode & (FWRITE | O_APPEND)) == FWRITE) {
366 return EPERM;
367 }
368 return 0;
369 }
370
371 int
372 tmpfs_close(void *v)
373 {
374 struct vop_close_args /* {
375 struct vnode *a_vp;
376 int a_fflag;
377 kauth_cred_t a_cred;
378 } */ *ap = v;
379 vnode_t *vp = ap->a_vp;
380
381 KASSERT(VOP_ISLOCKED(vp));
382
383 tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
384 return 0;
385 }
386
387 static int
388 tmpfs_check_possible(vnode_t *vp, tmpfs_node_t *node, mode_t mode)
389 {
390 const bool writing = (mode & VWRITE) != 0;
391
392 switch (vp->v_type) {
393 case VDIR:
394 case VLNK:
395 case VREG:
396 if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
397 return EROFS;
398 }
399 break;
400 case VBLK:
401 case VCHR:
402 case VSOCK:
403 case VFIFO:
404 break;
405 default:
406 return EINVAL;
407 }
408 return (writing && (node->tn_flags & IMMUTABLE) != 0) ? EPERM : 0;
409 }
410
411 static int
412 tmpfs_check_permitted(vnode_t *vp, tmpfs_node_t *node, mode_t mode,
413 kauth_cred_t cred)
414 {
415
416 return genfs_can_access(vp->v_type, node->tn_mode, node->tn_uid,
417 node->tn_gid, mode, cred);
418 }
419
420 int
421 tmpfs_access(void *v)
422 {
423 struct vop_access_args /* {
424 struct vnode *a_vp;
425 int a_mode;
426 kauth_cred_t a_cred;
427 } */ *ap = v;
428 vnode_t *vp = ap->a_vp;
429 mode_t mode = ap->a_mode;
430 kauth_cred_t cred = ap->a_cred;
431 tmpfs_node_t *node;
432 int error;
433
434 KASSERT(VOP_ISLOCKED(vp));
435
436 node = VP_TO_TMPFS_NODE(vp);
437 error = tmpfs_check_possible(vp, node, mode);
438 if (error) {
439 return error;
440 }
441 return kauth_authorize_vnode(cred, kauth_mode_to_action(mode), vp,
442 NULL, tmpfs_check_permitted(vp, node, mode, cred));
443 }
444
445 int
446 tmpfs_getattr(void *v)
447 {
448 struct vop_getattr_args /* {
449 struct vnode *a_vp;
450 struct vattr *a_vap;
451 kauth_cred_t a_cred;
452 } */ *ap = v;
453 vnode_t *vp = ap->a_vp;
454 struct vattr *vap = ap->a_vap;
455 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
456
457 vattr_null(vap);
458
459 tmpfs_update(vp, NULL, NULL, NULL, 0);
460
461 vap->va_type = vp->v_type;
462 vap->va_mode = node->tn_mode;
463 vap->va_nlink = node->tn_links;
464 vap->va_uid = node->tn_uid;
465 vap->va_gid = node->tn_gid;
466 vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
467 vap->va_fileid = node->tn_id;
468 vap->va_size = node->tn_size;
469 vap->va_blocksize = PAGE_SIZE;
470 vap->va_atime = node->tn_atime;
471 vap->va_mtime = node->tn_mtime;
472 vap->va_ctime = node->tn_ctime;
473 vap->va_birthtime = node->tn_birthtime;
474 vap->va_gen = TMPFS_NODE_GEN(node);
475 vap->va_flags = node->tn_flags;
476 vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
477 node->tn_spec.tn_dev.tn_rdev : VNOVAL;
478 vap->va_bytes = round_page(node->tn_size);
479 vap->va_filerev = VNOVAL;
480 vap->va_vaflags = 0;
481 vap->va_spare = VNOVAL; /* XXX */
482
483 return 0;
484 }
485
486 #define GOODTIME(tv) ((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL)
487 /* XXX Should this operation be atomic? I think it should, but code in
488 * XXX other places (e.g., ufs) doesn't seem to be... */
489 int
490 tmpfs_setattr(void *v)
491 {
492 struct vop_setattr_args /* {
493 struct vnode *a_vp;
494 struct vattr *a_vap;
495 kauth_cred_t a_cred;
496 } */ *ap = v;
497 vnode_t *vp = ap->a_vp;
498 struct vattr *vap = ap->a_vap;
499 kauth_cred_t cred = ap->a_cred;
500 lwp_t *l = curlwp;
501 int error = 0;
502
503 KASSERT(VOP_ISLOCKED(vp));
504
505 /* Abort if any unsettable attribute is given. */
506 if (vap->va_type != VNON || vap->va_nlink != VNOVAL ||
507 vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
508 vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) ||
509 vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL ||
510 vap->va_bytes != VNOVAL) {
511 return EINVAL;
512 }
513 if (error == 0 && (vap->va_flags != VNOVAL))
514 error = tmpfs_chflags(vp, vap->va_flags, cred, l);
515
516 if (error == 0 && (vap->va_size != VNOVAL))
517 error = tmpfs_chsize(vp, vap->va_size, cred, l);
518
519 if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
520 error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
521
522 if (error == 0 && (vap->va_mode != VNOVAL))
523 error = tmpfs_chmod(vp, vap->va_mode, cred, l);
524
525 if (error == 0 && (GOODTIME(&vap->va_atime) || GOODTIME(&vap->va_mtime)
526 || GOODTIME(&vap->va_birthtime))) {
527 error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
528 &vap->va_birthtime, vap->va_vaflags, cred, l);
529 if (error == 0)
530 return 0;
531 }
532 tmpfs_update(vp, NULL, NULL, NULL, 0);
533 return error;
534 }
535
536 int
537 tmpfs_read(void *v)
538 {
539 struct vop_read_args /* {
540 struct vnode *a_vp;
541 struct uio *a_uio;
542 int a_ioflag;
543 kauth_cred_t a_cred;
544 } */ *ap = v;
545 vnode_t *vp = ap->a_vp;
546 struct uio *uio = ap->a_uio;
547 const int ioflag = ap->a_ioflag;
548 tmpfs_node_t *node;
549 struct uvm_object *uobj;
550 int error;
551
552 KASSERT(VOP_ISLOCKED(vp));
553
554 if (vp->v_type != VREG) {
555 return EISDIR;
556 }
557 if (uio->uio_offset < 0) {
558 return EINVAL;
559 }
560
561 node = VP_TO_TMPFS_NODE(vp);
562 node->tn_status |= TMPFS_NODE_ACCESSED;
563 uobj = node->tn_spec.tn_reg.tn_aobj;
564 error = 0;
565
566 while (error == 0 && uio->uio_resid > 0) {
567 vsize_t len;
568
569 if (node->tn_size <= uio->uio_offset) {
570 break;
571 }
572 len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
573 if (len == 0) {
574 break;
575 }
576 error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
577 UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
578 }
579 return error;
580 }
581
582 int
583 tmpfs_write(void *v)
584 {
585 struct vop_write_args /* {
586 struct vnode *a_vp;
587 struct uio *a_uio;
588 int a_ioflag;
589 kauth_cred_t a_cred;
590 } */ *ap = v;
591 vnode_t *vp = ap->a_vp;
592 struct uio *uio = ap->a_uio;
593 const int ioflag = ap->a_ioflag;
594 tmpfs_node_t *node;
595 struct uvm_object *uobj;
596 off_t oldsize;
597 bool extended;
598 int error;
599
600 KASSERT(VOP_ISLOCKED(vp));
601
602 node = VP_TO_TMPFS_NODE(vp);
603 oldsize = node->tn_size;
604
605 if (uio->uio_offset < 0 || vp->v_type != VREG) {
606 error = EINVAL;
607 goto out;
608 }
609 if (uio->uio_resid == 0) {
610 error = 0;
611 goto out;
612 }
613 if (ioflag & IO_APPEND) {
614 uio->uio_offset = node->tn_size;
615 }
616
617 extended = uio->uio_offset + uio->uio_resid > node->tn_size;
618 if (extended) {
619 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
620 if (error)
621 goto out;
622 }
623
624 uobj = node->tn_spec.tn_reg.tn_aobj;
625 error = 0;
626 while (error == 0 && uio->uio_resid > 0) {
627 vsize_t len;
628
629 len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
630 if (len == 0) {
631 break;
632 }
633 error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
634 UBC_WRITE | UBC_UNMAP_FLAG(vp));
635 }
636 if (error) {
637 (void)tmpfs_reg_resize(vp, oldsize);
638 }
639
640 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
641 (extended ? TMPFS_NODE_CHANGED : 0);
642 VN_KNOTE(vp, NOTE_WRITE);
643 out:
644 if (error) {
645 KASSERT(oldsize == node->tn_size);
646 } else {
647 KASSERT(uio->uio_resid == 0);
648 }
649 return error;
650 }
651
652 int
653 tmpfs_fsync(void *v)
654 {
655 struct vop_fsync_args /* {
656 struct vnode *a_vp;
657 kauth_cred_t a_cred;
658 int a_flags;
659 off_t a_offlo;
660 off_t a_offhi;
661 struct lwp *a_l;
662 } */ *ap = v;
663 vnode_t *vp = ap->a_vp;
664
665 /* Nothing to do. Just update. */
666 KASSERT(VOP_ISLOCKED(vp));
667 tmpfs_update(vp, NULL, NULL, NULL, 0);
668 return 0;
669 }
670
671 /*
672 * tmpfs_remove: unlink a file.
673 *
674 * => Both directory (dvp) and file (vp) are locked.
675 * => We unlock and drop the reference on both.
676 */
677 int
678 tmpfs_remove(void *v)
679 {
680 struct vop_remove_args /* {
681 struct vnode *a_dvp;
682 struct vnode *a_vp;
683 struct componentname *a_cnp;
684 } */ *ap = v;
685 vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp;
686 tmpfs_node_t *node;
687 tmpfs_dirent_t *de;
688 int error;
689
690 KASSERT(VOP_ISLOCKED(dvp));
691 KASSERT(VOP_ISLOCKED(vp));
692
693 if (vp->v_type == VDIR) {
694 error = EPERM;
695 goto out;
696 }
697 node = VP_TO_TMPFS_NODE(vp);
698
699 /* Files marked as immutable or append-only cannot be deleted. */
700 if (node->tn_flags & (IMMUTABLE | APPEND)) {
701 error = EPERM;
702 goto out;
703 }
704
705 /* Lookup the directory entry (check the cached hint first). */
706 de = tmpfs_dir_cached(node);
707 if (de == NULL) {
708 tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
709 struct componentname *cnp = ap->a_cnp;
710 de = tmpfs_dir_lookup(dnode, cnp);
711 }
712 KASSERT(de && de->td_node == node);
713
714 /*
715 * Remove the entry from the directory (drops the link count) and
716 * destroy it. Note: the inode referred by it will not be destroyed
717 * until the vnode is reclaimed/recycled.
718 */
719 tmpfs_dir_detach(dvp, de);
720 tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de);
721 error = 0;
722 out:
723 /* Drop the references and unlock the vnodes. */
724 vput(vp);
725 if (dvp == vp) {
726 vrele(dvp);
727 } else {
728 vput(dvp);
729 }
730 return error;
731 }
732
733 /*
734 * tmpfs_link: create a hard link.
735 */
736 int
737 tmpfs_link(void *v)
738 {
739 struct vop_link_args /* {
740 struct vnode *a_dvp;
741 struct vnode *a_vp;
742 struct componentname *a_cnp;
743 } */ *ap = v;
744 vnode_t *dvp = ap->a_dvp;
745 vnode_t *vp = ap->a_vp;
746 struct componentname *cnp = ap->a_cnp;
747 tmpfs_node_t *dnode, *node;
748 tmpfs_dirent_t *de;
749 int error;
750
751 KASSERT(dvp != vp);
752 KASSERT(VOP_ISLOCKED(dvp));
753 KASSERT(vp->v_type != VDIR);
754 KASSERT(dvp->v_mount == vp->v_mount);
755
756 dnode = VP_TO_TMPFS_DIR(dvp);
757 node = VP_TO_TMPFS_NODE(vp);
758
759 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
760
761 /* Check for maximum number of links limit. */
762 if (node->tn_links == LINK_MAX) {
763 error = EMLINK;
764 goto out;
765 }
766 KASSERT(node->tn_links < LINK_MAX);
767
768 /* We cannot create links of files marked immutable or append-only. */
769 if (node->tn_flags & (IMMUTABLE | APPEND)) {
770 error = EPERM;
771 goto out;
772 }
773
774 /* Allocate a new directory entry to represent the inode. */
775 error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount),
776 cnp->cn_nameptr, cnp->cn_namelen, &de);
777 if (error) {
778 goto out;
779 }
780
781 /*
782 * Insert the entry into the directory.
783 * It will increase the inode link count.
784 */
785 tmpfs_dir_attach(dvp, de, node);
786
787 /* Update the timestamps and trigger the event. */
788 if (node->tn_vnode) {
789 VN_KNOTE(node->tn_vnode, NOTE_LINK);
790 }
791 node->tn_status |= TMPFS_NODE_CHANGED;
792 tmpfs_update(vp, NULL, NULL, NULL, 0);
793 error = 0;
794 out:
795 VOP_UNLOCK(vp);
796 vput(dvp);
797 return error;
798 }
799
800 /*
801 * tmpfs_parentcheck_p: check if 'lower' is a descendent of 'upper'.
802 *
803 * => Returns 'true' if parent, and 'false' otherwise.
804 */
805 static inline bool
806 tmpfs_parentcheck_p(tmpfs_node_t *lower, tmpfs_node_t *upper)
807 {
808 tmpfs_node_t *un = lower;
809
810 while (un != un->tn_spec.tn_dir.tn_parent) {
811 KASSERT(un->tn_type == VDIR);
812 if (un == upper) {
813 return true;
814 }
815 un = un->tn_spec.tn_dir.tn_parent;
816 }
817 return false;
818 }
819
820 /*
821 * tmpfs_rename: rename routine.
822 *
823 * Arguments: fdvp (from-parent vnode), fvp (from-leaf), tdvp (to-parent)
824 * and tvp (to-leaf), if exists (NULL if not).
825 *
826 * => Caller holds a reference on fdvp and fvp, they are unlocked.
827 * Note: fdvp and fvp can refer to the same object (i.e. when it is root).
828 *
829 * => Both tdvp and tvp are referenced and locked. It is our responsibility
830 * to release the references and unlock them (or destroy).
831 */
832 int
833 tmpfs_rename(void *v)
834 {
835 struct vop_rename_args /* {
836 struct vnode *a_fdvp;
837 struct vnode *a_fvp;
838 struct componentname *a_fcnp;
839 struct vnode *a_tdvp;
840 struct vnode *a_tvp;
841 struct componentname *a_tcnp;
842 } */ *ap = v;
843 vnode_t *fdvp = ap->a_fdvp;
844 vnode_t *fvp = ap->a_fvp;
845 struct componentname *fcnp = ap->a_fcnp;
846 vnode_t *tdvp = ap->a_tdvp;
847 vnode_t *tvp = ap->a_tvp;
848 struct componentname *tcnp = ap->a_tcnp;
849 tmpfs_node_t *fdnode, *fnode, *tnode, *tdnode;
850 tmpfs_dirent_t *de;
851 tmpfs_mount_t *tmp;
852 size_t namelen;
853 char *newname;
854 int error;
855
856 KASSERT(VOP_ISLOCKED(tdvp));
857 KASSERT(tvp == NULL || VOP_ISLOCKED(tvp) == LK_EXCLUSIVE);
858 KASSERT((fcnp->cn_flags & ISDOTDOT) == 0);
859 KASSERT((tcnp->cn_flags & ISDOTDOT) == 0);
860
861 newname = NULL;
862 namelen = 0;
863 tmp = NULL;
864
865 /* Disallow cross-device renames. */
866 if (fvp->v_mount != tdvp->v_mount ||
867 (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
868 error = EXDEV;
869 goto out_unlocked;
870 }
871
872 fnode = VP_TO_TMPFS_NODE(fvp);
873 fdnode = VP_TO_TMPFS_DIR(fdvp);
874 tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
875 tdnode = VP_TO_TMPFS_DIR(tdvp);
876 tmp = VFS_TO_TMPFS(tdvp->v_mount);
877
878 if (fdvp == tvp) {
879 error = 0;
880 goto out_unlocked;
881 }
882
883 /* Allocate memory, if necessary, for a new name. */
884 namelen = tcnp->cn_namelen;
885 if (tmpfs_strname_neqlen(fcnp, tcnp)) {
886 newname = tmpfs_strname_alloc(tmp, namelen);
887 if (newname == NULL) {
888 error = ENOSPC;
889 goto out_unlocked;
890 }
891 }
892
893 /* XXX: Lock order violation! */
894 if (fdvp != tdvp) {
895 vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
896 }
897 if (fvp != tvp) {
898 vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
899 }
900
901 /* If the inode we were renaming has scarpered, just give up. */
902 de = tmpfs_dir_lookup(fdnode, fcnp);
903 if (de == NULL || de->td_node != fnode) {
904 error = ENOENT;
905 goto out;
906 }
907
908 /*
909 * If source and target is the same vnode - it is either invalid
910 * rename of a directory, or a hard link. Remove the source link,
911 * if the later.
912 */
913 if (fvp == tvp) {
914 if (fvp->v_type == VDIR) {
915 error = EINVAL;
916 goto out;
917 }
918 /*
919 * Detach and free the directory entry. Drops the link
920 * count on the inode.
921 */
922 KASSERT(fnode == tnode);
923 tmpfs_dir_detach(fdvp, de);
924 tmpfs_free_dirent(tmp, de);
925 goto out_ok;
926 }
927
928 /* If replacing an existing entry, ensure we can do the operation. */
929 if (tvp != NULL) {
930 KASSERT(tnode != NULL);
931 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
932 if (tnode->tn_size > 0) {
933 error = ENOTEMPTY;
934 goto out;
935 }
936 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
937 error = ENOTDIR;
938 goto out;
939 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
940 error = EISDIR;
941 goto out;
942 } else {
943 KASSERT(fnode->tn_type != VDIR);
944 KASSERT(tnode->tn_type != VDIR);
945 }
946 }
947
948 /* Are we moving the inode to a different directory? */
949 if (fdvp != tdvp) {
950 /*
951 * If we are moving a directory - ensure that it is not
952 * parent of a target directory. Otherwise, it would
953 * result in stale nodes.
954 */
955 if (fnode->tn_type == VDIR &&
956 tmpfs_parentcheck_p(tdnode, fnode)) {
957 error = EINVAL;
958 goto out;
959 }
960
961 /*
962 * Perform the move: detach from the source directory and
963 * attach into the target directory.
964 */
965 tmpfs_dir_detach(fdvp, de);
966 tmpfs_dir_attach(tdvp, de, fnode);
967
968 } else if (tvp == NULL) {
969 /* Trigger the event, if not overwriting. */
970 VN_KNOTE(tdvp, NOTE_WRITE);
971 }
972
973 /* Are we overwriting the entry? */
974 if (tvp != NULL) {
975 tmpfs_dirent_t *tde;
976
977 tde = tmpfs_dir_cached(tnode);
978 if (tde == NULL) {
979 tde = tmpfs_dir_lookup(tdnode, tcnp);
980 }
981 KASSERT(tde && tde->td_node == tnode);
982 KASSERT(tnode->tn_type == fnode->tn_type);
983
984 /*
985 * Remove and destroy the directory entry on the target
986 * directory, since we overwrite it.
987 */
988 tmpfs_dir_detach(tdvp, tde);
989 tmpfs_free_dirent(tmp, tde);
990 }
991
992 /* If the name has changed, update directory entry. */
993 if (newname != NULL) {
994 KASSERT(tcnp->cn_namelen < MAXNAMLEN);
995
996 tmpfs_strname_free(tmp, de->td_name, de->td_namelen);
997 de->td_namelen = (uint16_t)namelen;
998 memcpy(newname, tcnp->cn_nameptr, namelen);
999 de->td_name = newname;
1000 newname = NULL;
1001
1002 fnode->tn_status |= TMPFS_NODE_CHANGED;
1003 tdnode->tn_status |= TMPFS_NODE_MODIFIED;
1004 }
1005 out_ok:
1006 /* Trigger the rename event. */
1007 VN_KNOTE(fvp, NOTE_RENAME);
1008 error = 0;
1009 out:
1010 if (fdvp != tdvp) {
1011 VOP_UNLOCK(fdvp);
1012 }
1013 if (fvp != tvp) {
1014 VOP_UNLOCK(fvp);
1015 }
1016 out_unlocked:
1017 /* Release target nodes. */
1018 if (tdvp == tvp) {
1019 vrele(tdvp);
1020 } else {
1021 vput(tdvp);
1022 }
1023 if (tvp) {
1024 vput(tvp);
1025 }
1026
1027 /* Release source nodes. */
1028 vrele(fdvp);
1029 vrele(fvp);
1030
1031 if (newname != NULL) {
1032 tmpfs_strname_free(tmp, newname, namelen);
1033 }
1034 return error;
1035 }
1036
1037 int
1038 tmpfs_mkdir(void *v)
1039 {
1040 struct vop_mkdir_args /* {
1041 struct vnode *a_dvp;
1042 struct vnode **a_vpp;
1043 struct componentname *a_cnp;
1044 struct vattr *a_vap;
1045 } */ *ap = v;
1046 vnode_t *dvp = ap->a_dvp;
1047 vnode_t **vpp = ap->a_vpp;
1048 struct componentname *cnp = ap->a_cnp;
1049 struct vattr *vap = ap->a_vap;
1050
1051 KASSERT(vap->va_type == VDIR);
1052 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
1053 }
1054
1055 int
1056 tmpfs_rmdir(void *v)
1057 {
1058 struct vop_rmdir_args /* {
1059 struct vnode *a_dvp;
1060 struct vnode *a_vp;
1061 struct componentname *a_cnp;
1062 } */ *ap = v;
1063 vnode_t *dvp = ap->a_dvp;
1064 vnode_t *vp = ap->a_vp;
1065 tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
1066 tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
1067 tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp);
1068 tmpfs_dirent_t *de;
1069 int error = 0;
1070
1071 KASSERT(VOP_ISLOCKED(dvp));
1072 KASSERT(VOP_ISLOCKED(vp));
1073 KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
1074
1075 /*
1076 * Directories with more than two entries ('.' and '..') cannot
1077 * be removed.
1078 */
1079 if (node->tn_size > 0) {
1080 error = ENOTEMPTY;
1081 goto out;
1082 }
1083
1084 /* Lookup the directory entry (check the cached hint first). */
1085 de = tmpfs_dir_cached(node);
1086 if (de == NULL) {
1087 struct componentname *cnp = ap->a_cnp;
1088 de = tmpfs_dir_lookup(dnode, cnp);
1089 }
1090 KASSERT(de && de->td_node == node);
1091
1092 /* Check flags to see if we are allowed to remove the directory. */
1093 if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
1094 error = EPERM;
1095 goto out;
1096 }
1097
1098 /* Decrement the link count for the virtual '.' entry. */
1099 node->tn_links--;
1100 node->tn_status |= TMPFS_NODE_STATUSALL;
1101
1102 /* Detach the directory entry from the directory. */
1103 tmpfs_dir_detach(dvp, de);
1104
1105 /* Purge the cache for parent. */
1106 cache_purge(dvp);
1107
1108 /*
1109 * Destroy the directory entry. Note: the inode referred by it
1110 * will not be destroyed until the vnode is reclaimed.
1111 */
1112 tmpfs_free_dirent(tmp, de);
1113 KASSERT(node->tn_links == 0);
1114 out:
1115 /* Release the nodes. */
1116 vput(dvp);
1117 vput(vp);
1118 return error;
1119 }
1120
1121 int
1122 tmpfs_symlink(void *v)
1123 {
1124 struct vop_symlink_args /* {
1125 struct vnode *a_dvp;
1126 struct vnode **a_vpp;
1127 struct componentname *a_cnp;
1128 struct vattr *a_vap;
1129 char *a_target;
1130 } */ *ap = v;
1131 vnode_t *dvp = ap->a_dvp;
1132 vnode_t **vpp = ap->a_vpp;
1133 struct componentname *cnp = ap->a_cnp;
1134 struct vattr *vap = ap->a_vap;
1135 char *target = ap->a_target;
1136
1137 KASSERT(vap->va_type == VLNK);
1138 return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
1139 }
1140
1141 int
1142 tmpfs_readdir(void *v)
1143 {
1144 struct vop_readdir_args /* {
1145 struct vnode *a_vp;
1146 struct uio *a_uio;
1147 kauth_cred_t a_cred;
1148 int *a_eofflag;
1149 off_t **a_cookies;
1150 int *ncookies;
1151 } */ *ap = v;
1152 vnode_t *vp = ap->a_vp;
1153 struct uio *uio = ap->a_uio;
1154 int *eofflag = ap->a_eofflag;
1155 off_t **cookies = ap->a_cookies;
1156 int *ncookies = ap->a_ncookies;
1157 off_t startoff, cnt;
1158 tmpfs_node_t *node;
1159 int error;
1160
1161 KASSERT(VOP_ISLOCKED(vp));
1162
1163 /* This operation only makes sense on directory nodes. */
1164 if (vp->v_type != VDIR) {
1165 return ENOTDIR;
1166 }
1167 node = VP_TO_TMPFS_DIR(vp);
1168 startoff = uio->uio_offset;
1169 cnt = 0;
1170
1171 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
1172 error = tmpfs_dir_getdotdent(node, uio);
1173 if (error != 0) {
1174 if (error == -1)
1175 error = 0;
1176 goto out;
1177 }
1178 cnt++;
1179 }
1180 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1181 error = tmpfs_dir_getdotdotdent(node, uio);
1182 if (error != 0) {
1183 if (error == -1)
1184 error = 0;
1185 goto out;
1186 }
1187 cnt++;
1188 }
1189 error = tmpfs_dir_getdents(node, uio, &cnt);
1190 if (error == -1) {
1191 error = 0;
1192 }
1193 KASSERT(error >= 0);
1194 out:
1195 if (eofflag != NULL) {
1196 *eofflag = (!error && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
1197 }
1198 if (error || cookies == NULL || ncookies == NULL) {
1199 return error;
1200 }
1201
1202 /* Update NFS-related variables, if any. */
1203 off_t i, off = startoff;
1204 tmpfs_dirent_t *de = NULL;
1205
1206 *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
1207 *ncookies = cnt;
1208
1209 for (i = 0; i < cnt; i++) {
1210 KASSERT(off != TMPFS_DIRCOOKIE_EOF);
1211 if (off != TMPFS_DIRCOOKIE_DOT) {
1212 if (off == TMPFS_DIRCOOKIE_DOTDOT) {
1213 de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
1214 } else if (de != NULL) {
1215 de = TAILQ_NEXT(de, td_entries);
1216 } else {
1217 de = tmpfs_dir_lookupbycookie(node, off);
1218 KASSERT(de != NULL);
1219 de = TAILQ_NEXT(de, td_entries);
1220 }
1221 if (de == NULL) {
1222 off = TMPFS_DIRCOOKIE_EOF;
1223 } else {
1224 off = tmpfs_dircookie(de);
1225 }
1226 } else {
1227 off = TMPFS_DIRCOOKIE_DOTDOT;
1228 }
1229 (*cookies)[i] = off;
1230 }
1231 KASSERT(uio->uio_offset == off);
1232 return error;
1233 }
1234
1235 int
1236 tmpfs_readlink(void *v)
1237 {
1238 struct vop_readlink_args /* {
1239 struct vnode *a_vp;
1240 struct uio *a_uio;
1241 kauth_cred_t a_cred;
1242 } */ *ap = v;
1243 vnode_t *vp = ap->a_vp;
1244 struct uio *uio = ap->a_uio;
1245 tmpfs_node_t *node;
1246 int error;
1247
1248 KASSERT(VOP_ISLOCKED(vp));
1249 KASSERT(uio->uio_offset == 0);
1250 KASSERT(vp->v_type == VLNK);
1251
1252 node = VP_TO_TMPFS_NODE(vp);
1253 error = uiomove(node->tn_spec.tn_lnk.tn_link,
1254 MIN(node->tn_size, uio->uio_resid), uio);
1255 node->tn_status |= TMPFS_NODE_ACCESSED;
1256
1257 return error;
1258 }
1259
1260 int
1261 tmpfs_inactive(void *v)
1262 {
1263 struct vop_inactive_args /* {
1264 struct vnode *a_vp;
1265 bool *a_recycle;
1266 } */ *ap = v;
1267 vnode_t *vp = ap->a_vp;
1268 tmpfs_node_t *node;
1269
1270 KASSERT(VOP_ISLOCKED(vp));
1271
1272 node = VP_TO_TMPFS_NODE(vp);
1273 *ap->a_recycle = (node->tn_links == 0);
1274 VOP_UNLOCK(vp);
1275
1276 return 0;
1277 }
1278
1279 int
1280 tmpfs_reclaim(void *v)
1281 {
1282 struct vop_reclaim_args /* {
1283 struct vnode *a_vp;
1284 } */ *ap = v;
1285 vnode_t *vp = ap->a_vp;
1286 tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
1287 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1288 bool racing;
1289
1290 /* Disassociate inode from vnode. */
1291 mutex_enter(&node->tn_vlock);
1292 node->tn_vnode = NULL;
1293 vp->v_data = NULL;
1294 /* Check if tmpfs_vnode_get() is racing with us. */
1295 racing = TMPFS_NODE_RECLAIMING(node);
1296 mutex_exit(&node->tn_vlock);
1297
1298 /*
1299 * If inode is not referenced, i.e. no links, then destroy it.
1300 * Note: if racing - inode is about to get a new vnode, leave it.
1301 */
1302 if (node->tn_links == 0 && !racing) {
1303 tmpfs_free_node(tmp, node);
1304 }
1305 return 0;
1306 }
1307
1308 int
1309 tmpfs_pathconf(void *v)
1310 {
1311 struct vop_pathconf_args /* {
1312 struct vnode *a_vp;
1313 int a_name;
1314 register_t *a_retval;
1315 } */ *ap = v;
1316 const int name = ap->a_name;
1317 register_t *retval = ap->a_retval;
1318 int error = 0;
1319
1320 switch (name) {
1321 case _PC_LINK_MAX:
1322 *retval = LINK_MAX;
1323 break;
1324 case _PC_NAME_MAX:
1325 *retval = NAME_MAX;
1326 break;
1327 case _PC_PATH_MAX:
1328 *retval = PATH_MAX;
1329 break;
1330 case _PC_PIPE_BUF:
1331 *retval = PIPE_BUF;
1332 break;
1333 case _PC_CHOWN_RESTRICTED:
1334 *retval = 1;
1335 break;
1336 case _PC_NO_TRUNC:
1337 *retval = 1;
1338 break;
1339 case _PC_SYNC_IO:
1340 *retval = 1;
1341 break;
1342 case _PC_FILESIZEBITS:
1343 *retval = sizeof(off_t) * CHAR_BIT;
1344 break;
1345 default:
1346 error = EINVAL;
1347 }
1348 return error;
1349 }
1350
1351 int
1352 tmpfs_advlock(void *v)
1353 {
1354 struct vop_advlock_args /* {
1355 struct vnode *a_vp;
1356 void * a_id;
1357 int a_op;
1358 struct flock *a_fl;
1359 int a_flags;
1360 } */ *ap = v;
1361 vnode_t *vp = ap->a_vp;
1362 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1363
1364 return lf_advlock(v, &node->tn_lockf, node->tn_size);
1365 }
1366
1367 int
1368 tmpfs_getpages(void *v)
1369 {
1370 struct vop_getpages_args /* {
1371 struct vnode *a_vp;
1372 voff_t a_offset;
1373 struct vm_page **a_m;
1374 int *a_count;
1375 int a_centeridx;
1376 vm_prot_t a_access_type;
1377 int a_advice;
1378 int a_flags;
1379 } */ * const ap = v;
1380 vnode_t *vp = ap->a_vp;
1381 const voff_t offset = ap->a_offset;
1382 struct vm_page **pgs = ap->a_m;
1383 const int centeridx = ap->a_centeridx;
1384 const vm_prot_t access_type = ap->a_access_type;
1385 const int advice = ap->a_advice;
1386 const int flags = ap->a_flags;
1387 int error, npages = *ap->a_count;
1388 tmpfs_node_t *node;
1389 struct uvm_object *uobj;
1390
1391 KASSERT(vp->v_type == VREG);
1392 KASSERT(mutex_owned(vp->v_interlock));
1393
1394 node = VP_TO_TMPFS_NODE(vp);
1395 uobj = node->tn_spec.tn_reg.tn_aobj;
1396
1397 /*
1398 * Currently, PGO_PASTEOF is not supported.
1399 */
1400 if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
1401 if ((flags & PGO_LOCKED) == 0)
1402 mutex_exit(vp->v_interlock);
1403 return EINVAL;
1404 }
1405
1406 if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
1407 npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
1408 }
1409
1410 if ((flags & PGO_LOCKED) != 0)
1411 return EBUSY;
1412
1413 if ((flags & PGO_NOTIMESTAMP) == 0) {
1414 if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1415 node->tn_status |= TMPFS_NODE_ACCESSED;
1416
1417 if ((access_type & VM_PROT_WRITE) != 0)
1418 node->tn_status |= TMPFS_NODE_MODIFIED;
1419 }
1420
1421 /*
1422 * Invoke the pager.
1423 *
1424 * Clean the array of pages before. XXX: PR/32166
1425 * Note that vnode lock is shared with underlying UVM object.
1426 */
1427 if (pgs) {
1428 memset(pgs, 0, sizeof(struct vm_pages *) * npages);
1429 }
1430 KASSERT(vp->v_interlock == uobj->vmobjlock);
1431
1432 error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, centeridx,
1433 access_type, advice, flags | PGO_ALLPAGES);
1434
1435 #if defined(DEBUG)
1436 if (!error && pgs) {
1437 for (int i = 0; i < npages; i++) {
1438 KASSERT(pgs[i] != NULL);
1439 }
1440 }
1441 #endif
1442 return error;
1443 }
1444
1445 int
1446 tmpfs_putpages(void *v)
1447 {
1448 struct vop_putpages_args /* {
1449 struct vnode *a_vp;
1450 voff_t a_offlo;
1451 voff_t a_offhi;
1452 int a_flags;
1453 } */ * const ap = v;
1454 vnode_t *vp = ap->a_vp;
1455 const voff_t offlo = ap->a_offlo;
1456 const voff_t offhi = ap->a_offhi;
1457 const int flags = ap->a_flags;
1458 tmpfs_node_t *node;
1459 struct uvm_object *uobj;
1460 int error;
1461
1462 KASSERT(mutex_owned(vp->v_interlock));
1463
1464 if (vp->v_type != VREG) {
1465 mutex_exit(vp->v_interlock);
1466 return 0;
1467 }
1468
1469 node = VP_TO_TMPFS_NODE(vp);
1470 uobj = node->tn_spec.tn_reg.tn_aobj;
1471
1472 KASSERT(vp->v_interlock == uobj->vmobjlock);
1473 error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags);
1474
1475 /* XXX mtime */
1476
1477 return error;
1478 }
1479
1480 #ifdef TMPFS_WHITEOUT
1481 int
1482 tmpfs_whiteout(void *v)
1483 {
1484 struct vop_whiteout_args /* {
1485 struct vnode *a_dvp;
1486 struct componentname *a_cnp;
1487 int a_flags;
1488 } */ *ap = v;
1489 vnode_t *dvp = ap->a_dvp;
1490 struct componentname *cnp = ap->a_cnp;
1491 const int flags = ap->a_flags;
1492 tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
1493 tmpfs_dirent_t *de;
1494 int error;
1495
1496 switch (flags) {
1497 case LOOKUP:
1498 break;
1499 case CREATE:
1500 error = tmpfs_alloc_dirent(tmp, cnp->cn_nameptr,
1501 cnp->cn_namelen, &de);
1502 if (error)
1503 return error;
1504 tmpfs_dir_attach(dvp, de, TMPFS_NODE_WHITEOUT);
1505 break;
1506 case DELETE:
1507 cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */
1508 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), cnp);
1509 if (de == NULL)
1510 return ENOENT;
1511 tmpfs_dir_detach(dvp, de);
1512 tmpfs_free_dirent(tmp, de);
1513 break;
1514 }
1515 return 0;
1516 }
1517 #endif
1518
1519 int
1520 tmpfs_print(void *v)
1521 {
1522 struct vop_print_args /* {
1523 struct vnode *a_vp;
1524 } */ *ap = v;
1525 vnode_t *vp = ap->a_vp;
1526 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1527
1528 printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n"
1529 "\tmode 0%o, owner %d, group %d, size %" PRIdMAX ", status 0x%x",
1530 node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid,
1531 node->tn_gid, (uintmax_t)node->tn_size, node->tn_status);
1532 if (vp->v_type == VFIFO) {
1533 VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v);
1534 }
1535 printf("\n");
1536 return 0;
1537 }
1538