From: Amir Goldstein <amir73il@gmail.com>
To: Miklos Szeredi <miklos@szeredi.hu>, Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>,
linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org
Subject: [PATCH 2/4] ovl: stash upper real file in backing_file struct
Date: Fri, 4 Oct 2024 12:23:40 +0200 [thread overview]
Message-ID: <20241004102342.179434-3-amir73il@gmail.com> (raw)
In-Reply-To: <20241004102342.179434-1-amir73il@gmail.com>
When an overlayfs file is opened as lower and then the file is copied up,
every operation on the overlayfs open file will open a temporary backing
file to the upper dentry and close it at the end of the operation.
The original (lower) real file is stored in file->private_data pointer.
We could have allocated a struct ovl_real_file to potentially store two
backing files, the lower and the upper, but the original backing file
struct is not very space optimized (it has no memcache pool), so add a
private_data pointer to the backing_file struct and store the optional
second backing upper file in there instead of opening a temporary upper
file on every operation.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/file_table.c | 7 +++++++
fs/internal.h | 6 ++++++
fs/overlayfs/file.c | 48 ++++++++++++++++++++++++++++++++++++++-------
3 files changed, 54 insertions(+), 7 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index eed5ffad9997..1c2c08a5a66a 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -47,6 +47,7 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
struct backing_file {
struct file file;
struct path user_path;
+ void *private_data;
};
static inline struct backing_file *backing_file(struct file *f)
@@ -60,6 +61,12 @@ struct path *backing_file_user_path(struct file *f)
}
EXPORT_SYMBOL_GPL(backing_file_user_path);
+void **backing_file_private_ptr(struct file *f)
+{
+ return &backing_file(f)->private_data;
+}
+EXPORT_SYMBOL_GPL(backing_file_private_ptr);
+
static inline void file_free(struct file *f)
{
security_file_free(f);
diff --git a/fs/internal.h b/fs/internal.h
index 8c1b7acbbe8f..b1152a3e8ba2 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -100,6 +100,12 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
struct file *alloc_empty_file(int flags, const struct cred *cred);
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
+void **backing_file_private_ptr(struct file *f);
+
+static inline void *backing_file_private(struct file *f)
+{
+ return READ_ONCE(*backing_file_private_ptr(f));
+}
static inline void file_put_write_access(struct file *file)
{
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 3d64d00ef981..60a543b9a834 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -14,6 +14,8 @@
#include <linux/backing-file.h>
#include "overlayfs.h"
+#include "../internal.h" /* for backing_file_private{,_ptr}() */
+
static char ovl_whatisit(struct inode *inode, struct inode *realinode)
{
if (realinode != ovl_inode_upper(inode))
@@ -94,6 +96,7 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
{
struct dentry *dentry = file_dentry(file);
struct file *realfile = file->private_data;
+ struct file *upperfile = backing_file_private(realfile);
struct path realpath;
int err;
@@ -114,15 +117,37 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
if (!realpath.dentry)
return -EIO;
- /* Has it been copied up since we'd opened it? */
+stashed_upper:
+ if (upperfile && file_inode(upperfile) == d_inode(realpath.dentry))
+ realfile = upperfile;
+
+ /*
+ * If realfile is lower and has been copied up since we'd opened it,
+ * open the real upper file and stash it in backing_file_private().
+ */
if (unlikely(file_inode(realfile) != d_inode(realpath.dentry))) {
- struct file *f = ovl_open_realfile(file, &realpath);
- if (IS_ERR(f))
- return PTR_ERR(f);
- real->word = (unsigned long)f | FDPUT_FPUT;
- return 0;
+ struct file *old;
+
+ /* Stashed upperfile has a mismatched inode */
+ if (unlikely(upperfile))
+ return -EIO;
+
+ upperfile = ovl_open_realfile(file, &realpath);
+ if (IS_ERR(upperfile))
+ return PTR_ERR(upperfile);
+
+ old = cmpxchg_release(backing_file_private_ptr(realfile), NULL,
+ upperfile);
+ if (old) {
+ fput(upperfile);
+ upperfile = old;
+ }
+
+ goto stashed_upper;
}
+ real->word = (unsigned long)realfile;
+
/* Did the flags change since open? */
if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS))
return ovl_change_flags(realfile, file->f_flags);
@@ -177,7 +202,16 @@ static int ovl_open(struct inode *inode, struct file *file)
static int ovl_release(struct inode *inode, struct file *file)
{
- fput(file->private_data);
+ struct file *realfile = file->private_data;
+ struct file *upperfile = backing_file_private(realfile);
+
+ fput(realfile);
+ /*
+ * If realfile is lower and file was copied up and accessed, we need
+ * to put reference also on the stashed real upperfile.
+ */
+ if (upperfile)
+ fput(upperfile);
return 0;
}
--
2.34.1
next prev parent reply other threads:[~2024-10-04 10:23 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-04 10:23 [PATCH 0/4] Stash overlay real upper file in backing_file Amir Goldstein
2024-10-04 10:23 ` [PATCH 1/4] ovl: do not open non-data lower file for fsync Amir Goldstein
2024-10-04 22:16 ` Al Viro
2024-10-04 22:28 ` Al Viro
2024-10-05 1:35 ` Al Viro
2024-10-05 6:30 ` Amir Goldstein
2024-10-05 19:49 ` Al Viro
2024-10-06 8:03 ` Amir Goldstein
2024-10-04 10:23 ` Amir Goldstein [this message]
2024-10-04 10:23 ` [PATCH 3/4] ovl: convert ovl_real_fdget_meta() callers to ovl_real_file_meta() Amir Goldstein
2024-10-04 22:23 ` Al Viro
2024-10-05 12:37 ` Amir Goldstein
2024-10-04 10:23 ` [PATCH 4/4] ovl: convert ovl_real_fdget() callers to ovl_real_file() Amir Goldstein
2024-10-04 22:25 ` Al Viro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241004102342.179434-3-amir73il@gmail.com \
--to=amir73il@gmail.com \
--cc=brauner@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-unionfs@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).