@@ -31,6 +31,7 @@
#include <linux/workqueue.h>
#include <linux/percpu-rwsem.h>
#include <linux/delayed_call.h>
+#include <linux/overlay_util.h>
#include <asm/byteorder.h>
#include <uapi/linux/fs.h>
@@ -1721,9 +1722,19 @@ struct inode_operations {
int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;
+
+static inline bool overlay_file_inconsistent(struct file *file)
+{
+ return unlikely(file->f_path.dentry->d_flags & DCACHE_OP_REAL) &&
+ unlikely(d_real_inode(file->f_path.dentry) != file_inode(file));
+}
+
static inline ssize_t call_read_iter(struct file *file, struct kiocb *kio,
struct iov_iter *iter)
{
+ if (overlay_file_inconsistent(file))
+ return overlay_read_iter(file, kio, iter);
+
return file->f_op->read_iter(kio, iter);
}
@@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.
attr.o bad_inode.o file.o filesystems.o namespace.o \
seq_file.o xattr.o libfs.o fs-writeback.o \
pnode.o splice.o sync.o utimes.o \
- stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
+ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o overlay_util.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o block_dev.o direct-io.o mpage.o
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/overlay_util.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include "internal.h"
+
+static struct file *overlay_clone_file(struct file *file)
+{
+ file = filp_clone_open(file);
+ if (!IS_ERR(file))
+ file->f_mode |= FMODE_NONOTIFY;
+
+ return file;
+}
+
+/*
+ * Do the release synchronously. Otherwise we'd have a DoS problem when doing
+ * multiple reads (e.g. through kernel_read()) and only releasing the cloned
+ * files when returning to userspace.
+ *
+ * There's no risk of final dput or final mntput happening, since caller holds
+ * ref to both through the original file.
+ */
+static void overlay_put_cloned_file(struct file *file)
+{
+ if (WARN_ON(!atomic_long_dec_and_test(&file->f_count)))
+ return;
+
+ __fput(file);
+}
+
+ssize_t overlay_read_iter(struct file *file, struct kiocb *kio,
+ struct iov_iter *iter)
+{
+ ssize_t ret;
+
+ file = overlay_clone_file(file);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ ret = vfs_iter_read(file, iter, &kio->ki_pos);
+ overlay_put_cloned_file(file);
+
+ return ret;
+}
+EXPORT_SYMBOL(overlay_read_iter);
@@ -0,0 +1,13 @@
+#ifndef _LINUX_OVERLAY_FS_H
+#define _LINUX_OVERLAY_FS_H
+
+#include <linux/types.h>
+
+struct file;
+struct kiocb;
+struct iov_iter;
+
+extern ssize_t overlay_read_iter(struct file *file, struct kiocb *kio,
+ struct iov_iter *iter);
+
+#endif /* _LINUX_OVERLAY_FS_H */
@@ -184,7 +184,7 @@ EXPORT_SYMBOL(alloc_file);
/* the real guts of fput() - releasing the last reference to file
*/
-static void __fput(struct file *file)
+void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
@@ -83,6 +83,7 @@ extern void chroot_fs_refs(const struct
* file_table.c
*/
extern struct file *get_empty_filp(void);
+extern void __fput(struct file *);
/*
* super.c