Annotation of src/sys/fs/tmpfs/tmpfs_subr.c, Revision 1.49
1.49 ! yamt 1: /* $NetBSD: tmpfs_subr.c,v 1.48 2008/06/19 19:03:44 christos Exp $ */
1.1 jmmv 2:
3: /*
1.43 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.8 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: /*
1.2 jmmv 34: * Efficient memory file system supporting functions.
1.1 jmmv 35: */
36:
37: #include <sys/cdefs.h>
1.49 ! yamt 38: __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.48 2008/06/19 19:03:44 christos Exp $");
1.1 jmmv 39:
40: #include <sys/param.h>
41: #include <sys/dirent.h>
42: #include <sys/event.h>
1.43 ad 43: #include <sys/kmem.h>
1.1 jmmv 44: #include <sys/mount.h>
45: #include <sys/namei.h>
46: #include <sys/time.h>
47: #include <sys/stat.h>
48: #include <sys/systm.h>
49: #include <sys/swap.h>
50: #include <sys/vnode.h>
1.20 christos 51: #include <sys/kauth.h>
1.35 ad 52: #include <sys/proc.h>
1.43 ad 53: #include <sys/atomic.h>
1.1 jmmv 54:
55: #include <uvm/uvm.h>
56:
57: #include <miscfs/specfs/specdev.h>
58: #include <fs/tmpfs/tmpfs.h>
59: #include <fs/tmpfs/tmpfs_fifoops.h>
60: #include <fs/tmpfs/tmpfs_specops.h>
61: #include <fs/tmpfs/tmpfs_vnops.h>
62:
63: /* --------------------------------------------------------------------- */
64:
1.8 jmmv 65: /*
66: * Allocates a new node of type 'type' inside the 'tmp' mount point, with
67: * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
68: * using the credentials of the process 'p'.
69: *
70: * If the node type is set to 'VDIR', then the parent parameter must point
71: * to the parent directory of the node being created. It may only be NULL
72: * while allocating the root node.
73: *
74: * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
75: * specifies the device the node represents.
76: *
77: * If the node type is set to 'VLNK', then the parameter target specifies
78: * the file name of the target file for the symbolic link that is being
79: * created.
80: *
81: * Note that new nodes are retrieved from the available list if it has
82: * items or, if it is empty, from the node pool as long as there is enough
83: * space to create them.
84: *
85: * Returns zero on success or an appropriate error code on failure.
86: */
1.1 jmmv 87: int
88: tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
89: uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
1.42 pooka 90: char *target, dev_t rdev, struct tmpfs_node **node)
1.1 jmmv 91: {
92: struct tmpfs_node *nnode;
93:
1.2 jmmv 94: /* If the root directory of the 'tmp' file system is not yet
1.1 jmmv 95: * allocated, this must be the request to do it. */
96: KASSERT(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
97:
98: KASSERT(IFF(type == VLNK, target != NULL));
99: KASSERT(IFF(type == VBLK || type == VCHR, rdev != VNOVAL));
100:
101: KASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL);
102:
103: nnode = NULL;
1.43 ad 104: if (atomic_inc_uint_nv(&tmp->tm_nodes_cnt) >= tmp->tm_nodes_max) {
105: atomic_dec_uint(&tmp->tm_nodes_cnt);
106: return ENOSPC;
107: }
1.1 jmmv 108:
1.43 ad 109: nnode = (struct tmpfs_node *)TMPFS_POOL_GET(&tmp->tm_node_pool, 0);
110: if (nnode == NULL) {
111: atomic_dec_uint(&tmp->tm_nodes_cnt);
112: return ENOSPC;
1.1 jmmv 113: }
1.43 ad 114:
115: /*
116: * XXX Where the pool is backed by a map larger than (4GB *
117: * sizeof(*nnode)), this may produce duplicate inode numbers
118: * for applications that do not understand 64-bit ino_t.
119: */
120: nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));
121: nnode->tn_gen = arc4random();
1.1 jmmv 122:
123: /* Generic initialization. */
124: nnode->tn_type = type;
125: nnode->tn_size = 0;
126: nnode->tn_status = 0;
127: nnode->tn_flags = 0;
128: nnode->tn_links = 0;
1.21 kardel 129: getnanotime(&nnode->tn_atime);
1.1 jmmv 130: nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime =
131: nnode->tn_atime;
132: nnode->tn_uid = uid;
133: nnode->tn_gid = gid;
134: nnode->tn_mode = mode;
1.11 jmmv 135: nnode->tn_lockf = NULL;
1.1 jmmv 136: nnode->tn_vnode = NULL;
137:
138: /* Type-specific initialization. */
139: switch (nnode->tn_type) {
140: case VBLK:
141: case VCHR:
1.18 jmmv 142: nnode->tn_spec.tn_dev.tn_rdev = rdev;
1.1 jmmv 143: break;
144:
145: case VDIR:
1.18 jmmv 146: TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
147: nnode->tn_spec.tn_dir.tn_parent =
148: (parent == NULL) ? nnode : parent;
149: nnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
150: nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
1.1 jmmv 151: nnode->tn_links++;
152: break;
153:
154: case VFIFO:
155: /* FALLTHROUGH */
156: case VSOCK:
157: break;
158:
159: case VLNK:
160: KASSERT(strlen(target) < MAXPATHLEN);
1.7 yamt 161: nnode->tn_size = strlen(target);
1.18 jmmv 162: nnode->tn_spec.tn_lnk.tn_link =
163: tmpfs_str_pool_get(&tmp->tm_str_pool, nnode->tn_size, 0);
164: if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
1.43 ad 165: atomic_dec_uint(&tmp->tm_nodes_cnt);
166: TMPFS_POOL_PUT(&tmp->tm_node_pool, nnode);
1.1 jmmv 167: return ENOSPC;
168: }
1.18 jmmv 169: memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size);
1.1 jmmv 170: break;
171:
172: case VREG:
1.18 jmmv 173: nnode->tn_spec.tn_reg.tn_aobj =
174: uao_create(INT32_MAX - PAGE_SIZE, 0);
175: nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
1.1 jmmv 176: break;
177:
178: default:
179: KASSERT(0);
180: }
181:
1.43 ad 182: mutex_init(&nnode->tn_vlock, MUTEX_DEFAULT, IPL_NONE);
183:
184: mutex_enter(&tmp->tm_lock);
185: LIST_INSERT_HEAD(&tmp->tm_nodes, nnode, tn_entries);
186: mutex_exit(&tmp->tm_lock);
187:
1.1 jmmv 188: *node = nnode;
189: return 0;
190: }
191:
192: /* --------------------------------------------------------------------- */
193:
1.8 jmmv 194: /*
195: * Destroys the node pointed to by node from the file system 'tmp'.
196: * If the node does not belong to the given mount point, the results are
197: * unpredicted.
198: *
199: * If the node references a directory; no entries are allowed because
200: * their removal could need a recursive algorithm, something forbidden in
201: * kernel space. Furthermore, there is not need to provide such
202: * functionality (recursive removal) because the only primitives offered
203: * to the user are the removal of empty directories and the deletion of
204: * individual files.
205: *
206: * Note that nodes are not really deleted; in fact, when a node has been
207: * allocated, it cannot be deleted during the whole life of the file
208: * system. Instead, they are moved to the available list and remain there
209: * until reused.
210: */
1.1 jmmv 211: void
212: tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
213: {
1.43 ad 214:
215: if (node->tn_type == VREG) {
216: atomic_add_int(&tmp->tm_pages_used,
217: -node->tn_spec.tn_reg.tn_aobj_pages);
218: }
219: atomic_dec_uint(&tmp->tm_nodes_cnt);
220: mutex_enter(&tmp->tm_lock);
221: LIST_REMOVE(node, tn_entries);
222: mutex_exit(&tmp->tm_lock);
1.1 jmmv 223:
1.40 ad 224: switch (node->tn_type) {
1.1 jmmv 225: case VLNK:
1.18 jmmv 226: tmpfs_str_pool_put(&tmp->tm_str_pool,
227: node->tn_spec.tn_lnk.tn_link, node->tn_size);
1.1 jmmv 228: break;
229:
230: case VREG:
1.18 jmmv 231: if (node->tn_spec.tn_reg.tn_aobj != NULL)
232: uao_detach(node->tn_spec.tn_reg.tn_aobj);
1.1 jmmv 233: break;
234:
235: default:
236: break;
237: }
238:
1.43 ad 239: mutex_destroy(&node->tn_vlock);
240: TMPFS_POOL_PUT(&tmp->tm_node_pool, node);
1.1 jmmv 241: }
242:
243: /* --------------------------------------------------------------------- */
244:
1.8 jmmv 245: /*
246: * Allocates a new directory entry for the node node with a name of name.
247: * The new directory entry is returned in *de.
248: *
249: * The link count of node is increased by one to reflect the new object
1.29 jmmv 250: * referencing it. This takes care of notifying kqueue listeners about
251: * this change.
1.8 jmmv 252: *
253: * Returns zero on success or an appropriate error code on failure.
254: */
1.1 jmmv 255: int
256: tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
257: const char *name, uint16_t len, struct tmpfs_dirent **de)
258: {
259: struct tmpfs_dirent *nde;
260:
261: nde = (struct tmpfs_dirent *)TMPFS_POOL_GET(&tmp->tm_dirent_pool, 0);
262: if (nde == NULL)
263: return ENOSPC;
264:
265: nde->td_name = tmpfs_str_pool_get(&tmp->tm_str_pool, len, 0);
266: if (nde->td_name == NULL) {
267: TMPFS_POOL_PUT(&tmp->tm_dirent_pool, nde);
268: return ENOSPC;
269: }
270: nde->td_namelen = len;
271: memcpy(nde->td_name, name, len);
272: nde->td_node = node;
273:
274: node->tn_links++;
1.29 jmmv 275: if (node->tn_links > 1 && node->tn_vnode != NULL)
276: VN_KNOTE(node->tn_vnode, NOTE_LINK);
1.1 jmmv 277: *de = nde;
278:
279: return 0;
280: }
281:
282: /* --------------------------------------------------------------------- */
283:
1.8 jmmv 284: /*
285: * Frees a directory entry. It is the caller's responsibility to destroy
286: * the node referenced by it if needed.
287: *
288: * The link count of node is decreased by one to reflect the removal of an
289: * object that referenced it. This only happens if 'node_exists' is true;
290: * otherwise the function will not access the node referred to by the
291: * directory entry, as it may already have been released from the outside.
1.29 jmmv 292: *
293: * Interested parties (kqueue) are notified of the link count change; note
294: * that this can include both the node pointed to by the directory entry
295: * as well as its parent.
1.8 jmmv 296: */
1.1 jmmv 297: void
298: tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
1.33 thorpej 299: bool node_exists)
1.1 jmmv 300: {
301: if (node_exists) {
302: struct tmpfs_node *node;
303:
304: node = de->td_node;
305:
306: KASSERT(node->tn_links > 0);
307: node->tn_links--;
1.29 jmmv 308: if (node->tn_vnode != NULL)
309: VN_KNOTE(node->tn_vnode, node->tn_links == 0 ?
310: NOTE_DELETE : NOTE_LINK);
311: if (node->tn_type == VDIR)
312: VN_KNOTE(node->tn_spec.tn_dir.tn_parent->tn_vnode,
313: NOTE_LINK);
1.1 jmmv 314: }
315:
316: tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name, de->td_namelen);
317: TMPFS_POOL_PUT(&tmp->tm_dirent_pool, de);
318: }
319:
320: /* --------------------------------------------------------------------- */
321:
1.8 jmmv 322: /*
323: * Allocates a new vnode for the node node or returns a new reference to
324: * an existing one if the node had already a vnode referencing it. The
325: * resulting locked vnode is returned in *vpp.
326: *
327: * Returns zero on success or an appropriate error code on failure.
328: */
1.1 jmmv 329: int
330: tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, struct vnode **vpp)
331: {
332: int error;
333: struct vnode *vp;
334:
1.43 ad 335: /* If there is already a vnode, then lock it. */
336: for (;;) {
337: mutex_enter(&node->tn_vlock);
338: if ((vp = node->tn_vnode) != NULL) {
339: mutex_enter(&vp->v_interlock);
340: mutex_exit(&node->tn_vlock);
341: error = vget(vp, LK_EXCLUSIVE | LK_INTERLOCK);
342: if (error == ENOENT) {
343: /* vnode was reclaimed. */
344: continue;
345: }
346: *vpp = vp;
347: return error;
348: }
349: break;
1.1 jmmv 350: }
351:
352: /* Get a new vnode and associate it with our node. */
353: error = getnewvnode(VT_TMPFS, mp, tmpfs_vnodeop_p, &vp);
1.43 ad 354: if (error != 0) {
355: mutex_exit(&node->tn_vlock);
356: return error;
357: }
1.1 jmmv 358:
359: error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
360: if (error != 0) {
1.43 ad 361: mutex_exit(&node->tn_vlock);
1.1 jmmv 362: ungetnewvnode(vp);
1.43 ad 363: return error;
1.1 jmmv 364: }
365:
366: vp->v_type = node->tn_type;
367:
368: /* Type-specific initialization. */
369: switch (node->tn_type) {
370: case VBLK:
371: /* FALLTHROUGH */
372: case VCHR:
373: vp->v_op = tmpfs_specop_p;
1.44 ad 374: spec_node_init(vp, node->tn_spec.tn_dev.tn_rdev);
1.1 jmmv 375: break;
376:
377: case VDIR:
1.40 ad 378: vp->v_vflag |= node->tn_spec.tn_dir.tn_parent == node ?
379: VV_ROOT : 0;
1.1 jmmv 380: break;
381:
382: case VFIFO:
383: vp->v_op = tmpfs_fifoop_p;
384: break;
385:
386: case VLNK:
387: /* FALLTHROUGH */
388: case VREG:
389: /* FALLTHROUGH */
390: case VSOCK:
391: break;
392:
393: default:
394: KASSERT(0);
395: }
396:
397: uvm_vnp_setsize(vp, node->tn_size);
1.43 ad 398: vp->v_data = node;
399: node->tn_vnode = vp;
400: mutex_exit(&node->tn_vlock);
401: *vpp = vp;
1.1 jmmv 402:
403: KASSERT(IFF(error == 0, *vpp != NULL && VOP_ISLOCKED(*vpp)));
1.40 ad 404: KASSERT(*vpp == node->tn_vnode);
1.1 jmmv 405:
406: return error;
407: }
408:
409: /* --------------------------------------------------------------------- */
410:
1.8 jmmv 411: /*
412: * Destroys the association between the vnode vp and the node it
413: * references.
414: */
1.1 jmmv 415: void
416: tmpfs_free_vp(struct vnode *vp)
417: {
418: struct tmpfs_node *node;
419:
420: node = VP_TO_TMPFS_NODE(vp);
421:
1.43 ad 422: mutex_enter(&node->tn_vlock);
1.1 jmmv 423: node->tn_vnode = NULL;
1.43 ad 424: mutex_exit(&node->tn_vlock);
1.1 jmmv 425: vp->v_data = NULL;
426: }
427:
428: /* --------------------------------------------------------------------- */
429:
1.9 jmmv 430: /*
431: * Allocates a new file of type 'type' and adds it to the parent directory
1.1 jmmv 432: * 'dvp'; this addition is done using the component name given in 'cnp'.
433: * The ownership of the new file is automatically assigned based on the
434: * credentials of the caller (through 'cnp'), the group is set based on
435: * the parent directory and the mode is determined from the 'vap' argument.
436: * If successful, *vpp holds a vnode to the newly created file and zero
437: * is returned. Otherwise *vpp is NULL and the function returns an
1.9 jmmv 438: * appropriate error code.
439: */
1.1 jmmv 440: int
441: tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
442: struct componentname *cnp, char *target)
443: {
444: int error;
445: struct tmpfs_dirent *de;
446: struct tmpfs_mount *tmp;
447: struct tmpfs_node *dnode;
448: struct tmpfs_node *node;
449: struct tmpfs_node *parent;
450:
451: KASSERT(VOP_ISLOCKED(dvp));
452: KASSERT(cnp->cn_flags & HASBUF);
453:
454: tmp = VFS_TO_TMPFS(dvp->v_mount);
455: dnode = VP_TO_TMPFS_DIR(dvp);
456: *vpp = NULL;
457:
458: /* If the entry we are creating is a directory, we cannot overflow
459: * the number of links of its parent, because it will get a new
460: * link. */
461: if (vap->va_type == VDIR) {
462: /* Ensure that we do not overflow the maximum number of links
463: * imposed by the system. */
464: KASSERT(dnode->tn_links <= LINK_MAX);
465: if (dnode->tn_links == LINK_MAX) {
466: error = EMLINK;
467: goto out;
468: }
469:
470: parent = dnode;
471: } else
472: parent = NULL;
473:
474: /* Allocate a node that represents the new file. */
1.19 elad 475: error = tmpfs_alloc_node(tmp, vap->va_type, kauth_cred_geteuid(cnp->cn_cred),
1.42 pooka 476: dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node);
1.1 jmmv 477: if (error != 0)
478: goto out;
479:
480: /* Allocate a directory entry that points to the new file. */
481: error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen,
482: &de);
483: if (error != 0) {
484: tmpfs_free_node(tmp, node);
485: goto out;
486: }
487:
488: /* Allocate a vnode for the new file. */
489: error = tmpfs_alloc_vp(dvp->v_mount, node, vpp);
490: if (error != 0) {
1.34 thorpej 491: tmpfs_free_dirent(tmp, de, true);
1.1 jmmv 492: tmpfs_free_node(tmp, node);
493: goto out;
494: }
495:
496: /* Now that all required items are allocated, we can proceed to
497: * insert the new node into the directory, an operation that
498: * cannot fail. */
499: tmpfs_dir_attach(dvp, de);
1.43 ad 500: if (vap->va_type == VDIR) {
501: VN_KNOTE(dvp, NOTE_LINK);
502: dnode->tn_links++;
503: KASSERT(dnode->tn_links <= LINK_MAX);
504: }
1.1 jmmv 505:
506: out:
507: if (error != 0 || !(cnp->cn_flags & SAVESTART))
508: PNBUF_PUT(cnp->cn_pnbuf);
509: vput(dvp);
510:
511: KASSERT(IFF(error == 0, *vpp != NULL));
512:
513: return error;
514: }
515:
516: /* --------------------------------------------------------------------- */
517:
1.8 jmmv 518: /*
519: * Attaches the directory entry de to the directory represented by vp.
520: * Note that this does not change the link count of the node pointed by
521: * the directory entry, as this is done by tmpfs_alloc_dirent.
1.29 jmmv 522: *
523: * As the "parent" directory changes, interested parties are notified of
524: * a write to it.
1.8 jmmv 525: */
1.1 jmmv 526: void
527: tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
528: {
529: struct tmpfs_node *dnode;
530:
531: dnode = VP_TO_TMPFS_DIR(vp);
532:
1.18 jmmv 533: TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
1.1 jmmv 534: dnode->tn_size += sizeof(struct tmpfs_dirent);
535: dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
536: TMPFS_NODE_MODIFIED;
537: uvm_vnp_setsize(vp, dnode->tn_size);
1.29 jmmv 538:
539: VN_KNOTE(vp, NOTE_WRITE);
1.1 jmmv 540: }
541:
542: /* --------------------------------------------------------------------- */
543:
1.8 jmmv 544: /*
545: * Detaches the directory entry de from the directory represented by vp.
546: * Note that this does not change the link count of the node pointed by
547: * the directory entry, as this is done by tmpfs_free_dirent.
1.29 jmmv 548: *
549: * As the "parent" directory changes, interested parties are notified of
550: * a write to it.
1.8 jmmv 551: */
1.1 jmmv 552: void
553: tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
554: {
555: struct tmpfs_node *dnode;
556:
1.5 yamt 557: KASSERT(VOP_ISLOCKED(vp));
558:
1.1 jmmv 559: dnode = VP_TO_TMPFS_DIR(vp);
560:
1.18 jmmv 561: if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {
562: dnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
563: dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
1.5 yamt 564: }
565:
1.18 jmmv 566: TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
1.1 jmmv 567: dnode->tn_size -= sizeof(struct tmpfs_dirent);
568: dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
569: TMPFS_NODE_MODIFIED;
570: uvm_vnp_setsize(vp, dnode->tn_size);
1.29 jmmv 571:
572: VN_KNOTE(vp, NOTE_WRITE);
1.1 jmmv 573: }
574:
575: /* --------------------------------------------------------------------- */
576:
1.8 jmmv 577: /*
578: * Looks for a directory entry in the directory represented by node.
579: * 'cnp' describes the name of the entry to look for. Note that the .
580: * and .. components are not allowed as they do not physically exist
581: * within directories.
582: *
583: * Returns a pointer to the entry when found, otherwise NULL.
584: */
1.1 jmmv 585: struct tmpfs_dirent *
586: tmpfs_dir_lookup(struct tmpfs_node *node, struct componentname *cnp)
587: {
588: struct tmpfs_dirent *de;
589:
1.49 ! yamt 590: KASSERT(VOP_ISLOCKED(node->tn_vnode));
1.1 jmmv 591: KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
592: KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
593: cnp->cn_nameptr[1] == '.')));
594: TMPFS_VALIDATE_DIR(node);
595:
596: node->tn_status |= TMPFS_NODE_ACCESSED;
597:
1.18 jmmv 598: TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
1.1 jmmv 599: KASSERT(cnp->cn_namelen < 0xffff);
600: if (de->td_namelen == (uint16_t)cnp->cn_namelen &&
1.40 ad 601: memcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) {
1.1 jmmv 602: break;
1.40 ad 603: }
1.1 jmmv 604: }
605:
1.49 ! yamt 606: return de;
1.1 jmmv 607: }
608:
609: /* --------------------------------------------------------------------- */
610:
1.9 jmmv 611: /*
612: * Helper function for tmpfs_readdir. Creates a '.' entry for the given
1.1 jmmv 613: * directory and returns it in the uio space. The function returns 0
614: * on success, -1 if there was not enough space in the uio structure to
615: * hold the directory entry or an appropriate error code if another
1.9 jmmv 616: * error happens.
617: */
1.1 jmmv 618: int
619: tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
620: {
621: int error;
1.37 rumble 622: struct dirent *dentp;
1.1 jmmv 623:
624: TMPFS_VALIDATE_DIR(node);
1.5 yamt 625: KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
1.1 jmmv 626:
1.43 ad 627: dentp = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
1.37 rumble 628:
629: dentp->d_fileno = node->tn_id;
630: dentp->d_type = DT_DIR;
631: dentp->d_namlen = 1;
632: dentp->d_name[0] = '.';
633: dentp->d_name[1] = '\0';
634: dentp->d_reclen = _DIRENT_SIZE(dentp);
1.1 jmmv 635:
1.37 rumble 636: if (dentp->d_reclen > uio->uio_resid)
1.1 jmmv 637: error = -1;
638: else {
1.37 rumble 639: error = uiomove(dentp, dentp->d_reclen, uio);
1.1 jmmv 640: if (error == 0)
1.5 yamt 641: uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
1.1 jmmv 642: }
643:
644: node->tn_status |= TMPFS_NODE_ACCESSED;
645:
1.43 ad 646: kmem_free(dentp, sizeof(struct dirent));
1.1 jmmv 647: return error;
648: }
649:
650: /* --------------------------------------------------------------------- */
651:
1.9 jmmv 652: /*
653: * Helper function for tmpfs_readdir. Creates a '..' entry for the given
1.1 jmmv 654: * directory and returns it in the uio space. The function returns 0
655: * on success, -1 if there was not enough space in the uio structure to
656: * hold the directory entry or an appropriate error code if another
1.9 jmmv 657: * error happens.
658: */
1.1 jmmv 659: int
660: tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
661: {
662: int error;
1.37 rumble 663: struct dirent *dentp;
1.1 jmmv 664:
665: TMPFS_VALIDATE_DIR(node);
1.5 yamt 666: KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
1.1 jmmv 667:
1.43 ad 668: dentp = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
1.37 rumble 669:
670: dentp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id;
671: dentp->d_type = DT_DIR;
672: dentp->d_namlen = 2;
673: dentp->d_name[0] = '.';
674: dentp->d_name[1] = '.';
675: dentp->d_name[2] = '\0';
676: dentp->d_reclen = _DIRENT_SIZE(dentp);
1.1 jmmv 677:
1.37 rumble 678: if (dentp->d_reclen > uio->uio_resid)
1.1 jmmv 679: error = -1;
680: else {
1.37 rumble 681: error = uiomove(dentp, dentp->d_reclen, uio);
1.5 yamt 682: if (error == 0) {
683: struct tmpfs_dirent *de;
684:
1.18 jmmv 685: de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
1.5 yamt 686: if (de == NULL)
687: uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
688: else
1.27 jmmv 689: uio->uio_offset = tmpfs_dircookie(de);
1.5 yamt 690: }
1.1 jmmv 691: }
692:
693: node->tn_status |= TMPFS_NODE_ACCESSED;
694:
1.43 ad 695: kmem_free(dentp, sizeof(struct dirent));
1.1 jmmv 696: return error;
697: }
698:
699: /* --------------------------------------------------------------------- */
700:
1.9 jmmv 701: /*
702: * Lookup a directory entry by its associated cookie.
703: */
1.5 yamt 704: struct tmpfs_dirent *
705: tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
706: {
707: struct tmpfs_dirent *de;
708:
1.49 ! yamt 709: KASSERT(VOP_ISLOCKED(node->tn_vnode));
! 710:
1.18 jmmv 711: if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn &&
712: node->tn_spec.tn_dir.tn_readdir_lastp != NULL) {
713: return node->tn_spec.tn_dir.tn_readdir_lastp;
1.5 yamt 714: }
715:
1.18 jmmv 716: TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
1.27 jmmv 717: if (tmpfs_dircookie(de) == cookie) {
1.5 yamt 718: break;
719: }
720: }
721:
722: return de;
723: }
724:
725: /* --------------------------------------------------------------------- */
726:
1.9 jmmv 727: /*
728: * Helper function for tmpfs_readdir. Returns as much directory entries
1.1 jmmv 729: * as can fit in the uio space. The read starts at uio->uio_offset.
730: * The function returns 0 on success, -1 if there was not enough space
731: * in the uio structure to hold the directory entry or an appropriate
1.9 jmmv 732: * error code if another error happens.
733: */
1.1 jmmv 734: int
1.5 yamt 735: tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
1.1 jmmv 736: {
737: int error;
1.5 yamt 738: off_t startcookie;
1.37 rumble 739: struct dirent *dentp;
1.1 jmmv 740: struct tmpfs_dirent *de;
741:
1.49 ! yamt 742: KASSERT(VOP_ISLOCKED(node->tn_vnode));
1.1 jmmv 743: TMPFS_VALIDATE_DIR(node);
744:
745: /* Locate the first directory entry we have to return. We have cached
746: * the last readdir in the node, so use those values if appropriate.
747: * Otherwise do a linear scan to find the requested entry. */
1.5 yamt 748: startcookie = uio->uio_offset;
749: KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);
750: KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
751: if (startcookie == TMPFS_DIRCOOKIE_EOF) {
752: return 0;
1.1 jmmv 753: } else {
1.5 yamt 754: de = tmpfs_dir_lookupbycookie(node, startcookie);
755: }
756: if (de == NULL) {
757: return EINVAL;
1.1 jmmv 758: }
759:
1.43 ad 760: dentp = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
1.37 rumble 761:
1.1 jmmv 762: /* Read as much entries as possible; i.e., until we reach the end of
763: * the directory or we exhaust uio space. */
764: do {
765: /* Create a dirent structure representing the current
766: * tmpfs_node and fill it. */
1.37 rumble 767: dentp->d_fileno = de->td_node->tn_id;
1.1 jmmv 768: switch (de->td_node->tn_type) {
769: case VBLK:
1.37 rumble 770: dentp->d_type = DT_BLK;
1.1 jmmv 771: break;
772:
773: case VCHR:
1.37 rumble 774: dentp->d_type = DT_CHR;
1.1 jmmv 775: break;
776:
777: case VDIR:
1.37 rumble 778: dentp->d_type = DT_DIR;
1.1 jmmv 779: break;
780:
781: case VFIFO:
1.37 rumble 782: dentp->d_type = DT_FIFO;
1.1 jmmv 783: break;
784:
785: case VLNK:
1.37 rumble 786: dentp->d_type = DT_LNK;
1.1 jmmv 787: break;
788:
789: case VREG:
1.37 rumble 790: dentp->d_type = DT_REG;
1.1 jmmv 791: break;
792:
793: case VSOCK:
1.37 rumble 794: dentp->d_type = DT_SOCK;
1.1 jmmv 795: break;
796:
797: default:
798: KASSERT(0);
799: }
1.37 rumble 800: dentp->d_namlen = de->td_namelen;
801: KASSERT(de->td_namelen < sizeof(dentp->d_name));
802: (void)memcpy(dentp->d_name, de->td_name, de->td_namelen);
803: dentp->d_name[de->td_namelen] = '\0';
804: dentp->d_reclen = _DIRENT_SIZE(dentp);
1.1 jmmv 805:
806: /* Stop reading if the directory entry we are treating is
807: * bigger than the amount of data that can be returned. */
1.37 rumble 808: if (dentp->d_reclen > uio->uio_resid) {
1.1 jmmv 809: error = -1;
810: break;
811: }
812:
813: /* Copy the new dirent structure into the output buffer and
814: * advance pointers. */
1.37 rumble 815: error = uiomove(dentp, dentp->d_reclen, uio);
1.1 jmmv 816:
1.5 yamt 817: (*cntp)++;
1.1 jmmv 818: de = TAILQ_NEXT(de, td_entries);
819: } while (error == 0 && uio->uio_resid > 0 && de != NULL);
820:
1.5 yamt 821: /* Update the offset and cache. */
1.1 jmmv 822: if (de == NULL) {
1.5 yamt 823: uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
1.18 jmmv 824: node->tn_spec.tn_dir.tn_readdir_lastn = 0;
825: node->tn_spec.tn_dir.tn_readdir_lastp = NULL;
1.1 jmmv 826: } else {
1.18 jmmv 827: node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset =
1.27 jmmv 828: tmpfs_dircookie(de);
1.18 jmmv 829: node->tn_spec.tn_dir.tn_readdir_lastp = de;
1.1 jmmv 830: }
831:
832: node->tn_status |= TMPFS_NODE_ACCESSED;
833:
1.43 ad 834: kmem_free(dentp, sizeof(struct dirent));
1.1 jmmv 835: return error;
836: }
837:
838: /* --------------------------------------------------------------------- */
839:
1.8 jmmv 840: /*
841: * Resizes the aobj associated to the regular file pointed to by vp to
842: * the size newsize. 'vp' must point to a vnode that represents a regular
843: * file. 'newsize' must be positive.
844: *
1.29 jmmv 845: * If the file is extended, the appropriate kevent is raised. This does
846: * not rise a write event though because resizing is not the same as
847: * writing.
848: *
1.8 jmmv 849: * Returns zero on success or an appropriate error code on failure.
850: */
1.1 jmmv 851: int
852: tmpfs_reg_resize(struct vnode *vp, off_t newsize)
853: {
854: int error;
1.46 jmmv 855: unsigned int newpages, oldpages;
1.1 jmmv 856: struct tmpfs_mount *tmp;
857: struct tmpfs_node *node;
1.13 yamt 858: off_t oldsize;
1.1 jmmv 859:
860: KASSERT(vp->v_type == VREG);
861: KASSERT(newsize >= 0);
862:
863: node = VP_TO_TMPFS_NODE(vp);
864: tmp = VFS_TO_TMPFS(vp->v_mount);
865:
866: /* Convert the old and new sizes to the number of pages needed to
867: * store them. It may happen that we do not need to do anything
868: * because the last allocated page can accommodate the change on
869: * its own. */
1.13 yamt 870: oldsize = node->tn_size;
871: oldpages = round_page(oldsize) / PAGE_SIZE;
1.18 jmmv 872: KASSERT(oldpages == node->tn_spec.tn_reg.tn_aobj_pages);
1.1 jmmv 873: newpages = round_page(newsize) / PAGE_SIZE;
874:
875: if (newpages > oldpages &&
1.43 ad 876: (ssize_t)(newpages - oldpages) > TMPFS_PAGES_AVAIL(tmp)) {
1.1 jmmv 877: error = ENOSPC;
878: goto out;
879: }
1.43 ad 880: atomic_add_int(&tmp->tm_pages_used, newpages - oldpages);
1.1 jmmv 881:
1.13 yamt 882: if (newsize < oldsize) {
883: int zerolen = MIN(round_page(newsize), node->tn_size) - newsize;
884:
885: /*
886: * zero out the truncated part of the last page.
887: */
888:
889: uvm_vnp_zerorange(vp, newsize, zerolen);
890: }
1.1 jmmv 891:
1.36 pooka 892: node->tn_spec.tn_reg.tn_aobj_pages = newpages;
893: node->tn_size = newsize;
894: uvm_vnp_setsize(vp, newsize);
895:
1.43 ad 896: /*
897: * free "backing store"
898: */
899:
900: if (newpages < oldpages) {
901: struct uvm_object *uobj;
902:
903: uobj = node->tn_spec.tn_reg.tn_aobj;
904:
905: mutex_enter(&uobj->vmobjlock);
906: uao_dropswap_range(uobj, newpages, oldpages);
907: mutex_exit(&uobj->vmobjlock);
908: }
1.40 ad 909:
1.1 jmmv 910: error = 0;
911:
1.29 jmmv 912: if (newsize > oldsize)
913: VN_KNOTE(vp, NOTE_EXTEND);
914:
1.1 jmmv 915: out:
916: return error;
917: }
918:
919: /* --------------------------------------------------------------------- */
920:
1.9 jmmv 921: /*
922: * Returns information about the number of available memory pages,
1.1 jmmv 923: * including physical and virtual ones.
924: *
1.45 jmmv 925: * If 'total' is true, the value returned is the total amount of memory
1.1 jmmv 926: * pages configured for the system (either in use or free).
927: * If it is FALSE, the value returned is the amount of free memory pages.
928: *
929: * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
930: * excessive memory usage.
931: *
1.9 jmmv 932: */
1.1 jmmv 933: size_t
1.33 thorpej 934: tmpfs_mem_info(bool total)
1.1 jmmv 935: {
936: size_t size;
937:
938: size = 0;
1.15 dan 939: size += uvmexp.swpgavail;
940: if (!total) {
941: size -= uvmexp.swpgonly;
1.1 jmmv 942: }
943: size += uvmexp.free;
1.16 dan 944: size += uvmexp.filepages;
945: if (size > uvmexp.wired) {
946: size -= uvmexp.wired;
947: } else {
948: size = 0;
949: }
1.1 jmmv 950:
951: return size;
952: }
953:
954: /* --------------------------------------------------------------------- */
955:
1.9 jmmv 956: /*
957: * Change flags of the given vnode.
1.12 yamt 958: * Caller should execute tmpfs_update on vp after a successful execution.
1.9 jmmv 959: * The vnode must be locked on entry and remain locked on exit.
960: */
1.1 jmmv 961: int
1.22 ad 962: tmpfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred, struct lwp *l)
1.1 jmmv 963: {
964: int error;
965: struct tmpfs_node *node;
966:
967: KASSERT(VOP_ISLOCKED(vp));
968:
969: node = VP_TO_TMPFS_NODE(vp);
970:
971: /* Disallow this operation if the file system is mounted read-only. */
972: if (vp->v_mount->mnt_flag & MNT_RDONLY)
973: return EROFS;
974:
975: /* XXX: The following comes from UFS code, and can be found in
976: * several other file systems. Shouldn't this be centralized
977: * somewhere? */
1.19 elad 978: if (kauth_cred_geteuid(cred) != node->tn_uid &&
979: (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
1.32 elad 980: NULL)))
1.1 jmmv 981: return error;
1.32 elad 982: if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) == 0) {
1.1 jmmv 983: /* The super-user is only allowed to change flags if the file
984: * wasn't protected before and the securelevel is zero. */
985: if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) &&
1.31 elad 986: kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CHSYSFLAGS,
987: 0, NULL, NULL, NULL))
1.1 jmmv 988: return EPERM;
989: node->tn_flags = flags;
990: } else {
991: /* Regular users can change flags provided they only want to
992: * change user-specific ones, not those reserved for the
993: * super-user. */
994: if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) ||
995: (flags & UF_SETTABLE) != flags)
996: return EPERM;
997: if ((node->tn_flags & SF_SETTABLE) != (flags & SF_SETTABLE))
998: return EPERM;
999: node->tn_flags &= SF_SETTABLE;
1000: node->tn_flags |= (flags & UF_SETTABLE);
1001: }
1002:
1003: node->tn_status |= TMPFS_NODE_CHANGED;
1004: VN_KNOTE(vp, NOTE_ATTRIB);
1005:
1006: KASSERT(VOP_ISLOCKED(vp));
1007:
1008: return 0;
1009: }
1010:
1011: /* --------------------------------------------------------------------- */
1012:
1.9 jmmv 1013: /*
1014: * Change access mode on the given vnode.
1.12 yamt 1015: * Caller should execute tmpfs_update on vp after a successful execution.
1.9 jmmv 1016: * The vnode must be locked on entry and remain locked on exit.
1017: */
1.1 jmmv 1018: int
1.22 ad 1019: tmpfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l)
1.1 jmmv 1020: {
1.19 elad 1021: int error, ismember = 0;
1.1 jmmv 1022: struct tmpfs_node *node;
1023:
1024: KASSERT(VOP_ISLOCKED(vp));
1025:
1026: node = VP_TO_TMPFS_NODE(vp);
1027:
1028: /* Disallow this operation if the file system is mounted read-only. */
1029: if (vp->v_mount->mnt_flag & MNT_RDONLY)
1030: return EROFS;
1031:
1032: /* Immutable or append-only files cannot be modified, either. */
1033: if (node->tn_flags & (IMMUTABLE | APPEND))
1034: return EPERM;
1035:
1036: /* XXX: The following comes from UFS code, and can be found in
1037: * several other file systems. Shouldn't this be centralized
1038: * somewhere? */
1.19 elad 1039: if (kauth_cred_geteuid(cred) != node->tn_uid &&
1040: (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
1.32 elad 1041: NULL)))
1.1 jmmv 1042: return error;
1.32 elad 1043: if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) {
1.1 jmmv 1044: if (vp->v_type != VDIR && (mode & S_ISTXT))
1045: return EFTYPE;
1046:
1.19 elad 1047: if ((kauth_cred_ismember_gid(cred, node->tn_gid,
1048: &ismember) != 0 || !ismember) && (mode & S_ISGID))
1.1 jmmv 1049: return EPERM;
1050: }
1051:
1052: node->tn_mode = (mode & ALLPERMS);
1053:
1054: node->tn_status |= TMPFS_NODE_CHANGED;
1055: VN_KNOTE(vp, NOTE_ATTRIB);
1056:
1057: KASSERT(VOP_ISLOCKED(vp));
1058:
1059: return 0;
1060: }
1061:
1062: /* --------------------------------------------------------------------- */
1063:
1.9 jmmv 1064: /*
1065: * Change ownership of the given vnode. At least one of uid or gid must
1.1 jmmv 1066: * be different than VNOVAL. If one is set to that value, the attribute
1067: * is unchanged.
1.12 yamt 1068: * Caller should execute tmpfs_update on vp after a successful execution.
1.9 jmmv 1069: * The vnode must be locked on entry and remain locked on exit.
1070: */
1.1 jmmv 1071: int
1.19 elad 1072: tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
1.22 ad 1073: struct lwp *l)
1.1 jmmv 1074: {
1.19 elad 1075: int error, ismember = 0;
1.1 jmmv 1076: struct tmpfs_node *node;
1077:
1078: KASSERT(VOP_ISLOCKED(vp));
1079:
1080: node = VP_TO_TMPFS_NODE(vp);
1081:
1082: /* Assign default values if they are unknown. */
1083: KASSERT(uid != VNOVAL || gid != VNOVAL);
1084: if (uid == VNOVAL)
1085: uid = node->tn_uid;
1086: if (gid == VNOVAL)
1087: gid = node->tn_gid;
1088: KASSERT(uid != VNOVAL && gid != VNOVAL);
1089:
1090: /* Disallow this operation if the file system is mounted read-only. */
1091: if (vp->v_mount->mnt_flag & MNT_RDONLY)
1092: return EROFS;
1093:
1094: /* Immutable or append-only files cannot be modified, either. */
1095: if (node->tn_flags & (IMMUTABLE | APPEND))
1096: return EPERM;
1097:
1098: /* XXX: The following comes from UFS code, and can be found in
1099: * several other file systems. Shouldn't this be centralized
1100: * somewhere? */
1.19 elad 1101: if ((kauth_cred_geteuid(cred) != node->tn_uid || uid != node->tn_uid ||
1102: (gid != node->tn_gid && !(kauth_cred_getegid(cred) == node->tn_gid ||
1.22 ad 1103: (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember)))) &&
1.19 elad 1104: ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
1.32 elad 1105: NULL)) != 0))
1.1 jmmv 1106: return error;
1107:
1108: node->tn_uid = uid;
1109: node->tn_gid = gid;
1110:
1111: node->tn_status |= TMPFS_NODE_CHANGED;
1112: VN_KNOTE(vp, NOTE_ATTRIB);
1113:
1114: KASSERT(VOP_ISLOCKED(vp));
1115:
1116: return 0;
1117: }
1118:
1119: /* --------------------------------------------------------------------- */
1120:
1.9 jmmv 1121: /*
1122: * Change size of the given vnode.
1.12 yamt 1123: * Caller should execute tmpfs_update on vp after a successful execution.
1.9 jmmv 1124: * The vnode must be locked on entry and remain locked on exit.
1125: */
1.1 jmmv 1126: int
1.30 christos 1127: tmpfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred,
1128: struct lwp *l)
1.1 jmmv 1129: {
1130: int error;
1131: struct tmpfs_node *node;
1132:
1133: KASSERT(VOP_ISLOCKED(vp));
1134:
1135: node = VP_TO_TMPFS_NODE(vp);
1136:
1137: /* Decide whether this is a valid operation based on the file type. */
1138: error = 0;
1139: switch (vp->v_type) {
1140: case VDIR:
1141: return EISDIR;
1142:
1143: case VREG:
1144: if (vp->v_mount->mnt_flag & MNT_RDONLY)
1145: return EROFS;
1146: break;
1147:
1148: case VBLK:
1149: /* FALLTHROUGH */
1150: case VCHR:
1151: /* FALLTHROUGH */
1152: case VFIFO:
1153: /* Allow modifications of special files even if in the file
1154: * system is mounted read-only (we are not modifying the
1155: * files themselves, but the objects they represent). */
1.14 yamt 1156: return 0;
1.1 jmmv 1157:
1158: default:
1159: /* Anything else is unsupported. */
1.14 yamt 1160: return EOPNOTSUPP;
1.1 jmmv 1161: }
1162:
1163: /* Immutable or append-only files cannot be modified, either. */
1164: if (node->tn_flags & (IMMUTABLE | APPEND))
1165: return EPERM;
1166:
1.12 yamt 1167: error = tmpfs_truncate(vp, size);
1.1 jmmv 1168: /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
1169: * for us, as will update tn_status; no need to do that here. */
1170:
1171: KASSERT(VOP_ISLOCKED(vp));
1172:
1173: return error;
1174: }
1175:
1176: /* --------------------------------------------------------------------- */
1177:
1.9 jmmv 1178: /*
1179: * Change access and modification times of the given vnode.
1.12 yamt 1180: * Caller should execute tmpfs_update on vp after a successful execution.
1.9 jmmv 1181: * The vnode must be locked on entry and remain locked on exit.
1182: */
1.1 jmmv 1183: int
1.48 christos 1184: tmpfs_chtimes(struct vnode *vp, const struct timespec *atime,
1185: const struct timespec *mtime, const struct timespec *btime,
1.19 elad 1186: int vaflags, kauth_cred_t cred, struct lwp *l)
1.1 jmmv 1187: {
1188: int error;
1189: struct tmpfs_node *node;
1190:
1191: KASSERT(VOP_ISLOCKED(vp));
1192:
1193: node = VP_TO_TMPFS_NODE(vp);
1194:
1195: /* Disallow this operation if the file system is mounted read-only. */
1196: if (vp->v_mount->mnt_flag & MNT_RDONLY)
1197: return EROFS;
1198:
1199: /* Immutable or append-only files cannot be modified, either. */
1200: if (node->tn_flags & (IMMUTABLE | APPEND))
1201: return EPERM;
1202:
1203: /* XXX: The following comes from UFS code, and can be found in
1204: * several other file systems. Shouldn't this be centralized
1205: * somewhere? */
1.19 elad 1206: if (kauth_cred_geteuid(cred) != node->tn_uid &&
1207: (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
1.32 elad 1208: NULL)) && ((vaflags & VA_UTIMES_NULL) == 0 ||
1.41 pooka 1209: (error = VOP_ACCESS(vp, VWRITE, cred))))
1.1 jmmv 1210: return error;
1211:
1212: if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
1213: node->tn_status |= TMPFS_NODE_ACCESSED;
1214:
1215: if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
1216: node->tn_status |= TMPFS_NODE_MODIFIED;
1217:
1.48 christos 1218: if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL)
1219: btime = NULL;
1220:
1221: tmpfs_update(vp, atime, mtime, btime, 0);
1.29 jmmv 1222: VN_KNOTE(vp, NOTE_ATTRIB);
1.1 jmmv 1223:
1224: KASSERT(VOP_ISLOCKED(vp));
1225:
1.12 yamt 1226: return 0;
1.1 jmmv 1227: }
1.10 yamt 1228:
1229: /* --------------------------------------------------------------------- */
1230:
1231: /* Sync timestamps */
1232: void
1233: tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
1.48 christos 1234: const struct timespec *mod, const struct timespec *birth)
1.10 yamt 1235: {
1.48 christos 1236: struct timespec now, *nowp = NULL;
1.10 yamt 1237: struct tmpfs_node *node;
1238:
1239: node = VP_TO_TMPFS_NODE(vp);
1240:
1241: if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
1242: TMPFS_NODE_CHANGED)) == 0)
1243: return;
1.26 jmmv 1244:
1.48 christos 1245: if (birth != NULL)
1246: node->tn_birthtime = *birth;
1247:
1.10 yamt 1248: if (node->tn_status & TMPFS_NODE_ACCESSED) {
1.48 christos 1249: if (acc == NULL) {
1250: if (nowp == NULL)
1251: getnanotime(nowp = &now);
1252: acc = nowp;
1253: }
1.10 yamt 1254: node->tn_atime = *acc;
1255: }
1256: if (node->tn_status & TMPFS_NODE_MODIFIED) {
1.48 christos 1257: if (mod == NULL) {
1258: if (nowp == NULL)
1259: getnanotime(nowp = &now);
1260: mod = nowp;
1261: }
1.10 yamt 1262: node->tn_mtime = *mod;
1263: }
1.48 christos 1264: if (node->tn_status & TMPFS_NODE_CHANGED) {
1265: if (nowp == NULL)
1266: getnanotime(nowp = &now);
1267: node->tn_ctime = *nowp;
1268: }
1.21 kardel 1269:
1.10 yamt 1270: node->tn_status &=
1271: ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
1272: }
1.12 yamt 1273:
1274: /* --------------------------------------------------------------------- */
1275:
1276: void
1277: tmpfs_update(struct vnode *vp, const struct timespec *acc,
1.48 christos 1278: const struct timespec *mod, const struct timespec *birth, int flags)
1.12 yamt 1279: {
1280:
1281: struct tmpfs_node *node;
1282:
1283: KASSERT(VOP_ISLOCKED(vp));
1284:
1285: node = VP_TO_TMPFS_NODE(vp);
1286:
1.23 christos 1287: #if 0
1.12 yamt 1288: if (flags & UPDATE_CLOSE)
1289: ; /* XXX Need to do anything special? */
1.23 christos 1290: #endif
1.12 yamt 1291:
1.48 christos 1292: tmpfs_itimes(vp, acc, mod, birth);
1.12 yamt 1293:
1294: KASSERT(VOP_ISLOCKED(vp));
1295: }
1296:
1297: /* --------------------------------------------------------------------- */
1298:
1299: int
1300: tmpfs_truncate(struct vnode *vp, off_t length)
1301: {
1.33 thorpej 1302: bool extended;
1.12 yamt 1303: int error;
1304: struct tmpfs_node *node;
1305:
1306: node = VP_TO_TMPFS_NODE(vp);
1307: extended = length > node->tn_size;
1308:
1309: if (length < 0) {
1310: error = EINVAL;
1311: goto out;
1312: }
1313:
1314: if (node->tn_size == length) {
1315: error = 0;
1316: goto out;
1317: }
1318:
1319: error = tmpfs_reg_resize(vp, length);
1.29 jmmv 1320: if (error == 0)
1.12 yamt 1321: node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1322:
1323: out:
1.48 christos 1324: tmpfs_update(vp, NULL, NULL, NULL, 0);
1.12 yamt 1325:
1326: return error;
1327: }
CVSweb <webmaster@jp.NetBSD.org>