Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/kern_descrip.c
Show First 20 Lines • Show All 847 Lines • ▼ Show 20 Lines | kern_dup(struct thread *td, u_int mode, int flags, int old, int new) | ||||
if ((mode == FDDUP_FIXED || mode == FDDUP_MUSTREPLACE) && old == new) { | if ((mode == FDDUP_FIXED || mode == FDDUP_MUSTREPLACE) && old == new) { | ||||
td->td_retval[0] = new; | td->td_retval[0] = new; | ||||
if (flags & FDDUP_FLAG_CLOEXEC) | if (flags & FDDUP_FLAG_CLOEXEC) | ||||
fdp->fd_ofiles[new].fde_flags |= UF_EXCLOSE; | fdp->fd_ofiles[new].fde_flags |= UF_EXCLOSE; | ||||
error = 0; | error = 0; | ||||
goto unlock; | goto unlock; | ||||
} | } | ||||
oldfde = &fdp->fd_ofiles[old]; | |||||
if (!fhold(oldfde->fde_file)) | |||||
goto unlock; | |||||
/* | /* | ||||
* If the caller specified a file descriptor, make sure the file | * If the caller specified a file descriptor, make sure the file | ||||
* table is large enough to hold it, and grab it. Otherwise, just | * table is large enough to hold it, and grab it. Otherwise, just | ||||
* allocate a new descriptor the usual way. | * allocate a new descriptor the usual way. | ||||
*/ | */ | ||||
switch (mode) { | switch (mode) { | ||||
case FDDUP_NORMAL: | case FDDUP_NORMAL: | ||||
case FDDUP_FCNTL: | case FDDUP_FCNTL: | ||||
if ((error = fdalloc(td, new, &new)) != 0) | if ((error = fdalloc(td, new, &new)) != 0) { | ||||
fdrop(oldfde->fde_file, td); | |||||
goto unlock; | goto unlock; | ||||
} | |||||
break; | break; | ||||
case FDDUP_MUSTREPLACE: | case FDDUP_MUSTREPLACE: | ||||
/* Target file descriptor must exist. */ | /* Target file descriptor must exist. */ | ||||
if (fget_locked(fdp, new) == NULL) | if (fget_locked(fdp, new) == NULL) { | ||||
fdrop(oldfde->fde_file, td); | |||||
goto unlock; | goto unlock; | ||||
} | |||||
break; | break; | ||||
case FDDUP_FIXED: | case FDDUP_FIXED: | ||||
if (new >= fdp->fd_nfiles) { | if (new >= fdp->fd_nfiles) { | ||||
/* | /* | ||||
* The resource limits are here instead of e.g. | * The resource limits are here instead of e.g. | ||||
* fdalloc(), because the file descriptor table may be | * fdalloc(), because the file descriptor table may be | ||||
* shared between processes, so we can't really use | * shared between processes, so we can't really use | ||||
* racct_add()/racct_sub(). Instead of counting the | * racct_add()/racct_sub(). Instead of counting the | ||||
* number of actually allocated descriptors, just put | * number of actually allocated descriptors, just put | ||||
* the limit on the size of the file descriptor table. | * the limit on the size of the file descriptor table. | ||||
*/ | */ | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (RACCT_ENABLED()) { | if (RACCT_ENABLED()) { | ||||
error = racct_set_unlocked(p, RACCT_NOFILE, new + 1); | error = racct_set_unlocked(p, RACCT_NOFILE, new + 1); | ||||
if (error != 0) { | if (error != 0) { | ||||
error = EMFILE; | error = EMFILE; | ||||
fdrop(oldfde->fde_file, td); | |||||
goto unlock; | goto unlock; | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
fdgrowtable_exp(fdp, new + 1); | fdgrowtable_exp(fdp, new + 1); | ||||
} | } | ||||
if (!fdisused(fdp, new)) | if (!fdisused(fdp, new)) | ||||
fdused(fdp, new); | fdused(fdp, new); | ||||
break; | break; | ||||
default: | default: | ||||
KASSERT(0, ("%s unsupported mode %d", __func__, mode)); | KASSERT(0, ("%s unsupported mode %d", __func__, mode)); | ||||
} | } | ||||
KASSERT(old != new, ("new fd is same as old")); | KASSERT(old != new, ("new fd is same as old")); | ||||
oldfde = &fdp->fd_ofiles[old]; | |||||
fhold(oldfde->fde_file); | |||||
newfde = &fdp->fd_ofiles[new]; | newfde = &fdp->fd_ofiles[new]; | ||||
delfp = newfde->fde_file; | delfp = newfde->fde_file; | ||||
oioctls = filecaps_free_prep(&newfde->fde_caps); | oioctls = filecaps_free_prep(&newfde->fde_caps); | ||||
nioctls = filecaps_copy_prep(&oldfde->fde_caps); | nioctls = filecaps_copy_prep(&oldfde->fde_caps); | ||||
/* | /* | ||||
* Duplicate the source descriptor. | * Duplicate the source descriptor. | ||||
▲ Show 20 Lines • Show All 984 Lines • ▼ Show 20 Lines | |||||
finstall(struct thread *td, struct file *fp, int *fd, int flags, | finstall(struct thread *td, struct file *fp, int *fd, int flags, | ||||
struct filecaps *fcaps) | struct filecaps *fcaps) | ||||
{ | { | ||||
struct filedesc *fdp = td->td_proc->p_fd; | struct filedesc *fdp = td->td_proc->p_fd; | ||||
int error; | int error; | ||||
MPASS(fd != NULL); | MPASS(fd != NULL); | ||||
if (!fhold(fp)) | |||||
return (EBADF); | |||||
FILEDESC_XLOCK(fdp); | FILEDESC_XLOCK(fdp); | ||||
if ((error = fdalloc(td, 0, fd))) { | if ((error = fdalloc(td, 0, fd))) { | ||||
FILEDESC_XUNLOCK(fdp); | FILEDESC_XUNLOCK(fdp); | ||||
fdrop(fp, td); | |||||
return (error); | return (error); | ||||
} | } | ||||
fhold(fp); | |||||
_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. | * Copy the current, root, and jail root vnode references. | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | fdcopy(struct filedesc *fdp) | ||||
MPASS(fdp != NULL); | MPASS(fdp != NULL); | ||||
newfdp = fdinit(fdp, true); | newfdp = fdinit(fdp, true); | ||||
/* copy all passable descriptors (i.e. not kqueue) */ | /* copy all passable descriptors (i.e. not kqueue) */ | ||||
newfdp->fd_freefile = -1; | newfdp->fd_freefile = -1; | ||||
for (i = 0; i <= fdp->fd_lastfile; ++i) { | for (i = 0; i <= fdp->fd_lastfile; ++i) { | ||||
ofde = &fdp->fd_ofiles[i]; | ofde = &fdp->fd_ofiles[i]; | ||||
if (ofde->fde_file == NULL || | if (ofde->fde_file == NULL || | ||||
(ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { | (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0 || | ||||
!fhold(ofde->fde_file)) { | |||||
if (newfdp->fd_freefile == -1) | if (newfdp->fd_freefile == -1) | ||||
newfdp->fd_freefile = i; | newfdp->fd_freefile = i; | ||||
continue; | continue; | ||||
} | } | ||||
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); | ||||
fhold(nfde->fde_file); | |||||
fdused_init(newfdp, i); | fdused_init(newfdp, i); | ||||
newfdp->fd_lastfile = i; | newfdp->fd_lastfile = 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; | newfdp->fd_cmask = fdp->fd_cmask; | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
return (newfdp); | return (newfdp); | ||||
Show All 36 Lines | if (ofde->fde_file == NULL) { | ||||
error = EBADF; | error = EBADF; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
if ((ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { | if ((ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { | ||||
/* File descriptor cannot be passed. */ | /* File descriptor cannot be passed. */ | ||||
error = EINVAL; | error = EINVAL; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
if (!fhold(nfde->fde_file)) { | |||||
error = EBADF; | |||||
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); | ||||
fhold(nfde->fde_file); | |||||
fdused_init(newfdp, i); | fdused_init(newfdp, i); | ||||
newfdp->fd_lastfile = i; | newfdp->fd_lastfile = i; | ||||
} | } | ||||
newfdp->fd_cmask = fdp->fd_cmask; | newfdp->fd_cmask = fdp->fd_cmask; | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
*ret = newfdp; | *ret = newfdp; | ||||
return (0); | return (0); | ||||
bad: | bad: | ||||
Show All 25 Lines | fdclearlocks(struct thread *td) | ||||
FILEDESC_XLOCK(fdp); | FILEDESC_XLOCK(fdp); | ||||
KASSERT(fdtol->fdl_refcount > 0, | KASSERT(fdtol->fdl_refcount > 0, | ||||
("filedesc_to_refcount botch: fdl_refcount=%d", | ("filedesc_to_refcount botch: fdl_refcount=%d", | ||||
fdtol->fdl_refcount)); | fdtol->fdl_refcount)); | ||||
if (fdtol->fdl_refcount == 1 && | if (fdtol->fdl_refcount == 1 && | ||||
(p->p_leader->p_flag & P_ADVLOCK) != 0) { | (p->p_leader->p_flag & P_ADVLOCK) != 0) { | ||||
for (i = 0; i <= fdp->fd_lastfile; i++) { | for (i = 0; i <= fdp->fd_lastfile; i++) { | ||||
fp = fdp->fd_ofiles[i].fde_file; | fp = fdp->fd_ofiles[i].fde_file; | ||||
if (fp == NULL || fp->f_type != DTYPE_VNODE) | if (fp == NULL || fp->f_type != DTYPE_VNODE || | ||||
!fhold(fp)) | |||||
continue; | continue; | ||||
fhold(fp); | |||||
FILEDESC_XUNLOCK(fdp); | FILEDESC_XUNLOCK(fdp); | ||||
lf.l_whence = SEEK_SET; | lf.l_whence = SEEK_SET; | ||||
lf.l_start = 0; | lf.l_start = 0; | ||||
lf.l_len = 0; | lf.l_len = 0; | ||||
lf.l_type = F_UNLCK; | lf.l_type = F_UNLCK; | ||||
vp = fp->f_vnode; | vp = fp->f_vnode; | ||||
(void) VOP_ADVLOCK(vp, | (void) VOP_ADVLOCK(vp, | ||||
(caddr_t)p->p_leader, F_UNLCK, | (caddr_t)p->p_leader, F_UNLCK, | ||||
▲ Show 20 Lines • Show All 424 Lines • ▼ Show 20 Lines | #else | ||||
} | } | ||||
*fpp = fp; | *fpp = fp; | ||||
return (0); | return (0); | ||||
get_locked: | get_locked: | ||||
FILEDESC_SLOCK(fdp); | FILEDESC_SLOCK(fdp); | ||||
error = fget_cap_locked(fdp, fd, needrightsp, fpp, havecapsp); | error = fget_cap_locked(fdp, fd, needrightsp, fpp, havecapsp); | ||||
if (error == 0) | if (error == 0 && !fhold(*fpp)) | ||||
fhold(*fpp); | error = EBADF; | ||||
FILEDESC_SUNLOCK(fdp); | FILEDESC_SUNLOCK(fdp); | ||||
#endif | #endif | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, | fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, | ||||
struct file **fpp, seqc_t *seqp) | struct file **fpp, seqc_t *seqp) | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | #endif | ||||
count = fp->f_count; | count = fp->f_count; | ||||
retry: | retry: | ||||
if (count == 0) { | if (count == 0) { | ||||
/* | /* | ||||
* Force a reload. Other thread could reallocate the | * Force a reload. Other thread could reallocate the | ||||
* table before this fd was closed, so it possible that | * table before this fd was closed, so it possible that | ||||
* there is a stale fp pointer in cached version. | * there is a stale fp pointer in cached version. | ||||
*/ | */ | ||||
fdt = *(const struct fdescenttbl * const volatile *)&(fdp->fd_files); | fdt = *(const struct fdescenttbl * const volatile *) | ||||
&(fdp->fd_files); | |||||
continue; | continue; | ||||
} | } | ||||
if (__predict_false(count + 1 < count)) | |||||
return (EBADF); | |||||
/* | /* | ||||
* Use an acquire barrier to force re-reading of fdt so it is | * Use an acquire barrier to force re-reading of fdt so it is | ||||
* refreshed for verification. | * refreshed for verification. | ||||
*/ | */ | ||||
if (atomic_fcmpset_acq_int(&fp->f_count, &count, count + 1) == 0) | if (__predict_false(atomic_fcmpset_acq_int(&fp->f_count, | ||||
&count, count + 1) == 0)) | |||||
goto retry; | goto retry; | ||||
fdt = fdp->fd_files; | fdt = fdp->fd_files; | ||||
#ifdef CAPABILITIES | #ifdef CAPABILITIES | ||||
if (seqc_consistent_nomb(fd_seqc(fdt, fd), seq)) | if (seqc_consistent_nomb(fd_seqc(fdt, fd), seq)) | ||||
#else | #else | ||||
if (fp == fdt->fdt_ofiles[fd].fde_file) | if (fp == fdt->fdt_ofiles[fd].fde_file) | ||||
#endif | #endif | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 368 Lines • ▼ Show 20 Lines | case ENODEV: | ||||
* Check that the mode the file is being opened for is a | * Check that the mode the file is being opened for is a | ||||
* subset of the mode of the existing descriptor. | * subset of the mode of the existing descriptor. | ||||
*/ | */ | ||||
if (((mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { | if (((mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { | ||||
fdunused(fdp, indx); | fdunused(fdp, indx); | ||||
FILEDESC_XUNLOCK(fdp); | FILEDESC_XUNLOCK(fdp); | ||||
return (EACCES); | return (EACCES); | ||||
} | } | ||||
fhold(fp); | if (!fhold(fp)) { | ||||
fdunused(fdp, indx); | |||||
FILEDESC_XUNLOCK(fdp); | |||||
return (EBADF); | |||||
} | |||||
newfde = &fdp->fd_ofiles[indx]; | newfde = &fdp->fd_ofiles[indx]; | ||||
oldfde = &fdp->fd_ofiles[dfd]; | oldfde = &fdp->fd_ofiles[dfd]; | ||||
ioctls = filecaps_copy_prep(&oldfde->fde_caps); | ioctls = filecaps_copy_prep(&oldfde->fde_caps); | ||||
#ifdef CAPABILITIES | #ifdef CAPABILITIES | ||||
seqc_write_begin(&newfde->fde_seqc); | seqc_write_begin(&newfde->fde_seqc); | ||||
#endif | #endif | ||||
memcpy(newfde, oldfde, fde_change_size); | memcpy(newfde, oldfde, fde_change_size); | ||||
filecaps_copy_finish(&oldfde->fde_caps, &newfde->fde_caps, | filecaps_copy_finish(&oldfde->fde_caps, &newfde->fde_caps, | ||||
▲ Show 20 Lines • Show All 1,180 Lines • Show Last 20 Lines |