Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_descrip.c
Show First 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | static MALLOC_DEFINE(M_FILEDESC_TO_LEADER, "filedesc_to_leader", | ||||
"file desc to leader structures"); | "file desc to leader structures"); | ||||
static MALLOC_DEFINE(M_SIGIO, "sigio", "sigio structures"); | static MALLOC_DEFINE(M_SIGIO, "sigio", "sigio structures"); | ||||
MALLOC_DEFINE(M_FILECAPS, "filecaps", "descriptor capabilities"); | MALLOC_DEFINE(M_FILECAPS, "filecaps", "descriptor capabilities"); | ||||
MALLOC_DECLARE(M_FADVISE); | MALLOC_DECLARE(M_FADVISE); | ||||
static __read_mostly uma_zone_t file_zone; | static __read_mostly uma_zone_t file_zone; | ||||
static __read_mostly uma_zone_t filedesc0_zone; | static __read_mostly uma_zone_t filedesc0_zone; | ||||
static __read_mostly uma_zone_t pathsdesc_zone; | |||||
__read_mostly uma_zone_t pwd_zone; | __read_mostly uma_zone_t pwd_zone; | ||||
VFS_SMR_DECLARE; | VFS_SMR_DECLARE; | ||||
static int closefp(struct filedesc *fdp, int fd, struct file *fp, | static int closefp(struct filedesc *fdp, int fd, struct file *fp, | ||||
struct thread *td, int holdleaders); | struct thread *td, int holdleaders); | ||||
static int fd_first_free(struct filedesc *fdp, int low, int size); | static int fd_first_free(struct filedesc *fdp, int low, int size); | ||||
static void fdgrowtable(struct filedesc *fdp, int nfd); | static void fdgrowtable(struct filedesc *fdp, int nfd); | ||||
static void fdgrowtable_exp(struct filedesc *fdp, int nfd); | static void fdgrowtable_exp(struct filedesc *fdp, int nfd); | ||||
▲ Show 20 Lines • Show All 1,901 Lines • ▼ Show 20 Lines | finstall(struct thread *td, struct file *fp, int *fd, int flags, | ||||
} | } | ||||
_finstall(fdp, fp, *fd, flags, fcaps); | _finstall(fdp, fp, *fd, flags, fcaps); | ||||
FILEDESC_XUNLOCK(fdp); | FILEDESC_XUNLOCK(fdp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Build a new filedesc structure from another. | * Build a new filedesc structure from another. | ||||
* Copy the current, root, and jail root vnode references. | |||||
* | * | ||||
* If fdp is not NULL, return with it shared locked. | * If fdp is not NULL, return with it shared locked. | ||||
*/ | */ | ||||
struct filedesc * | struct filedesc * | ||||
fdinit(struct filedesc *fdp, bool prepfiles, int *lastfile) | fdinit(struct filedesc *fdp, bool prepfiles, int *lastfile) | ||||
{ | { | ||||
struct filedesc0 *newfdp0; | struct filedesc0 *newfdp0; | ||||
struct filedesc *newfdp; | struct filedesc *newfdp; | ||||
struct pwd *newpwd; | |||||
if (prepfiles) | if (prepfiles) | ||||
MPASS(lastfile != NULL); | MPASS(lastfile != NULL); | ||||
else | else | ||||
MPASS(lastfile == NULL); | MPASS(lastfile == NULL); | ||||
newfdp0 = uma_zalloc(filedesc0_zone, M_WAITOK | M_ZERO); | newfdp0 = uma_zalloc(filedesc0_zone, M_WAITOK | M_ZERO); | ||||
newfdp = &newfdp0->fd_fd; | newfdp = &newfdp0->fd_fd; | ||||
/* Create the file descriptor table. */ | /* Create the file descriptor table. */ | ||||
FILEDESC_LOCK_INIT(newfdp); | FILEDESC_LOCK_INIT(newfdp); | ||||
refcount_init(&newfdp->fd_refcnt, 1); | refcount_init(&newfdp->fd_refcnt, 1); | ||||
refcount_init(&newfdp->fd_holdcnt, 1); | refcount_init(&newfdp->fd_holdcnt, 1); | ||||
newfdp->fd_cmask = CMASK; | |||||
newfdp->fd_map = newfdp0->fd_dmap; | newfdp->fd_map = newfdp0->fd_dmap; | ||||
newfdp->fd_files = (struct fdescenttbl *)&newfdp0->fd_dfiles; | newfdp->fd_files = (struct fdescenttbl *)&newfdp0->fd_dfiles; | ||||
newfdp->fd_files->fdt_nfiles = NDFILE; | newfdp->fd_files->fdt_nfiles = NDFILE; | ||||
if (fdp == NULL) { | if (fdp == NULL) | ||||
newpwd = pwd_alloc(); | |||||
smr_serialized_store(&newfdp->fd_pwd, newpwd, true); | |||||
return (newfdp); | return (newfdp); | ||||
} | |||||
FILEDESC_SLOCK(fdp); | FILEDESC_SLOCK(fdp); | ||||
newpwd = pwd_hold_filedesc(fdp); | |||||
smr_serialized_store(&newfdp->fd_pwd, newpwd, true); | |||||
if (!prepfiles) { | if (!prepfiles) { | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
return (newfdp); | return (newfdp); | ||||
} | } | ||||
for (;;) { | for (;;) { | ||||
*lastfile = fdlastfile(fdp); | *lastfile = fdlastfile(fdp); | ||||
if (*lastfile < newfdp->fd_nfiles) | if (*lastfile < newfdp->fd_nfiles) | ||||
break; | break; | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
fdgrowtable(newfdp, *lastfile + 1); | fdgrowtable(newfdp, *lastfile + 1); | ||||
FILEDESC_SLOCK(fdp); | FILEDESC_SLOCK(fdp); | ||||
} | } | ||||
return (newfdp); | return (newfdp); | ||||
} | } | ||||
/* | |||||
* Build a pathsdesc structure from another. | |||||
* Copy the current, root, and jail root vnode references. | |||||
* | |||||
* If pdp is not NULL, return with it shared locked. | |||||
*/ | |||||
struct pathsdesc * | |||||
pdinit(struct pathsdesc *pdp, bool keeplock) | |||||
{ | |||||
struct pathsdesc *newpdp; | |||||
struct pwd *newpwd; | |||||
newpdp = uma_zalloc(pathsdesc_zone, M_WAITOK | M_ZERO); | |||||
PATHSDESC_LOCK_INIT(newpdp); | |||||
refcount_init(&newpdp->pd_refcount, 1); | |||||
newpdp->pd_cmask = CMASK; | |||||
if (pdp == NULL) { | |||||
newpwd = pwd_alloc(); | |||||
smr_serialized_store(&newpdp->pd_pwd, newpwd, true); | |||||
return (newpdp); | |||||
} | |||||
PATHSDESC_SLOCK(pdp); | |||||
newpwd = pwd_hold_pathsdesc(pdp); | |||||
smr_serialized_store(&newpdp->pd_pwd, newpwd, true); | |||||
if (!keeplock) | |||||
PATHSDESC_SUNLOCK(pdp); | |||||
return (newpdp); | |||||
} | |||||
static struct filedesc * | static struct filedesc * | ||||
fdhold(struct proc *p) | fdhold(struct proc *p) | ||||
{ | { | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
fdp = p->p_fd; | fdp = p->p_fd; | ||||
if (fdp != NULL) | if (fdp != NULL) | ||||
refcount_acquire(&fdp->fd_holdcnt); | refcount_acquire(&fdp->fd_holdcnt); | ||||
return (fdp); | return (fdp); | ||||
} | } | ||||
static struct pathsdesc * | |||||
pdhold(struct proc *p) | |||||
{ | |||||
struct pathsdesc *pdp; | |||||
PROC_LOCK_ASSERT(p, MA_OWNED); | |||||
pdp = p->p_pd; | |||||
if (pdp != NULL) | |||||
refcount_acquire(&pdp->pd_refcount); | |||||
return (pdp); | |||||
} | |||||
static void | static void | ||||
fddrop(struct filedesc *fdp) | fddrop(struct filedesc *fdp) | ||||
{ | { | ||||
if (fdp->fd_holdcnt > 1) { | if (fdp->fd_holdcnt > 1) { | ||||
if (refcount_release(&fdp->fd_holdcnt) == 0) | if (refcount_release(&fdp->fd_holdcnt) == 0) | ||||
return; | return; | ||||
} | } | ||||
FILEDESC_LOCK_DESTROY(fdp); | FILEDESC_LOCK_DESTROY(fdp); | ||||
uma_zfree(filedesc0_zone, fdp); | uma_zfree(filedesc0_zone, fdp); | ||||
} | } | ||||
static void | |||||
pddrop(struct pathsdesc *pdp) | |||||
{ | |||||
struct pwd *pwd; | |||||
if (refcount_release_if_not_last(&pdp->pd_refcount)) | |||||
return; | |||||
PATHSDESC_XLOCK(pdp); | |||||
if (refcount_release(&pdp->pd_refcount) == 0) { | |||||
PATHSDESC_XUNLOCK(pdp); | |||||
return; | |||||
} | |||||
pwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | |||||
pwd_set(pdp, NULL); | |||||
PATHSDESC_XUNLOCK(pdp); | |||||
pwd_drop(pwd); | |||||
PATHSDESC_LOCK_DESTROY(pdp); | |||||
uma_zfree(pathsdesc_zone, pdp); | |||||
} | |||||
/* | /* | ||||
* Share a filedesc structure. | * Share a filedesc structure. | ||||
*/ | */ | ||||
struct filedesc * | struct filedesc * | ||||
fdshare(struct filedesc *fdp) | fdshare(struct filedesc *fdp) | ||||
{ | { | ||||
refcount_acquire(&fdp->fd_refcnt); | refcount_acquire(&fdp->fd_refcnt); | ||||
return (fdp); | return (fdp); | ||||
} | } | ||||
/* | /* | ||||
* Share a pathsdesc structure. | |||||
*/ | |||||
struct pathsdesc * | |||||
pdshare(struct pathsdesc *pdp) | |||||
{ | |||||
refcount_acquire(&pdp->pd_refcount); | |||||
return (pdp); | |||||
} | |||||
/* | |||||
* Unshare a filedesc structure, if necessary by making a copy | * Unshare a filedesc structure, if necessary by making a copy | ||||
*/ | */ | ||||
void | void | ||||
fdunshare(struct thread *td) | fdunshare(struct thread *td) | ||||
{ | { | ||||
struct filedesc *tmp; | struct filedesc *tmp; | ||||
struct proc *p = td->td_proc; | struct proc *p = td->td_proc; | ||||
if (p->p_fd->fd_refcnt == 1) | if (p->p_fd->fd_refcnt == 1) | ||||
return; | return; | ||||
tmp = fdcopy(p->p_fd); | tmp = fdcopy(p->p_fd); | ||||
fdescfree(td); | fdescfree(td); | ||||
p->p_fd = tmp; | p->p_fd = tmp; | ||||
} | } | ||||
/* | |||||
* Unshare a pathsdesc structure. | |||||
*/ | |||||
void | void | ||||
pdunshare(struct thread *td) | |||||
{ | |||||
struct pathsdesc *pdp; | |||||
struct proc *p; | |||||
p = td->td_proc; | |||||
/* Not shared. */ | |||||
if (p->p_pd->pd_refcount == 1) | |||||
return; | |||||
pdp = pdcopy(p->p_pd); | |||||
pdescfree(td); | |||||
p->p_pd = pdp; | |||||
} | |||||
void | |||||
fdinstall_remapped(struct thread *td, struct filedesc *fdp) | fdinstall_remapped(struct thread *td, struct filedesc *fdp) | ||||
{ | { | ||||
fdescfree(td); | fdescfree(td); | ||||
td->td_proc->p_fd = fdp; | td->td_proc->p_fd = fdp; | ||||
} | } | ||||
/* | /* | ||||
Show All 23 Lines | for (i = 0; i <= lastfile; ++i) { | ||||
} | } | ||||
nfde = &newfdp->fd_ofiles[i]; | nfde = &newfdp->fd_ofiles[i]; | ||||
*nfde = *ofde; | *nfde = *ofde; | ||||
filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); | filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); | ||||
fdused_init(newfdp, i); | fdused_init(newfdp, i); | ||||
} | } | ||||
if (newfdp->fd_freefile == -1) | if (newfdp->fd_freefile == -1) | ||||
newfdp->fd_freefile = i; | newfdp->fd_freefile = i; | ||||
newfdp->fd_cmask = fdp->fd_cmask; | |||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
return (newfdp); | return (newfdp); | ||||
} | } | ||||
/* | /* | ||||
* Copy a pathsdesc structure. | |||||
*/ | |||||
struct pathsdesc * | |||||
pdcopy(struct pathsdesc *pdp) | |||||
{ | |||||
struct pathsdesc *newpdp; | |||||
MPASS(pdp != NULL); | |||||
newpdp = pdinit(pdp, true); | |||||
newpdp->pd_cmask = pdp->pd_cmask; | |||||
PATHSDESC_SUNLOCK(pdp); | |||||
return (newpdp); | |||||
} | |||||
/* | |||||
* Copies a filedesc structure, while remapping all file descriptors | * Copies a filedesc structure, while remapping all file descriptors | ||||
* stored inside using a translation table. | * stored inside using a translation table. | ||||
* | * | ||||
* File descriptors are copied over to the new file descriptor table, | * File descriptors are copied over to the new file descriptor table, | ||||
* regardless of whether the close-on-exec flag is set. | * regardless of whether the close-on-exec flag is set. | ||||
*/ | */ | ||||
int | int | ||||
fdcopy_remapped(struct filedesc *fdp, const int *fds, size_t nfds, | fdcopy_remapped(struct filedesc *fdp, const int *fds, size_t nfds, | ||||
Show All 34 Lines | if (!fhold(ofde->fde_file)) { | ||||
error = EBADF; | error = EBADF; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
nfde = &newfdp->fd_ofiles[i]; | nfde = &newfdp->fd_ofiles[i]; | ||||
*nfde = *ofde; | *nfde = *ofde; | ||||
filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); | filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); | ||||
fdused_init(newfdp, i); | fdused_init(newfdp, i); | ||||
} | } | ||||
newfdp->fd_cmask = fdp->fd_cmask; | |||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
*ret = newfdp; | *ret = newfdp; | ||||
return (0); | return (0); | ||||
bad: | bad: | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
fdescfree_remapped(newfdp); | fdescfree_remapped(newfdp); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | fdescfree_fds(struct thread *td, struct filedesc *fdp, bool needclose) | ||||
fddrop(fdp); | fddrop(fdp); | ||||
} | } | ||||
void | void | ||||
fdescfree(struct thread *td) | fdescfree(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct pwd *pwd; | |||||
p = td->td_proc; | p = td->td_proc; | ||||
fdp = p->p_fd; | fdp = p->p_fd; | ||||
MPASS(fdp != NULL); | MPASS(fdp != NULL); | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (RACCT_ENABLED()) | if (RACCT_ENABLED()) | ||||
racct_set_unlocked(p, RACCT_NOFILE, 0); | racct_set_unlocked(p, RACCT_NOFILE, 0); | ||||
#endif | #endif | ||||
if (p->p_fdtol != NULL) | if (p->p_fdtol != NULL) | ||||
fdclearlocks(td); | fdclearlocks(td); | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
p->p_fd = NULL; | p->p_fd = NULL; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (refcount_release(&fdp->fd_refcnt) == 0) | if (refcount_release(&fdp->fd_refcnt) == 0) | ||||
return; | return; | ||||
FILEDESC_XLOCK(fdp); | fdescfree_fds(td, fdp, 1); | ||||
pwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | } | ||||
pwd_set(fdp, NULL); | |||||
FILEDESC_XUNLOCK(fdp); | |||||
pwd_drop(pwd); | void | ||||
pdescfree(struct thread *td) | |||||
{ | |||||
struct proc *p; | |||||
struct pathsdesc *pdp; | |||||
fdescfree_fds(td, fdp, 1); | p = td->td_proc; | ||||
pdp = p->p_pd; | |||||
MPASS(pdp != NULL); | |||||
PROC_LOCK(p); | |||||
p->p_pd = NULL; | |||||
PROC_UNLOCK(p); | |||||
pddrop(pdp); | |||||
} | } | ||||
void | void | ||||
fdescfree_remapped(struct filedesc *fdp) | fdescfree_remapped(struct filedesc *fdp) | ||||
{ | { | ||||
pwd_drop(smr_serialized_load(&fdp->fd_pwd, true)); | |||||
fdescfree_fds(curthread, fdp, 0); | fdescfree_fds(curthread, fdp, 0); | ||||
} | } | ||||
/* | /* | ||||
* For setugid programs, we don't want to people to use that setugidness | * For setugid programs, we don't want to people to use that setugidness | ||||
* to generate error messages which write to a file which otherwise would | * to generate error messages which write to a file which otherwise would | ||||
* otherwise be off-limits to the process. We check for filesystems where | * otherwise be off-limits to the process. We check for filesystems where | ||||
* the vnode can change out from under us after execve (like [lin]procfs). | * the vnode can change out from under us after execve (like [lin]procfs). | ||||
▲ Show 20 Lines • Show All 992 Lines • ▼ Show 20 Lines | |||||
* any filedescriptors are open directories. | * any filedescriptors are open directories. | ||||
*/ | */ | ||||
static int | static int | ||||
chroot_refuse_vdir_fds(struct filedesc *fdp) | chroot_refuse_vdir_fds(struct filedesc *fdp) | ||||
{ | { | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct file *fp; | struct file *fp; | ||||
int fd, lastfile; | int fd, lastfile; | ||||
int error; | |||||
FILEDESC_LOCK_ASSERT(fdp); | FILEDESC_SLOCK(fdp); | ||||
lastfile = fdlastfile(fdp); | lastfile = fdlastfile(fdp); | ||||
for (fd = 0; fd <= lastfile; fd++) { | for (fd = 0; fd <= lastfile; fd++) { | ||||
fp = fget_locked(fdp, fd); | fp = fget_locked(fdp, fd); | ||||
if (fp == NULL) | if (fp == NULL) | ||||
continue; | continue; | ||||
if (fp->f_type == DTYPE_VNODE) { | if (fp->f_type == DTYPE_VNODE) { | ||||
vp = fp->f_vnode; | vp = fp->f_vnode; | ||||
if (vp->v_type == VDIR) | if (vp->v_type == VDIR) { | ||||
return (EPERM); | error = EPERM; | ||||
goto out; | |||||
} | } | ||||
} | } | ||||
return (0); | |||||
} | } | ||||
error = 0; | |||||
out: | |||||
FILEDESC_SUNLOCK(fdp); | |||||
return (error); | |||||
} | |||||
static void | static void | ||||
pwd_fill(struct pwd *oldpwd, struct pwd *newpwd) | pwd_fill(struct pwd *oldpwd, struct pwd *newpwd) | ||||
{ | { | ||||
if (newpwd->pwd_cdir == NULL && oldpwd->pwd_cdir != NULL) { | if (newpwd->pwd_cdir == NULL && oldpwd->pwd_cdir != NULL) { | ||||
vrefact(oldpwd->pwd_cdir); | vrefact(oldpwd->pwd_cdir); | ||||
newpwd->pwd_cdir = oldpwd->pwd_cdir; | newpwd->pwd_cdir = oldpwd->pwd_cdir; | ||||
} | } | ||||
if (newpwd->pwd_rdir == NULL && oldpwd->pwd_rdir != NULL) { | if (newpwd->pwd_rdir == NULL && oldpwd->pwd_rdir != NULL) { | ||||
vrefact(oldpwd->pwd_rdir); | vrefact(oldpwd->pwd_rdir); | ||||
newpwd->pwd_rdir = oldpwd->pwd_rdir; | newpwd->pwd_rdir = oldpwd->pwd_rdir; | ||||
} | } | ||||
if (newpwd->pwd_jdir == NULL && oldpwd->pwd_jdir != NULL) { | if (newpwd->pwd_jdir == NULL && oldpwd->pwd_jdir != NULL) { | ||||
vrefact(oldpwd->pwd_jdir); | vrefact(oldpwd->pwd_jdir); | ||||
newpwd->pwd_jdir = oldpwd->pwd_jdir; | newpwd->pwd_jdir = oldpwd->pwd_jdir; | ||||
} | } | ||||
} | } | ||||
struct pwd * | struct pwd * | ||||
pwd_hold_filedesc(struct filedesc *fdp) | pwd_hold_pathsdesc(struct pathsdesc *pdp) | ||||
{ | { | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
FILEDESC_LOCK_ASSERT(fdp); | PATHSDESC_ASSERT_LOCKED(pdp); | ||||
pwd = FILEDESC_LOCKED_LOAD_PWD(fdp); | pwd = PATHSDESC_LOCKED_LOAD_PWD(pdp); | ||||
if (pwd != NULL) | if (pwd != NULL) | ||||
refcount_acquire(&pwd->pwd_refcount); | refcount_acquire(&pwd->pwd_refcount); | ||||
return (pwd); | return (pwd); | ||||
} | } | ||||
bool | bool | ||||
pwd_hold_smr(struct pwd *pwd) | pwd_hold_smr(struct pwd *pwd) | ||||
{ | { | ||||
MPASS(pwd != NULL); | MPASS(pwd != NULL); | ||||
if (__predict_true(refcount_acquire_if_not_zero(&pwd->pwd_refcount))) { | if (__predict_true(refcount_acquire_if_not_zero(&pwd->pwd_refcount))) { | ||||
return (true); | return (true); | ||||
} | } | ||||
return (false); | return (false); | ||||
} | } | ||||
struct pwd * | struct pwd * | ||||
pwd_hold(struct thread *td) | pwd_hold(struct thread *td) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
fdp = td->td_proc->p_fd; | pdp = td->td_proc->p_pd; | ||||
vfs_smr_enter(); | vfs_smr_enter(); | ||||
pwd = vfs_smr_entered_load(&fdp->fd_pwd); | pwd = vfs_smr_entered_load(&pdp->pd_pwd); | ||||
if (pwd_hold_smr(pwd)) { | if (pwd_hold_smr(pwd)) { | ||||
vfs_smr_exit(); | vfs_smr_exit(); | ||||
return (pwd); | return (pwd); | ||||
} | } | ||||
vfs_smr_exit(); | vfs_smr_exit(); | ||||
FILEDESC_SLOCK(fdp); | PATHSDESC_SLOCK(pdp); | ||||
pwd = pwd_hold_filedesc(fdp); | pwd = pwd_hold_pathsdesc(pdp); | ||||
MPASS(pwd != NULL); | MPASS(pwd != NULL); | ||||
FILEDESC_SUNLOCK(fdp); | PATHSDESC_SUNLOCK(pdp); | ||||
return (pwd); | return (pwd); | ||||
} | } | ||||
struct pwd * | struct pwd * | ||||
pwd_get_smr(void) | pwd_get_smr(void) | ||||
{ | { | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
pwd = vfs_smr_entered_load(&curproc->p_fd->fd_pwd); | pwd = vfs_smr_entered_load(&curproc->p_pd->pd_pwd); | ||||
MPASS(pwd != NULL); | MPASS(pwd != NULL); | ||||
return (pwd); | return (pwd); | ||||
} | } | ||||
static struct pwd * | static struct pwd * | ||||
pwd_alloc(void) | pwd_alloc(void) | ||||
{ | { | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
Show All 23 Lines | |||||
/* | /* | ||||
* Common routine for kern_chroot() and jail_attach(). The caller is | * Common routine for kern_chroot() and jail_attach(). The caller is | ||||
* responsible for invoking priv_check() and mac_vnode_check_chroot() to | * responsible for invoking priv_check() and mac_vnode_check_chroot() to | ||||
* authorize this operation. | * authorize this operation. | ||||
*/ | */ | ||||
int | int | ||||
pwd_chroot(struct thread *td, struct vnode *vp) | pwd_chroot(struct thread *td, struct vnode *vp) | ||||
{ | { | ||||
struct pathsdesc *pdp; | |||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct pwd *newpwd, *oldpwd; | struct pwd *newpwd, *oldpwd; | ||||
int error; | int error; | ||||
fdp = td->td_proc->p_fd; | fdp = td->td_proc->p_fd; | ||||
pdp = td->td_proc->p_pd; | |||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
if (chroot_allow_open_directories == 0 || | if (chroot_allow_open_directories == 0 || | ||||
(chroot_allow_open_directories == 1 && | (chroot_allow_open_directories == 1 && | ||||
oldpwd->pwd_rdir != rootvnode)) { | oldpwd->pwd_rdir != rootvnode)) { | ||||
error = chroot_refuse_vdir_fds(fdp); | error = chroot_refuse_vdir_fds(fdp); | ||||
if (error != 0) { | if (error != 0) { | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(newpwd); | pwd_drop(newpwd); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
vrefact(vp); | vrefact(vp); | ||||
newpwd->pwd_rdir = vp; | newpwd->pwd_rdir = vp; | ||||
if (oldpwd->pwd_jdir == NULL) { | if (oldpwd->pwd_jdir == NULL) { | ||||
vrefact(vp); | vrefact(vp); | ||||
newpwd->pwd_jdir = vp; | newpwd->pwd_jdir = vp; | ||||
} | } | ||||
pwd_fill(oldpwd, newpwd); | pwd_fill(oldpwd, newpwd); | ||||
pwd_set(fdp, newpwd); | pwd_set(pdp, newpwd); | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(oldpwd); | pwd_drop(oldpwd); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
pwd_chdir(struct thread *td, struct vnode *vp) | pwd_chdir(struct thread *td, struct vnode *vp) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *newpwd, *oldpwd; | struct pwd *newpwd, *oldpwd; | ||||
VNPASS(vp->v_usecount > 0, vp); | VNPASS(vp->v_usecount > 0, vp); | ||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
fdp = td->td_proc->p_fd; | pdp = td->td_proc->p_pd; | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
newpwd->pwd_cdir = vp; | newpwd->pwd_cdir = vp; | ||||
pwd_fill(oldpwd, newpwd); | pwd_fill(oldpwd, newpwd); | ||||
pwd_set(fdp, newpwd); | pwd_set(pdp, newpwd); | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(oldpwd); | pwd_drop(oldpwd); | ||||
} | } | ||||
void | void | ||||
pwd_ensure_dirs(void) | pwd_ensure_dirs(void) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *oldpwd, *newpwd; | struct pwd *oldpwd, *newpwd; | ||||
fdp = curproc->p_fd; | pdp = curproc->p_pd; | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
if (oldpwd->pwd_cdir != NULL && oldpwd->pwd_rdir != NULL) { | if (oldpwd->pwd_cdir != NULL && oldpwd->pwd_rdir != NULL) { | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
return; | return; | ||||
} | } | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
pwd_fill(oldpwd, newpwd); | pwd_fill(oldpwd, newpwd); | ||||
if (newpwd->pwd_cdir == NULL) { | if (newpwd->pwd_cdir == NULL) { | ||||
vrefact(rootvnode); | vrefact(rootvnode); | ||||
newpwd->pwd_cdir = rootvnode; | newpwd->pwd_cdir = rootvnode; | ||||
} | } | ||||
if (newpwd->pwd_rdir == NULL) { | if (newpwd->pwd_rdir == NULL) { | ||||
vrefact(rootvnode); | vrefact(rootvnode); | ||||
newpwd->pwd_rdir = rootvnode; | newpwd->pwd_rdir = rootvnode; | ||||
} | } | ||||
pwd_set(fdp, newpwd); | pwd_set(pdp, newpwd); | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(oldpwd); | pwd_drop(oldpwd); | ||||
} | } | ||||
void | void | ||||
pwd_set_rootvnode(void) | pwd_set_rootvnode(void) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *oldpwd, *newpwd; | struct pwd *oldpwd, *newpwd; | ||||
fdp = curproc->p_fd; | pdp = curproc->p_pd; | ||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
vrefact(rootvnode); | vrefact(rootvnode); | ||||
newpwd->pwd_cdir = rootvnode; | newpwd->pwd_cdir = rootvnode; | ||||
vrefact(rootvnode); | vrefact(rootvnode); | ||||
newpwd->pwd_rdir = rootvnode; | newpwd->pwd_rdir = rootvnode; | ||||
pwd_fill(oldpwd, newpwd); | pwd_fill(oldpwd, newpwd); | ||||
pwd_set(fdp, newpwd); | pwd_set(pdp, newpwd); | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(oldpwd); | pwd_drop(oldpwd); | ||||
} | } | ||||
/* | /* | ||||
* Scan all active processes and prisons to see if any of them have a current | * Scan all active processes and prisons to see if any of them have a current | ||||
* or root directory of `olddp'. If so, replace them with the new mount point. | * or root directory of `olddp'. If so, replace them with the new mount point. | ||||
*/ | */ | ||||
void | void | ||||
mountcheckdirs(struct vnode *olddp, struct vnode *newdp) | mountcheckdirs(struct vnode *olddp, struct vnode *newdp) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *newpwd, *oldpwd; | struct pwd *newpwd, *oldpwd; | ||||
struct prison *pr; | struct prison *pr; | ||||
struct proc *p; | struct proc *p; | ||||
int nrele; | int nrele; | ||||
if (vrefcnt(olddp) == 1) | if (vrefcnt(olddp) == 1) | ||||
return; | return; | ||||
nrele = 0; | nrele = 0; | ||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
sx_slock(&allproc_lock); | sx_slock(&allproc_lock); | ||||
FOREACH_PROC_IN_SYSTEM(p) { | FOREACH_PROC_IN_SYSTEM(p) { | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
fdp = fdhold(p); | pdp = pdhold(p); | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (fdp == NULL) | if (pdp == NULL) | ||||
continue; | continue; | ||||
FILEDESC_XLOCK(fdp); | PATHSDESC_XLOCK(pdp); | ||||
oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); | oldpwd = PATHSDESC_XLOCKED_LOAD_PWD(pdp); | ||||
if (oldpwd == NULL || | if (oldpwd == NULL || | ||||
(oldpwd->pwd_cdir != olddp && | (oldpwd->pwd_cdir != olddp && | ||||
oldpwd->pwd_rdir != olddp && | oldpwd->pwd_rdir != olddp && | ||||
oldpwd->pwd_jdir != olddp)) { | oldpwd->pwd_jdir != olddp)) { | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
fddrop(fdp); | pddrop(pdp); | ||||
continue; | continue; | ||||
} | } | ||||
if (oldpwd->pwd_cdir == olddp) { | if (oldpwd->pwd_cdir == olddp) { | ||||
vrefact(newdp); | vrefact(newdp); | ||||
newpwd->pwd_cdir = newdp; | newpwd->pwd_cdir = newdp; | ||||
} | } | ||||
if (oldpwd->pwd_rdir == olddp) { | if (oldpwd->pwd_rdir == olddp) { | ||||
vrefact(newdp); | vrefact(newdp); | ||||
newpwd->pwd_rdir = newdp; | newpwd->pwd_rdir = newdp; | ||||
} | } | ||||
if (oldpwd->pwd_jdir == olddp) { | if (oldpwd->pwd_jdir == olddp) { | ||||
vrefact(newdp); | vrefact(newdp); | ||||
newpwd->pwd_jdir = newdp; | newpwd->pwd_jdir = newdp; | ||||
} | } | ||||
pwd_fill(oldpwd, newpwd); | pwd_fill(oldpwd, newpwd); | ||||
pwd_set(fdp, newpwd); | pwd_set(pdp, newpwd); | ||||
FILEDESC_XUNLOCK(fdp); | PATHSDESC_XUNLOCK(pdp); | ||||
pwd_drop(oldpwd); | pwd_drop(oldpwd); | ||||
fddrop(fdp); | pddrop(pdp); | ||||
newpwd = pwd_alloc(); | newpwd = pwd_alloc(); | ||||
} | } | ||||
sx_sunlock(&allproc_lock); | sx_sunlock(&allproc_lock); | ||||
pwd_drop(newpwd); | pwd_drop(newpwd); | ||||
if (rootvnode == olddp) { | if (rootvnode == olddp) { | ||||
vrefact(newdp); | vrefact(newdp); | ||||
rootvnode = newdp; | rootvnode = newdp; | ||||
nrele++; | nrele++; | ||||
▲ Show 20 Lines • Show All 256 Lines • ▼ Show 20 Lines | if ((flags & KERN_FILEDESC_PACK_KINFO) != 0) | ||||
pack_kinfo(kif); | pack_kinfo(kif); | ||||
else | else | ||||
kif->kf_structsize = roundup2(sizeof(*kif), sizeof(uint64_t)); | kif->kf_structsize = roundup2(sizeof(*kif), sizeof(uint64_t)); | ||||
vrele(vp); | vrele(vp); | ||||
} | } | ||||
struct export_fd_buf { | struct export_fd_buf { | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct pathsdesc *pdp; | |||||
struct sbuf *sb; | struct sbuf *sb; | ||||
ssize_t remainder; | ssize_t remainder; | ||||
struct kinfo_file kif; | struct kinfo_file kif; | ||||
int flags; | int flags; | ||||
}; | }; | ||||
static int | static int | ||||
export_kinfo_to_sb(struct export_fd_buf *efbuf) | export_kinfo_to_sb(struct export_fd_buf *efbuf) | ||||
Show All 31 Lines | |||||
static int | static int | ||||
export_vnode_to_sb(struct vnode *vp, int fd, int fflags, | export_vnode_to_sb(struct vnode *vp, int fd, int fflags, | ||||
struct export_fd_buf *efbuf) | struct export_fd_buf *efbuf) | ||||
{ | { | ||||
int error; | int error; | ||||
if (efbuf->remainder == 0) | if (efbuf->remainder == 0) | ||||
return (0); | return (0); | ||||
if (efbuf->fdp != NULL) | if (efbuf->pdp != NULL) | ||||
FILEDESC_SUNLOCK(efbuf->fdp); | PATHSDESC_SUNLOCK(efbuf->pdp); | ||||
mjg: given that pwd is copy on write and it gets refed above, this code can drop both fdp and pdp. | |||||
Not Done Inline Actionssome of the cleanup is applicable to stock head, i'll make the changes today mjg: some of the cleanup is applicable to stock head, i'll make the changes today | |||||
export_vnode_to_kinfo(vp, fd, fflags, &efbuf->kif, efbuf->flags); | export_vnode_to_kinfo(vp, fd, fflags, &efbuf->kif, efbuf->flags); | ||||
error = export_kinfo_to_sb(efbuf); | error = export_kinfo_to_sb(efbuf); | ||||
if (efbuf->fdp != NULL) | if (efbuf->pdp != NULL) | ||||
FILEDESC_SLOCK(efbuf->fdp); | PATHSDESC_SLOCK(efbuf->pdp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Store a process file descriptor information to sbuf. | * Store a process file descriptor information to sbuf. | ||||
* | * | ||||
* Takes a locked proc as argument, and returns with the proc unlocked. | * Takes a locked proc as argument, and returns with the proc unlocked. | ||||
*/ | */ | ||||
int | int | ||||
kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, | kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, | ||||
int flags) | int flags) | ||||
{ | { | ||||
struct file *fp; | struct file *fp; | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct pathsdesc *pdp; | |||||
struct export_fd_buf *efbuf; | struct export_fd_buf *efbuf; | ||||
struct vnode *cttyvp, *textvp, *tracevp; | struct vnode *cttyvp, *textvp, *tracevp; | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
int error, i, lastfile; | int error, i, lastfile; | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
/* ktrace vnode */ | /* ktrace vnode */ | ||||
tracevp = p->p_tracevp; | tracevp = p->p_tracevp; | ||||
if (tracevp != NULL) | if (tracevp != NULL) | ||||
vrefact(tracevp); | vrefact(tracevp); | ||||
/* text vnode */ | /* text vnode */ | ||||
textvp = p->p_textvp; | textvp = p->p_textvp; | ||||
if (textvp != NULL) | if (textvp != NULL) | ||||
vrefact(textvp); | vrefact(textvp); | ||||
/* Controlling tty. */ | /* Controlling tty. */ | ||||
cttyvp = NULL; | cttyvp = NULL; | ||||
if (p->p_pgrp != NULL && p->p_pgrp->pg_session != NULL) { | if (p->p_pgrp != NULL && p->p_pgrp->pg_session != NULL) { | ||||
cttyvp = p->p_pgrp->pg_session->s_ttyvp; | cttyvp = p->p_pgrp->pg_session->s_ttyvp; | ||||
if (cttyvp != NULL) | if (cttyvp != NULL) | ||||
vrefact(cttyvp); | vrefact(cttyvp); | ||||
} | } | ||||
fdp = fdhold(p); | fdp = fdhold(p); | ||||
pdp = pdhold(p); | |||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); | efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); | ||||
efbuf->fdp = NULL; | efbuf->fdp = NULL; | ||||
efbuf->pdp = NULL; | |||||
efbuf->sb = sb; | efbuf->sb = sb; | ||||
efbuf->remainder = maxlen; | efbuf->remainder = maxlen; | ||||
efbuf->flags = flags; | efbuf->flags = flags; | ||||
if (tracevp != NULL) | if (tracevp != NULL) | ||||
export_vnode_to_sb(tracevp, KF_FD_TYPE_TRACE, FREAD | FWRITE, | export_vnode_to_sb(tracevp, KF_FD_TYPE_TRACE, FREAD | FWRITE, | ||||
efbuf); | efbuf); | ||||
if (textvp != NULL) | if (textvp != NULL) | ||||
export_vnode_to_sb(textvp, KF_FD_TYPE_TEXT, FREAD, efbuf); | export_vnode_to_sb(textvp, KF_FD_TYPE_TEXT, FREAD, efbuf); | ||||
if (cttyvp != NULL) | if (cttyvp != NULL) | ||||
export_vnode_to_sb(cttyvp, KF_FD_TYPE_CTTY, FREAD | FWRITE, | export_vnode_to_sb(cttyvp, KF_FD_TYPE_CTTY, FREAD | FWRITE, | ||||
efbuf); | efbuf); | ||||
error = 0; | error = 0; | ||||
if (fdp == NULL) | if (pdp == NULL || fdp == NULL) | ||||
goto fail; | goto fail; | ||||
efbuf->fdp = fdp; | efbuf->fdp = fdp; | ||||
FILEDESC_SLOCK(fdp); | efbuf->pdp = pdp; | ||||
pwd = pwd_hold_filedesc(fdp); | PATHSDESC_SLOCK(pdp); | ||||
pwd = pwd_hold_pathsdesc(pdp); | |||||
if (pwd != NULL) { | if (pwd != NULL) { | ||||
/* working directory */ | /* working directory */ | ||||
if (pwd->pwd_cdir != NULL) { | if (pwd->pwd_cdir != NULL) { | ||||
vrefact(pwd->pwd_cdir); | vrefact(pwd->pwd_cdir); | ||||
export_vnode_to_sb(pwd->pwd_cdir, KF_FD_TYPE_CWD, FREAD, efbuf); | export_vnode_to_sb(pwd->pwd_cdir, KF_FD_TYPE_CWD, FREAD, efbuf); | ||||
} | } | ||||
/* root directory */ | /* root directory */ | ||||
if (pwd->pwd_rdir != NULL) { | if (pwd->pwd_rdir != NULL) { | ||||
vrefact(pwd->pwd_rdir); | vrefact(pwd->pwd_rdir); | ||||
export_vnode_to_sb(pwd->pwd_rdir, KF_FD_TYPE_ROOT, FREAD, efbuf); | export_vnode_to_sb(pwd->pwd_rdir, KF_FD_TYPE_ROOT, FREAD, efbuf); | ||||
} | } | ||||
/* jail directory */ | /* jail directory */ | ||||
if (pwd->pwd_jdir != NULL) { | if (pwd->pwd_jdir != NULL) { | ||||
vrefact(pwd->pwd_jdir); | vrefact(pwd->pwd_jdir); | ||||
export_vnode_to_sb(pwd->pwd_jdir, KF_FD_TYPE_JAIL, FREAD, efbuf); | export_vnode_to_sb(pwd->pwd_jdir, KF_FD_TYPE_JAIL, FREAD, efbuf); | ||||
} | } | ||||
} | } | ||||
PATHSDESC_SUNLOCK(pdp); | |||||
if (pwd != NULL) | |||||
pwd_drop(pwd); | |||||
FILEDESC_SLOCK(fdp); | |||||
lastfile = fdlastfile(fdp); | lastfile = fdlastfile(fdp); | ||||
for (i = 0; fdp->fd_refcnt > 0 && i <= lastfile; i++) { | for (i = 0; fdp->fd_refcnt > 0 && i <= lastfile; i++) { | ||||
if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) | if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) | ||||
continue; | continue; | ||||
#ifdef CAPABILITIES | #ifdef CAPABILITIES | ||||
rights = *cap_rights(fdp, i); | rights = *cap_rights(fdp, i); | ||||
#else /* !CAPABILITIES */ | #else /* !CAPABILITIES */ | ||||
rights = cap_no_rights; | rights = cap_no_rights; | ||||
#endif | #endif | ||||
/* | /* | ||||
* Create sysctl entry. It is OK to drop the filedesc | * Create sysctl entry. It is OK to drop the filedesc | ||||
* lock inside of export_file_to_sb() as we will | * lock inside of export_file_to_sb() as we will | ||||
* re-validate and re-evaluate its properties when the | * re-validate and re-evaluate its properties when the | ||||
* loop continues. | * loop continues. | ||||
*/ | */ | ||||
error = export_file_to_sb(fp, i, &rights, efbuf); | error = export_file_to_sb(fp, i, &rights, efbuf); | ||||
if (error != 0 || efbuf->remainder == 0) | if (error != 0 || efbuf->remainder == 0) | ||||
break; | break; | ||||
} | } | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
if (pwd != NULL) | |||||
pwd_drop(pwd); | |||||
fddrop(fdp); | |||||
fail: | fail: | ||||
if (fdp != NULL) | |||||
fddrop(fdp); | |||||
if (pdp != NULL) | |||||
pddrop(pdp); | |||||
free(efbuf, M_TEMP); | free(efbuf, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
#define FILEDESC_SBUF_SIZE (sizeof(struct kinfo_file) * 5) | #define FILEDESC_SBUF_SIZE (sizeof(struct kinfo_file) * 5) | ||||
/* | /* | ||||
* Get per-process file descriptors for use by procstat(1), et al. | * Get per-process file descriptors for use by procstat(1), et al. | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | kinfo_to_okinfo(struct kinfo_file *kif, struct kinfo_ofile *okif) | ||||
} else { | } else { | ||||
okif->kf_sa_local.ss_family = AF_UNSPEC; | okif->kf_sa_local.ss_family = AF_UNSPEC; | ||||
okif->kf_sa_peer.ss_family = AF_UNSPEC; | okif->kf_sa_peer.ss_family = AF_UNSPEC; | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
export_vnode_for_osysctl(struct vnode *vp, int type, struct kinfo_file *kif, | export_vnode_for_osysctl(struct vnode *vp, int type, struct kinfo_file *kif, | ||||
struct kinfo_ofile *okif, struct filedesc *fdp, struct sysctl_req *req) | struct kinfo_ofile *okif, struct pathsdesc *pdp, struct sysctl_req *req) | ||||
{ | { | ||||
int error; | int error; | ||||
vrefact(vp); | vrefact(vp); | ||||
FILEDESC_SUNLOCK(fdp); | PATHSDESC_SUNLOCK(pdp); | ||||
export_vnode_to_kinfo(vp, type, 0, kif, KERN_FILEDESC_PACK_KINFO); | export_vnode_to_kinfo(vp, type, 0, kif, KERN_FILEDESC_PACK_KINFO); | ||||
kinfo_to_okinfo(kif, okif); | kinfo_to_okinfo(kif, okif); | ||||
error = SYSCTL_OUT(req, okif, sizeof(*okif)); | error = SYSCTL_OUT(req, okif, sizeof(*okif)); | ||||
FILEDESC_SLOCK(fdp); | PATHSDESC_SLOCK(pdp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Get per-process file descriptors for use by procstat(1), et al. | * Get per-process file descriptors for use by procstat(1), et al. | ||||
*/ | */ | ||||
static int | static int | ||||
sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) | sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct kinfo_ofile *okif; | struct kinfo_ofile *okif; | ||||
struct kinfo_file *kif; | struct kinfo_file *kif; | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct pathsdesc *pdp; | |||||
struct pwd *pwd; | struct pwd *pwd; | ||||
int error, i, lastfile, *name; | int error, i, lastfile, *name; | ||||
struct file *fp; | struct file *fp; | ||||
struct proc *p; | struct proc *p; | ||||
name = (int *)arg1; | name = (int *)arg1; | ||||
error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); | error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
fdp = fdhold(p); | fdp = fdhold(p); | ||||
if (fdp != NULL) | |||||
pdp = pdhold(p); | |||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (fdp == NULL) | if (fdp == NULL || pdp == NULL) { | ||||
if (fdp != NULL) | |||||
fddrop(fdp); | |||||
return (ENOENT); | return (ENOENT); | ||||
} | |||||
kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); | kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); | ||||
okif = malloc(sizeof(*okif), M_TEMP, M_WAITOK); | okif = malloc(sizeof(*okif), M_TEMP, M_WAITOK); | ||||
FILEDESC_SLOCK(fdp); | PATHSDESC_SLOCK(pdp); | ||||
pwd = pwd_hold_filedesc(fdp); | pwd = pwd_hold_pathsdesc(pdp); | ||||
if (pwd != NULL) { | if (pwd != NULL) { | ||||
if (pwd->pwd_cdir != NULL) | if (pwd->pwd_cdir != NULL) | ||||
export_vnode_for_osysctl(pwd->pwd_cdir, KF_FD_TYPE_CWD, kif, | export_vnode_for_osysctl(pwd->pwd_cdir, KF_FD_TYPE_CWD, kif, | ||||
okif, fdp, req); | okif, pdp, req); | ||||
if (pwd->pwd_rdir != NULL) | if (pwd->pwd_rdir != NULL) | ||||
export_vnode_for_osysctl(pwd->pwd_rdir, KF_FD_TYPE_ROOT, kif, | export_vnode_for_osysctl(pwd->pwd_rdir, KF_FD_TYPE_ROOT, kif, | ||||
okif, fdp, req); | okif, pdp, req); | ||||
if (pwd->pwd_jdir != NULL) | if (pwd->pwd_jdir != NULL) | ||||
export_vnode_for_osysctl(pwd->pwd_jdir, KF_FD_TYPE_JAIL, kif, | export_vnode_for_osysctl(pwd->pwd_jdir, KF_FD_TYPE_JAIL, kif, | ||||
okif, fdp, req); | okif, pdp, req); | ||||
} | } | ||||
PATHSDESC_SUNLOCK(pdp); | |||||
if (pwd != NULL) | |||||
pwd_drop(pwd); | |||||
FILEDESC_SLOCK(fdp); | |||||
lastfile = fdlastfile(fdp); | lastfile = fdlastfile(fdp); | ||||
for (i = 0; fdp->fd_refcnt > 0 && i <= lastfile; i++) { | for (i = 0; fdp->fd_refcnt > 0 && i <= lastfile; i++) { | ||||
if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) | if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) | ||||
continue; | continue; | ||||
export_file_to_kinfo(fp, i, NULL, kif, fdp, | export_file_to_kinfo(fp, i, NULL, kif, fdp, | ||||
KERN_FILEDESC_PACK_KINFO); | KERN_FILEDESC_PACK_KINFO); | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
kinfo_to_okinfo(kif, okif); | kinfo_to_okinfo(kif, okif); | ||||
error = SYSCTL_OUT(req, okif, sizeof(*okif)); | error = SYSCTL_OUT(req, okif, sizeof(*okif)); | ||||
FILEDESC_SLOCK(fdp); | FILEDESC_SLOCK(fdp); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
if (pwd != NULL) | |||||
pwd_drop(pwd); | |||||
fddrop(fdp); | fddrop(fdp); | ||||
pddrop(pdp); | |||||
free(kif, M_TEMP); | free(kif, M_TEMP); | ||||
free(okif, M_TEMP); | free(okif, M_TEMP); | ||||
return (0); | return (0); | ||||
} | } | ||||
static SYSCTL_NODE(_kern_proc, KERN_PROC_OFILEDESC, ofiledesc, | static SYSCTL_NODE(_kern_proc, KERN_PROC_OFILEDESC, ofiledesc, | ||||
CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_ofiledesc, | CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_ofiledesc, | ||||
"Process ofiledesc entries"); | "Process ofiledesc entries"); | ||||
Show All 35 Lines | |||||
/* | /* | ||||
* Store a process current working directory information to sbuf. | * Store a process current working directory information to sbuf. | ||||
* | * | ||||
* Takes a locked proc as argument, and returns with the proc unlocked. | * Takes a locked proc as argument, and returns with the proc unlocked. | ||||
*/ | */ | ||||
int | int | ||||
kern_proc_cwd_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) | kern_proc_cwd_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) | ||||
{ | { | ||||
struct filedesc *fdp; | struct pathsdesc *pdp; | ||||
struct pwd *pwd; | struct pwd *pwd; | ||||
struct export_fd_buf *efbuf; | struct export_fd_buf *efbuf; | ||||
struct vnode *cdir; | struct vnode *cdir; | ||||
int error; | int error; | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
fdp = fdhold(p); | pdp = pdhold(p); | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (fdp == NULL) | if (pdp == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); | efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); | ||||
efbuf->fdp = fdp; | efbuf->pdp = pdp; | ||||
efbuf->sb = sb; | efbuf->sb = sb; | ||||
efbuf->remainder = maxlen; | efbuf->remainder = maxlen; | ||||
FILEDESC_SLOCK(fdp); | PATHSDESC_SLOCK(pdp); | ||||
pwd = FILEDESC_LOCKED_LOAD_PWD(fdp); | pwd = PATHSDESC_LOCKED_LOAD_PWD(pdp); | ||||
cdir = pwd->pwd_cdir; | cdir = pwd->pwd_cdir; | ||||
if (cdir == NULL) { | if (cdir == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
} else { | } else { | ||||
vrefact(cdir); | vrefact(cdir); | ||||
error = export_vnode_to_sb(cdir, KF_FD_TYPE_CWD, FREAD, efbuf); | error = export_vnode_to_sb(cdir, KF_FD_TYPE_CWD, FREAD, efbuf); | ||||
} | } | ||||
FILEDESC_SUNLOCK(fdp); | PATHSDESC_SUNLOCK(pdp); | ||||
fddrop(fdp); | pddrop(pdp); | ||||
free(efbuf, M_TEMP); | free(efbuf, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Get per-process current working directory. | * Get per-process current working directory. | ||||
*/ | */ | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | |||||
/* ARGSUSED*/ | /* ARGSUSED*/ | ||||
static void | static void | ||||
filelistinit(void *dummy) | filelistinit(void *dummy) | ||||
{ | { | ||||
file_zone = uma_zcreate("Files", sizeof(struct file), NULL, NULL, | file_zone = uma_zcreate("Files", sizeof(struct file), NULL, NULL, | ||||
NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | ||||
filedesc0_zone = uma_zcreate("filedesc0", sizeof(struct filedesc0), | filedesc0_zone = uma_zcreate("filedesc0", sizeof(struct filedesc0), | ||||
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); | |||||
pathsdesc_zone = uma_zcreate("pathsdesc", sizeof(struct pathsdesc), | |||||
mjgUnsubmitted Not Done Inline Actionsany need of this to be a zone? malloc is preferred due to shared buckets mjg: any need of this to be a zone? malloc is preferred due to shared buckets | |||||
Done Inline Actionsagain why the zone. malloc should be fine. file is a zone because of NOFREE, filedesc0 because of size (1080 bytes) and pwd because of SMR. mjg: again why the zone. malloc should be fine. file is a zone because of NOFREE, filedesc0 because… | |||||
Done Inline ActionsIt doesn't matter either way. cem: It doesn't matter either way. | |||||
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); | NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); | ||||
pwd_zone = uma_zcreate("PWD", sizeof(struct pwd), NULL, NULL, | pwd_zone = uma_zcreate("PWD", sizeof(struct pwd), NULL, NULL, | ||||
NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_SMR); | NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_SMR); | ||||
/* | /* | ||||
* XXXMJG this is a temporary hack due to boot ordering issues against | * XXXMJG this is a temporary hack due to boot ordering issues against | ||||
* the vnode zone. | * the vnode zone. | ||||
*/ | */ | ||||
vfs_smr = uma_zone_get_smr(pwd_zone); | vfs_smr = uma_zone_get_smr(pwd_zone); | ||||
▲ Show 20 Lines • Show All 224 Lines • Show Last 20 Lines |
given that pwd is copy on write and it gets refed above, this code can drop both fdp and pdp. instead you can just grab a ref to pwd and drop the pdp lock. then the relockign inside is eliminated.