static int /*ARGSUSED*/ pty_allocvp(struct mount *mp, struct lwp *l, struct vnode **vp, dev_t dev, char ms) { int error; struct pathbuf *pb; struct nameidata nd; char name[TTY_NAMESIZE]; error = pty_makename(NULL, l, name, sizeof(name), dev, ms); if (error) return error; pb = pathbuf_create(name); if (pb == NULL) { return ENOMEM; } NDINIT(&nd, LOOKUP, NOFOLLOW|LOCKLEAF, pb); if ((error = namei(&nd)) != 0) { pathbuf_destroy(pb); return error; } *vp = nd.ni_vp; pathbuf_destroy(pb); return 0; }
/* * Search the alternate path for dynamic binary interpreter. If not found * there, check if the interpreter exists in within 'proper' tree. */ int emul_find_interp(struct lwp *l, struct exec_package *epp, const char *itp) { int error; struct pathbuf *pb; struct nameidata nd; unsigned int flags; pb = pathbuf_create(itp); if (pb == NULL) { return ENOMEM; } /* If we haven't found the emulation root already, do so now */ /* Maybe we should remember failures somehow ? */ if (epp->ep_esch->es_emul->e_path != 0 && epp->ep_emul_root == NULL) emul_find_root(l, epp); if (epp->ep_interp != NULL) vrele(epp->ep_interp); /* We need to use the emulation root for the new program, * not the one for the current process. */ if (epp->ep_emul_root == NULL) flags = FOLLOW; else { nd.ni_erootdir = epp->ep_emul_root; /* hack: Pass in the emulation path for ktrace calls */ nd.ni_next = epp->ep_esch->es_emul->e_path; flags = FOLLOW | TRYEMULROOT | EMULROOTSET; } NDINIT(&nd, LOOKUP, flags, pb); error = namei(&nd); if (error != 0) { epp->ep_interp = NULL; return error; } /* Save interpreter in case we actually need to load it */ epp->ep_interp = nd.ni_vp; pathbuf_destroy(pb); return 0; }
/* * kobj_load_vfs: * * Load an object located in the file system. */ int kobj_load_vfs(kobj_t *kop, const char *path, const bool nochroot) { struct nameidata nd; struct pathbuf *pb; kauth_cred_t cred; int error; kobj_t ko; KASSERT(path != NULL); if (strchr(path, '/') == NULL) return ENOENT; cred = kauth_cred_get(); ko = kmem_zalloc(sizeof(*ko), KM_SLEEP); if (ko == NULL) { return ENOMEM; } pb = pathbuf_create(path); if (pb == NULL) { kmem_free(ko, sizeof(*ko)); return ENOMEM; } NDINIT(&nd, LOOKUP, FOLLOW | (nochroot ? NOCHROOT : 0), pb); error = vn_open(&nd, FREAD, 0); if (error != 0) { pathbuf_destroy(pb); kmem_free(ko, sizeof(*ko)); return error; } ko->ko_type = KT_VNODE; kobj_setname(ko, path); ko->ko_source = nd.ni_vp; ko->ko_read = kobj_read_vfs; ko->ko_close = kobj_close_vfs; pathbuf_destroy(pb); *kop = ko; return kobj_load(ko); }
/* * 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); }
static void ptyfs_getinfo(struct ptyfsnode *ptyfs, struct lwp *l) { extern struct ptm_pty *ptyfs_save_ptm, ptm_ptyfspty; if (ptyfs->ptyfs_type == PTYFSroot) { ptyfs->ptyfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP| S_IROTH|S_IXOTH; goto out; } else ptyfs->ptyfs_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP| S_IROTH|S_IWOTH; if (ptyfs_save_ptm != NULL && ptyfs_save_ptm != &ptm_ptyfspty) { int error; struct pathbuf *pb; struct nameidata nd; char ttyname[64]; kauth_cred_t cred; struct vattr va; /* * We support traditional ptys, so we copy the info * from the inode */ if ((error = (*ptyfs_save_ptm->makename)( ptyfs_save_ptm, l, ttyname, sizeof(ttyname), ptyfs->ptyfs_pty, ptyfs->ptyfs_type == PTYFSpts ? 't' : 'p')) != 0) goto out; pb = pathbuf_create(ttyname); if (pb == NULL) { error = ENOMEM; goto out; } NDINIT(&nd, LOOKUP, NOFOLLOW|LOCKLEAF, pb); if ((error = namei(&nd)) != 0) { pathbuf_destroy(pb); goto out; } cred = kauth_cred_alloc(); error = VOP_GETATTR(nd.ni_vp, &va, cred); kauth_cred_free(cred); VOP_UNLOCK(nd.ni_vp); vrele(nd.ni_vp); pathbuf_destroy(pb); if (error) goto out; ptyfs->ptyfs_uid = va.va_uid; ptyfs->ptyfs_gid = va.va_gid; ptyfs->ptyfs_mode = va.va_mode; ptyfs->ptyfs_flags = va.va_flags; ptyfs->ptyfs_birthtime = va.va_birthtime; ptyfs->ptyfs_ctime = va.va_ctime; ptyfs->ptyfs_mtime = va.va_mtime; ptyfs->ptyfs_atime = va.va_atime; return; } out: ptyfs->ptyfs_uid = ptyfs->ptyfs_gid = 0; ptyfs->ptyfs_status |= PTYFS_CHANGE; PTYFS_ITIMES(ptyfs, NULL, NULL, NULL); ptyfs->ptyfs_birthtime = ptyfs->ptyfs_mtime = ptyfs->ptyfs_atime = ptyfs->ptyfs_ctime; ptyfs->ptyfs_flags = 0; }
/* * 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; }
/* * module_load_plist_vfs: * * Load a plist located in the file system into memory. */ static int module_load_plist_vfs(const char *modpath, const bool nochroot, prop_dictionary_t *filedictp) { struct pathbuf *pb; struct nameidata nd; struct stat sb; void *base; char *proppath; const size_t plistsize = 8192; size_t resid; int error, pathlen; KASSERT(filedictp != NULL); base = NULL; proppath = PNBUF_GET(); strcpy(proppath, modpath); pathlen = strlen(proppath); if ((pathlen >= 6) && (strcmp(&proppath[pathlen - 5], ".kmod") == 0)) { strcpy(&proppath[pathlen - 5], ".plist"); } else if (pathlen < MAXPATHLEN - 6) { strcat(proppath, ".plist"); } else { error = ENOENT; goto out1; } /* XXX this makes an unnecessary extra copy of the path */ pb = pathbuf_create(proppath); if (pb == NULL) { error = ENOMEM; goto out1; } NDINIT(&nd, LOOKUP, FOLLOW | (nochroot ? NOCHROOT : 0), pb); error = vn_open(&nd, FREAD, 0); if (error != 0) { goto out2; } error = vn_stat(nd.ni_vp, &sb); if (error != 0) { goto out3; } if (sb.st_size >= (plistsize - 1)) { /* leave space for term \0 */ error = EFBIG; goto out3; } base = kmem_alloc(plistsize, KM_SLEEP); if (base == NULL) { error = ENOMEM; goto out3; } error = vn_rdwr(UIO_READ, nd.ni_vp, base, sb.st_size, 0, UIO_SYSSPACE, IO_NODELOCKED, curlwp->l_cred, &resid, curlwp); *((uint8_t *)base + sb.st_size) = '\0'; if (error == 0 && resid != 0) { error = EFBIG; } if (error != 0) { kmem_free(base, plistsize); base = NULL; goto out3; } *filedictp = prop_dictionary_internalize(base); if (*filedictp == NULL) { error = EINVAL; } kmem_free(base, plistsize); base = NULL; KASSERT(error == 0); out3: VOP_UNLOCK(nd.ni_vp); vn_close(nd.ni_vp, FREAD, kauth_cred_get()); out2: pathbuf_destroy(pb); out1: PNBUF_PUT(proppath); return error; }