aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2021-01-28 23:27:44 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2021-01-29 11:23:43 +0000
commiteaad8d1303da500ed691bd774742a4555a05e729 (patch)
tree216991c09e5d80b94c432ceefdabed26eb9dfa1b
parentabd619045a54c73f41a95b3e038a5ba083391400 (diff)
downloadsrc-eaad8d1303da500ed691bd774742a4555a05e729.tar.gz
src-eaad8d1303da500ed691bd774742a4555a05e729.zip
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.
-rw-r--r--sys/kern/kern_descrip.c70
-rw-r--r--sys/sys/filedesc.h12
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;
@@ -3177,6 +3181,66 @@ out_fallback:
}
/*
+ * 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 *