Date: Fri, 29 Jan 2021 11:24:05 GMT From: Mateusz Guzik <mjg@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: eaad8d1303da - main - fd: add fget_only_user Message-ID: <202101291124.10TBO571054678@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by mjg: URL: https://cgit.FreeBSD.org/src/commit/?id=eaad8d1303da500ed691bd774742a4555a05e729 commit eaad8d1303da500ed691bd774742a4555a05e729 Author: Mateusz Guzik <mjg@FreeBSD.org> AuthorDate: 2021-01-28 23:27:44 +0000 Commit: Mateusz Guzik <mjg@FreeBSD.org> CommitDate: 2021-01-29 11:23:43 +0000 fd: add fget_only_user This can be used by single-threaded processes which don't share a file descriptor table to access their file objects without having to reference them. For example select consumers tend to match the requirement and have several file descriptors to inspect. --- sys/kern/kern_descrip.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- sys/sys/filedesc.h | 12 +++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 257b1de2a6ac..059e5123c7b5 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1860,10 +1860,14 @@ fdgrowtable(struct filedesc *fdp, int nfd) * which must not be freed. */ if (onfiles > NDFILE) { - if (curproc->p_numthreads == 1 && - refcount_load(&fdp->fd_refcnt) == 1) + /* + * Note we may be called here from fdinit while allocating a + * table for a new process in which case ->p_fd points + * elsewhere. + */ + if (curproc->p_fd != fdp || FILEDESC_IS_ONLY_USER(fdp)) { free(otable, M_FILEDESC); - else { + } else { ft = (struct freetable *)&otable->fdt_ofiles[onfiles]; fdp0 = (struct filedesc0 *)fdp; ft->ft_table = otable; @@ -3176,6 +3180,66 @@ out_fallback: return (fget_unlocked_seq(fdp, fd, needrightsp, fpp, NULL)); } +/* + * Translate fd -> file when the caller guarantees the file descriptor table + * can't be changed by others. + * + * Note this does not mean the file object itself is only visible to the caller, + * merely that it wont disappear without having to be referenced. + * + * Must be paired with fput_only_user. + */ +#ifdef CAPABILITIES +int +fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, + struct file **fpp) +{ + const struct filedescent *fde; + const struct fdescenttbl *fdt; + const cap_rights_t *haverights; + struct file *fp; + int error; + + MPASS(FILEDESC_IS_ONLY_USER(fdp)); + + if (__predict_false(fd >= fdp->fd_nfiles)) + return (EBADF); + + fdt = fdp->fd_files; + fde = &fdt->fdt_ofiles[fd]; + fp = fde->fde_file; + if (__predict_false(fp == NULL)) + return (EBADF); + MPASS(refcount_load(&fp->f_count) > 0); + haverights = cap_rights_fde_inline(fde); + error = cap_check_inline(haverights, needrightsp); + if (__predict_false(error != 0)) + return (EBADF); + *fpp = fp; + return (0); +} +#else +int +fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, + struct file **fpp) +{ + struct file *fp; + + MPASS(FILEDESC_IS_ONLY_USER(fdp)); + + if (__predict_false(fd >= fdp->fd_nfiles)) + return (EBADF); + + fp = fdp->fd_ofiles[fd].fde_file; + if (__predict_false(fp == NULL)) + return (EBADF); + + MPASS(refcount_load(&fp->f_count) > 0); + *fpp = fp; + return (0); +} +#endif + /* * Extract the file pointer associated with the specified descriptor for the * current user process. diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 9b65c7a66054..890232b7f160 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -177,6 +177,11 @@ struct filedesc_to_leader { SX_NOTRECURSED) #define FILEDESC_UNLOCK_ASSERT(fdp) sx_assert(&(fdp)->fd_sx, SX_UNLOCKED) +#define FILEDESC_IS_ONLY_USER(fdp) ({ \ + struct filedesc *_fdp = (fdp); \ + MPASS(curproc->p_fd == _fdp); \ + (curproc->p_numthreads == 1 && refcount_load(&_fdp->fd_refcnt) == 1); \ +}) #else /* @@ -272,6 +277,13 @@ int fget_unlocked_seq(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, struct file **fpp, seqc_t *seqp); int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, struct file **fpp); +/* Return a file pointer without a ref. FILEDESC_IS_ONLY_USER must be true. */ +int fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, + struct file **fpp); +#define fput_only_user(fdp, fp) ({ \ + MPASS(FILEDESC_IS_ONLY_USER(fdp)); \ + MPASS(refcount_load(&fp->f_count) > 0); \ +}) /* Requires a FILEDESC_{S,X}LOCK held and returns without a ref. */ static __inline struct file *
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202101291124.10TBO571054678>