spin_unlock(&files->file_lock);
}
-static inline struct file *__fget_files_rcu(struct files_struct *files,
- unsigned int fd, fmode_t mask, unsigned int refs)
-{
- for (;;) {
- struct file *file;
- struct fdtable *fdt = rcu_dereference_raw(files->fdt);
- struct file __rcu **fdentry;
-
- if (unlikely(fd >= fdt->max_fds))
- return NULL;
-
- fdentry = fdt->fd + array_index_nospec(fd, fdt->max_fds);
- file = rcu_dereference_raw(*fdentry);
- if (unlikely(!file))
- return NULL;
-
- if (unlikely(file->f_mode & mask))
- return NULL;
-
- /*
- * Ok, we have a file pointer. However, because we do
- * this all locklessly under RCU, we may be racing with
- * that file being closed.
- *
- * Such a race can take two forms:
- *
- * (a) the file ref already went down to zero,
- * and get_file_rcu_many() fails. Just try
- * again:
- */
- if (unlikely(!get_file_rcu_many(file, refs)))
- continue;
-
- /*
- * (b) the file table entry has changed under us.
- * Note that we don't need to re-check the 'fdt->fd'
- * pointer having changed, because it always goes
- * hand-in-hand with 'fdt'.
- *
- * If so, we need to put our refs and try again.
- */
- if (unlikely(rcu_dereference_raw(files->fdt) != fdt) ||
- unlikely(rcu_dereference_raw(*fdentry) != file)) {
- fput_many(file, refs);
- continue;
- }
-
- /*
- * Ok, we have a ref to the file, and checked that it
- * still exists.
- */
- return file;
- }
-}
-
static struct file *__fget_files(struct files_struct *files, unsigned int fd,
fmode_t mask, unsigned int refs)
{
struct file *file;
rcu_read_lock();
- file = __fget_files_rcu(files, fd, mask, refs);
+loop:
+ file = fcheck_files(files, fd);
+ if (file) {
+ /* File object ref couldn't be taken.
+ * dup2() atomicity guarantee is the reason
+ * we loop to catch the new file (or NULL pointer)
+ */
+ if (file->f_mode & mask)
+ file = NULL;
+ else if (!get_file_rcu_many(file, refs))
+ goto loop;
+ else if (__fcheck_files(files, fd) != file) {
+ fput_many(file, refs);
+ goto loop;
+ }
+ }
rcu_read_unlock();
return file;