[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.34

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

CVSweb <webmaster@jp.NetBSD.org>