[BACK]Return to tmpfs_subr.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / fs / tmpfs

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>