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