/* * Initialize this vnode / nfs node pair. * Caller assures no other thread will try to load this node. */ int nfs_loadvnode(struct mount *mp, struct vnode *vp, const void *key, size_t key_len, const void **new_key) { int fhsize = key_len; const nfsfh_t *fhp = key; struct nfsnode *np; /* Aloocate and initialize the nfsnode. */ np = pool_get(&nfs_node_pool, PR_WAITOK); memset(np, 0, sizeof *np); if (fhsize > NFS_SMALLFH) { np->n_fhp = kmem_alloc(fhsize, KM_SLEEP); } else np->n_fhp = &np->n_fh; vp->v_tag = VT_NFS; vp->v_type = VNON; vp->v_op = nfsv2_vnodeop_p; vp->v_data = np; memcpy(np->n_fhp, fhp, fhsize); np->n_fhsize = fhsize; np->n_accstamp = -1; np->n_vattr = pool_get(&nfs_vattr_pool, PR_WAITOK); np->n_vnode = vp; /* Initialize genfs node. */ genfs_node_init(vp, &nfs_genfsops); /* * Initalize read/write creds to useful values. VOP_OPEN will * overwrite these. */ np->n_rcred = curlwp->l_cred; kauth_cred_hold(np->n_rcred); np->n_wcred = curlwp->l_cred; kauth_cred_hold(np->n_wcred); NFS_INVALIDATE_ATTRCACHE(np); uvm_vnp_setsize(vp, 0); *new_key = np->n_fhp; return 0; }
void lwp_update_creds(struct lwp *l) { struct proc *p; kauth_cred_t oldcred; p = l->l_proc; oldcred = l->l_cred; l->l_prflag &= ~LPR_CRMOD; mutex_enter(p->p_lock); kauth_cred_hold(p->p_cred); l->l_cred = p->p_cred; mutex_exit(p->p_lock); if (oldcred != NULL) kauth_cred_free(oldcred); }
/* * Mount union filesystem */ int union_mount(struct mount *mp, const char *path, void *data, size_t *data_len) { struct lwp *l = curlwp; int error = 0; struct union_args *args = data; struct vnode *lowerrootvp = NULLVP; struct vnode *upperrootvp = NULLVP; struct union_mount *um = 0; const char *cp; char *xp; int len; size_t size; if (*data_len < sizeof *args) return EINVAL; #ifdef UNION_DIAGNOSTIC printf("union_mount(mp = %p)\n", mp); #endif if (mp->mnt_flag & MNT_GETARGS) { um = MOUNTTOUNIONMOUNT(mp); if (um == NULL) return EIO; args->target = NULL; args->mntflags = um->um_op; *data_len = sizeof *args; return 0; } /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { /* * Need to provide. * 1. a way to convert between rdonly and rdwr mounts. * 2. support for nfs exports. */ error = EOPNOTSUPP; goto bad; } lowerrootvp = mp->mnt_vnodecovered; vref(lowerrootvp); /* * Find upper node. */ error = namei_simple_user(args->target, NSM_FOLLOW_NOEMULROOT, &upperrootvp); if (error != 0) goto bad; if (upperrootvp->v_type != VDIR) { error = EINVAL; goto bad; } um = kmem_zalloc(sizeof(struct union_mount), KM_SLEEP); /* * Keep a held reference to the target vnodes. * They are vrele'd in union_unmount. * * Depending on the _BELOW flag, the filesystems are * viewed in a different order. In effect, this is the * same as providing a mount under option to the mount syscall. */ um->um_op = args->mntflags & UNMNT_OPMASK; switch (um->um_op) { case UNMNT_ABOVE: um->um_lowervp = lowerrootvp; um->um_uppervp = upperrootvp; break; case UNMNT_BELOW: um->um_lowervp = upperrootvp; um->um_uppervp = lowerrootvp; break; case UNMNT_REPLACE: vrele(lowerrootvp); lowerrootvp = NULLVP; um->um_uppervp = upperrootvp; um->um_lowervp = lowerrootvp; break; default: error = EINVAL; goto bad; } mp->mnt_iflag |= IMNT_MPSAFE; /* * Unless the mount is readonly, ensure that the top layer * supports whiteout operations */ if ((mp->mnt_flag & MNT_RDONLY) == 0) { vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY); error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP); VOP_UNLOCK(um->um_uppervp); if (error) goto bad; } um->um_cred = l->l_cred; kauth_cred_hold(um->um_cred); um->um_cmode = UN_DIRMODE &~ l->l_proc->p_cwdi->cwdi_cmask; /* * Depending on what you think the MNT_LOCAL flag might mean, * you may want the && to be || on the conditional below. * At the moment it has been defined that the filesystem is * only local if it is all local, ie the MNT_LOCAL flag implies * that the entire namespace is local. If you think the MNT_LOCAL * flag implies that some of the files might be stored locally * then you will want to change the conditional. */ if (um->um_op == UNMNT_ABOVE) { if (((um->um_lowervp == NULLVP) || (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) mp->mnt_flag |= MNT_LOCAL; } /* * Copy in the upper layer's RDONLY flag. This is for the benefit * of lookup() which explicitly checks the flag, rather than asking * the filesystem for it's own opinion. This means, that an update * mount of the underlying filesystem to go from rdonly to rdwr * will leave the unioned view as read-only. */ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); mp->mnt_data = um; vfs_getnewfsid(mp); error = set_statvfs_info( path, UIO_USERSPACE, NULL, UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l); if (error) goto bad; switch (um->um_op) { case UNMNT_ABOVE: cp = "<above>:"; break; case UNMNT_BELOW: cp = "<below>:"; break; case UNMNT_REPLACE: cp = ""; break; default: cp = "<invalid>:"; #ifdef DIAGNOSTIC panic("union_mount: bad um_op"); #endif break; } len = strlen(cp); memcpy(mp->mnt_stat.f_mntfromname, cp, len); xp = mp->mnt_stat.f_mntfromname + len; len = MNAMELEN - len; (void) copyinstr(args->target, xp, len - 1, &size); memset(xp + size, 0, len - size); #ifdef UNION_DIAGNOSTIC printf("union_mount: from %s, on %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif /* Setup the readdir hook if it's not set already */ if (!vn_union_readdir_hook) vn_union_readdir_hook = union_readdirhook; return (0); bad: if (um) kmem_free(um, sizeof(struct union_mount)); if (upperrootvp) vrele(upperrootvp); if (lowerrootvp) vrele(lowerrootvp); return (error); }
/* * set up a quota file for a particular file system. */ int lfsquota1_handle_cmd_quotaon(struct lwp *l, struct ulfsmount *ump, int type, const char *fname) { struct mount *mp = ump->um_mountp; struct lfs *fs = ump->um_lfs; struct vnode *vp, **vpp; struct vnode_iterator *marker; struct dquot *dq; int error; struct pathbuf *pb; struct nameidata nd; if (fs->um_flags & ULFS_QUOTA2) { uprintf("%s: quotas v2 already enabled\n", mp->mnt_stat.f_mntonname); return (EBUSY); } vpp = &ump->um_quotas[type]; pb = pathbuf_create(fname); if (pb == NULL) { return ENOMEM; } NDINIT(&nd, LOOKUP, FOLLOW, pb); if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { pathbuf_destroy(pb); return error; } vp = nd.ni_vp; pathbuf_destroy(pb); VOP_UNLOCK(vp); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, l->l_cred); return (EACCES); } if (*vpp != vp) lfsquota1_handle_cmd_quotaoff(l, ump, type); mutex_enter(&lfs_dqlock); while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) cv_wait(&lfs_dqcv, &lfs_dqlock); ump->umq1_qflags[type] |= QTF_OPENING; mutex_exit(&lfs_dqlock); mp->mnt_flag |= MNT_QUOTA; vp->v_vflag |= VV_SYSTEM; /* XXXSMP */ *vpp = vp; /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. */ kauth_cred_hold(l->l_cred); ump->um_cred[type] = l->l_cred; ump->umq1_btime[type] = MAX_DQ_TIME; ump->umq1_itime[type] = MAX_IQ_TIME; if (lfs_dqget(NULLVP, 0, ump, type, &dq) == 0) { if (dq->dq_btime > 0) ump->umq1_btime[type] = dq->dq_btime; if (dq->dq_itime > 0) ump->umq1_itime[type] = dq->dq_itime; lfs_dqrele(NULLVP, dq); } /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. */ vfs_vnode_iterator_init(mp, &marker); while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) { error = vn_lock(vp, LK_EXCLUSIVE); if (error) { vrele(vp); continue; } mutex_enter(vp->v_interlock); if (VTOI(vp) == NULL || vp->v_type == VNON || vp->v_writecount == 0) { mutex_exit(vp->v_interlock); vput(vp); continue; } mutex_exit(vp->v_interlock); if ((error = lfs_getinoquota(VTOI(vp))) != 0) { vput(vp); break; } vput(vp); } vfs_vnode_iterator_destroy(marker); mutex_enter(&lfs_dqlock); ump->umq1_qflags[type] &= ~QTF_OPENING; cv_broadcast(&lfs_dqcv); if (error == 0) fs->um_flags |= ULFS_QUOTA; mutex_exit(&lfs_dqlock); if (error) lfsquota1_handle_cmd_quotaoff(l, ump, type); return (error); }
/* * Q_QUOTAON - set up a quota file for a particular file system. */ int quotaon(struct lwp *l, struct mount *mp, int type, void *fname) { struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp, **vpp, *mvp; struct dquot *dq; int error; struct nameidata nd; /* XXX XXX XXX */ if (mp->mnt_wapbl != NULL) { printf("%s: quotas cannot yet be used with -o log\n", mp->mnt_stat.f_mntonname); return (EOPNOTSUPP); } vpp = &ump->um_quotas[type]; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname); if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) return (error); vp = nd.ni_vp; VOP_UNLOCK(vp, 0); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, l->l_cred); return (EACCES); } if (*vpp != vp) quotaoff(l, mp, type); mutex_enter(&dqlock); while ((ump->um_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) cv_wait(&dqcv, &dqlock); ump->um_qflags[type] |= QTF_OPENING; mutex_exit(&dqlock); mp->mnt_flag |= MNT_QUOTA; vp->v_vflag |= VV_SYSTEM; /* XXXSMP */ *vpp = vp; /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. */ kauth_cred_hold(l->l_cred); ump->um_cred[type] = l->l_cred; ump->um_btime[type] = MAX_DQ_TIME; ump->um_itime[type] = MAX_IQ_TIME; if (dqget(NULLVP, 0, ump, type, &dq) == 0) { if (dq->dq_btime > 0) ump->um_btime[type] = dq->dq_btime; if (dq->dq_itime > 0) ump->um_itime[type] = dq->dq_itime; dqrele(NULLVP, dq); } /* Allocate a marker vnode. */ if ((mvp = vnalloc(mp)) == NULL) { error = ENOMEM; goto out; } /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. */ mutex_enter(&mntvnode_lock); again: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { vmark(mvp, vp); mutex_enter(&vp->v_interlock); if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) || vp->v_type == VNON || vp->v_writecount == 0 || (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) { mutex_exit(&vp->v_interlock); continue; } mutex_exit(&mntvnode_lock); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) { mutex_enter(&mntvnode_lock); (void)vunmark(mvp); goto again; } if ((error = getinoquota(VTOI(vp))) != 0) { vput(vp); mutex_enter(&mntvnode_lock); (void)vunmark(mvp); break; } vput(vp); mutex_enter(&mntvnode_lock); } mutex_exit(&mntvnode_lock); vnfree(mvp); out: mutex_enter(&dqlock); ump->um_qflags[type] &= ~QTF_OPENING; cv_broadcast(&dqcv); mutex_exit(&dqlock); if (error) quotaoff(l, mp, type); return (error); }
/* * Dump core, into a file named "progname.core" or "core" (depending on the * value of shortcorename), unless the process was setuid/setgid. */ static int coredump(struct lwp *l, const char *pattern) { struct vnode *vp; struct proc *p; struct vmspace *vm; kauth_cred_t cred; struct pathbuf *pb; struct nameidata nd; struct vattr vattr; struct coredump_iostate io; struct plimit *lim; int error, error1; char *name, *lastslash; name = PNBUF_GET(); p = l->l_proc; vm = p->p_vmspace; mutex_enter(proc_lock); /* p_session */ mutex_enter(p->p_lock); /* * Refuse to core if the data + stack + user size is larger than * the core dump limit. XXX THIS IS WRONG, because of mapped * data. */ if (USPACE + ctob(vm->vm_dsize + vm->vm_ssize) >= p->p_rlimit[RLIMIT_CORE].rlim_cur) { error = EFBIG; /* better error code? */ mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } /* * It may well not be curproc, so grab a reference to its current * credentials. */ kauth_cred_hold(p->p_cred); cred = p->p_cred; /* * Make sure the process has not set-id, to prevent data leaks, * unless it was specifically requested to allow set-id coredumps. */ if (p->p_flag & PK_SUGID) { if (!security_setidcore_dump) { error = EPERM; mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } pattern = security_setidcore_path; } /* Lock, as p_limit and pl_corename might change. */ lim = p->p_limit; mutex_enter(&lim->pl_lock); if (pattern == NULL) { pattern = lim->pl_corename; } error = coredump_buildname(p, name, pattern, MAXPATHLEN); mutex_exit(&lim->pl_lock); if (error) { mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } /* * On a simple filename, see if the filesystem allow us to write * core dumps there. */ lastslash = strrchr(name, '/'); if (!lastslash) { vp = p->p_cwdi->cwdi_cdir; if (vp->v_mount == NULL || (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0) error = EPERM; } mutex_exit(p->p_lock); mutex_exit(proc_lock); if (error) goto done; /* * On a complex filename, see if the filesystem allow us to write * core dumps there. * * XXX: We should have an API that avoids double lookups */ if (lastslash) { char c[2]; if (lastslash - name >= MAXPATHLEN - 2) { error = EPERM; goto done; } c[0] = lastslash[1]; c[1] = lastslash[2]; lastslash[1] = '.'; lastslash[2] = '\0'; error = namei_simple_kernel(name, NSM_FOLLOW_NOEMULROOT, &vp); if (error) goto done; if (vp->v_mount == NULL || (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0) error = EPERM; vrele(vp); if (error) goto done; lastslash[1] = c[0]; lastslash[2] = c[1]; } pb = pathbuf_create(name); if (pb == NULL) { error = ENOMEM; goto done; } NDINIT(&nd, LOOKUP, NOFOLLOW, pb); if ((error = vn_open(&nd, O_CREAT | O_NOFOLLOW | FWRITE, S_IRUSR | S_IWUSR)) != 0) { pathbuf_destroy(pb); goto done; } vp = nd.ni_vp; pathbuf_destroy(pb); /* * Don't dump to: * - non-regular files * - files with links * - files we don't own */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred) || vattr.va_nlink != 1 || vattr.va_uid != kauth_cred_geteuid(cred)) { error = EACCES; goto out; } vattr_null(&vattr); vattr.va_size = 0; if ((p->p_flag & PK_SUGID) && security_setidcore_dump) { vattr.va_uid = security_setidcore_owner; vattr.va_gid = security_setidcore_group; vattr.va_mode = security_setidcore_mode; } VOP_SETATTR(vp, &vattr, cred); p->p_acflag |= ACORE; io.io_lwp = l; io.io_vp = vp; io.io_cred = cred; io.io_offset = 0; /* Now dump the actual core file. */ error = (*p->p_execsw->es_coredump)(l, &io); out: VOP_UNLOCK(vp); error1 = vn_close(vp, FWRITE, cred); if (error == 0) error = error1; done: if (name != NULL) PNBUF_PUT(name); return error; }
/* * Look up a vnode/nfsnode by file handle. * Callers must check for mount points!! * In all cases, a pointer to a * nfsnode structure is returned. */ int nfs_nget1(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp, int lkflags) { struct nfsnode *np; struct vnode *vp; struct nfsmount *nmp = VFSTONFS(mntp); int error; struct fh_match fhm; fhm.fhm_fhp = fhp; fhm.fhm_fhsize = fhsize; loop: rw_enter(&nmp->nm_rbtlock, RW_READER); np = rb_tree_find_node(&nmp->nm_rbtree, &fhm); if (np != NULL) { vp = NFSTOV(np); mutex_enter(vp->v_interlock); rw_exit(&nmp->nm_rbtlock); error = vget(vp, LK_EXCLUSIVE | lkflags); if (error == EBUSY) return error; if (error) goto loop; *npp = np; return(0); } rw_exit(&nmp->nm_rbtlock); error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, NULL, &vp); if (error) { *npp = 0; return (error); } np = pool_get(&nfs_node_pool, PR_WAITOK); memset(np, 0, sizeof *np); np->n_vnode = vp; /* * Insert the nfsnode in the hash queue for its new file handle */ if (fhsize > NFS_SMALLFH) { np->n_fhp = kmem_alloc(fhsize, KM_SLEEP); } else np->n_fhp = &np->n_fh; memcpy(np->n_fhp, fhp, fhsize); np->n_fhsize = fhsize; np->n_accstamp = -1; np->n_vattr = pool_get(&nfs_vattr_pool, PR_WAITOK); rw_enter(&nmp->nm_rbtlock, RW_WRITER); if (NULL != rb_tree_find_node(&nmp->nm_rbtree, &fhm)) { rw_exit(&nmp->nm_rbtlock); if (fhsize > NFS_SMALLFH) { kmem_free(np->n_fhp, fhsize); } pool_put(&nfs_vattr_pool, np->n_vattr); pool_put(&nfs_node_pool, np); ungetnewvnode(vp); goto loop; } vp->v_data = np; genfs_node_init(vp, &nfs_genfsops); /* * Initalize read/write creds to useful values. VOP_OPEN will * overwrite these. */ np->n_rcred = curlwp->l_cred; kauth_cred_hold(np->n_rcred); np->n_wcred = curlwp->l_cred; kauth_cred_hold(np->n_wcred); VOP_LOCK(vp, LK_EXCLUSIVE); NFS_INVALIDATE_ATTRCACHE(np); uvm_vnp_setsize(vp, 0); (void)rb_tree_insert_node(&nmp->nm_rbtree, np); rw_exit(&nmp->nm_rbtlock); *npp = np; return (0); }
/* * Dump core, into a file named "progname.core" or "core" (depending on the * value of shortcorename), unless the process was setuid/setgid. */ int coredump(struct lwp *l, const char *pattern) { struct vnode *vp; struct proc *p; struct vmspace *vm; kauth_cred_t cred; struct nameidata nd; struct vattr vattr; struct coredump_iostate io; struct plimit *lim; int error, error1; char *name; name = PNBUF_GET(); p = l->l_proc; vm = p->p_vmspace; mutex_enter(proc_lock); /* p_session */ mutex_enter(p->p_lock); /* * Refuse to core if the data + stack + user size is larger than * the core dump limit. XXX THIS IS WRONG, because of mapped * data. */ if (USPACE + ctob(vm->vm_dsize + vm->vm_ssize) >= p->p_rlimit[RLIMIT_CORE].rlim_cur) { error = EFBIG; /* better error code? */ mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } /* * It may well not be curproc, so grab a reference to its current * credentials. */ kauth_cred_hold(p->p_cred); cred = p->p_cred; /* * The core dump will go in the current working directory. Make * sure that the directory is still there and that the mount flags * allow us to write core dumps there. * * XXX: this is partially bogus, it should be checking the directory * into which the file is actually written - which probably needs * a flag on namei() */ vp = p->p_cwdi->cwdi_cdir; if (vp->v_mount == NULL || (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0) { error = EPERM; mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } /* * Make sure the process has not set-id, to prevent data leaks, * unless it was specifically requested to allow set-id coredumps. */ if (p->p_flag & PK_SUGID) { if (!security_setidcore_dump) { error = EPERM; mutex_exit(p->p_lock); mutex_exit(proc_lock); goto done; } pattern = security_setidcore_path; } /* It is (just) possible for p_limit and pl_corename to change */ lim = p->p_limit; mutex_enter(&lim->pl_lock); if (pattern == NULL) pattern = lim->pl_corename; error = coredump_buildname(p, name, pattern, MAXPATHLEN); mutex_exit(&lim->pl_lock); mutex_exit(p->p_lock); mutex_exit(proc_lock); if (error) goto done; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name); if ((error = vn_open(&nd, O_CREAT | O_NOFOLLOW | FWRITE, S_IRUSR | S_IWUSR)) != 0) goto done; vp = nd.ni_vp; /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred) || vattr.va_nlink != 1) { error = EINVAL; goto out; } VATTR_NULL(&vattr); vattr.va_size = 0; if ((p->p_flag & PK_SUGID) && security_setidcore_dump) { vattr.va_uid = security_setidcore_owner; vattr.va_gid = security_setidcore_group; vattr.va_mode = security_setidcore_mode; } VOP_SETATTR(vp, &vattr, cred); p->p_acflag |= ACORE; io.io_lwp = l; io.io_vp = vp; io.io_cred = cred; io.io_offset = 0; /* Now dump the actual core file. */ error = (*p->p_execsw->es_coredump)(l, &io); out: VOP_UNLOCK(vp, 0); error1 = vn_close(vp, FWRITE, cred); if (error == 0) error = error1; done: if (name != NULL) PNBUF_PUT(name); return error; }