Skip to content

Commit 927bc12

Browse files
committed
Merge tag 'fs.close_range.v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull close_range() cleanup from Christian Brauner: "This is a cleanup for close_range() which was sent as part of a bugfix we did some time ago in commit 9b5b872 ("file: fix close_range() for unshare+cloexec"). We used to share more code between some helpers for close_range() which made retrieving the maximum number of open fds before calling into the helpers sensible. But with the introduction of CLOSE_RANGE_CLOEXEC and the need to retrieve the number of maximum fds once more for CLOSE_RANGE_CLOEXEC that stopped making sense. So the code was in a dumb in-limbo state. Fix this by simplifying the code a bit. The original idea was to only fix the bug itself and make backporting easy. And since the cleanup wasn't very pressing I left it in linux-next for a very long time. I didn't pull the patches from the list again back then which is why they don't have lore-links. So I'm listing them below explicitly" Commit 03ba0fe ("file: simplify logic in __close_range()") Link: https://lore.kernel.org/linux-fsdevel/[email protected] Commit f49fd6d ("file: let pick_file() tell caller it's done") Link: https://lore.kernel.org/linux-fsdevel/[email protected] * tag 'fs.close_range.v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: file: simplify logic in __close_range() file: let pick_file() tell caller it's done
2 parents 1dd5915 + 03ba0fe commit 927bc12

File tree

1 file changed

+40
-24
lines changed

1 file changed

+40
-24
lines changed

fs/file.c

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -596,18 +596,32 @@ void fd_install(unsigned int fd, struct file *file)
596596

597597
EXPORT_SYMBOL(fd_install);
598598

599+
/**
600+
* pick_file - return file associatd with fd
601+
* @files: file struct to retrieve file from
602+
* @fd: file descriptor to retrieve file for
603+
*
604+
* If this functions returns an EINVAL error pointer the fd was beyond the
605+
* current maximum number of file descriptors for that fdtable.
606+
*
607+
* Returns: The file associated with @fd, on error returns an error pointer.
608+
*/
599609
static struct file *pick_file(struct files_struct *files, unsigned fd)
600610
{
601-
struct file *file = NULL;
611+
struct file *file;
602612
struct fdtable *fdt;
603613

604614
spin_lock(&files->file_lock);
605615
fdt = files_fdtable(files);
606-
if (fd >= fdt->max_fds)
616+
if (fd >= fdt->max_fds) {
617+
file = ERR_PTR(-EINVAL);
607618
goto out_unlock;
619+
}
608620
file = fdt->fd[fd];
609-
if (!file)
621+
if (!file) {
622+
file = ERR_PTR(-EBADF);
610623
goto out_unlock;
624+
}
611625
rcu_assign_pointer(fdt->fd[fd], NULL);
612626
__put_unused_fd(files, fd);
613627

@@ -622,7 +636,7 @@ int close_fd(unsigned fd)
622636
struct file *file;
623637

624638
file = pick_file(files, fd);
625-
if (!file)
639+
if (IS_ERR(file))
626640
return -EBADF;
627641

628642
return filp_close(file, files);
@@ -663,11 +677,16 @@ static inline void __range_close(struct files_struct *cur_fds, unsigned int fd,
663677
struct file *file;
664678

665679
file = pick_file(cur_fds, fd++);
666-
if (!file)
680+
if (!IS_ERR(file)) {
681+
/* found a valid file to close */
682+
filp_close(file, cur_fds);
683+
cond_resched();
667684
continue;
685+
}
668686

669-
filp_close(file, cur_fds);
670-
cond_resched();
687+
/* beyond the last fd in that table */
688+
if (PTR_ERR(file) == -EINVAL)
689+
return;
671690
}
672691
}
673692

@@ -682,7 +701,6 @@ static inline void __range_close(struct files_struct *cur_fds, unsigned int fd,
682701
*/
683702
int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
684703
{
685-
unsigned int cur_max;
686704
struct task_struct *me = current;
687705
struct files_struct *cur_fds = me->files, *fds = NULL;
688706

@@ -692,26 +710,26 @@ int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
692710
if (fd > max_fd)
693711
return -EINVAL;
694712

695-
rcu_read_lock();
696-
cur_max = files_fdtable(cur_fds)->max_fds;
697-
rcu_read_unlock();
698-
699-
/* cap to last valid index into fdtable */
700-
cur_max--;
701-
702713
if (flags & CLOSE_RANGE_UNSHARE) {
703714
int ret;
704715
unsigned int max_unshare_fds = NR_OPEN_MAX;
705716

706717
/*
707-
* If the requested range is greater than the current maximum,
708-
* we're closing everything so only copy all file descriptors
709-
* beneath the lowest file descriptor.
710-
* If the caller requested all fds to be made cloexec copy all
711-
* of the file descriptors since they still want to use them.
718+
* If the caller requested all fds to be made cloexec we always
719+
* copy all of the file descriptors since they still want to
720+
* use them.
712721
*/
713-
if (!(flags & CLOSE_RANGE_CLOEXEC) && (max_fd >= cur_max))
714-
max_unshare_fds = fd;
722+
if (!(flags & CLOSE_RANGE_CLOEXEC)) {
723+
/*
724+
* If the requested range is greater than the current
725+
* maximum, we're closing everything so only copy all
726+
* file descriptors beneath the lowest file descriptor.
727+
*/
728+
rcu_read_lock();
729+
if (max_fd >= last_fd(files_fdtable(cur_fds)))
730+
max_unshare_fds = fd;
731+
rcu_read_unlock();
732+
}
715733

716734
ret = unshare_fd(CLONE_FILES, max_unshare_fds, &fds);
717735
if (ret)
@@ -725,8 +743,6 @@ int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
725743
swap(cur_fds, fds);
726744
}
727745

728-
max_fd = min(max_fd, cur_max);
729-
730746
if (flags & CLOSE_RANGE_CLOEXEC)
731747
__range_cloexec(cur_fds, fd, max_fd);
732748
else

0 commit comments

Comments
 (0)