Skip to content

Commit 18e48d0

Browse files
committed
ovl: store upper real file in ovl_file struct
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. Store the upper real file along side the original (lower) real file in ovl_file instead of opening a temporary upper file on every operation. Signed-off-by: Amir Goldstein <amir73il@gmail.com>
1 parent 87a8a76 commit 18e48d0

File tree

1 file changed

+41
-8
lines changed

1 file changed

+41
-8
lines changed

fs/overlayfs/file.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
9191

9292
struct ovl_file {
9393
struct file *realfile;
94+
struct file *upperfile;
9495
};
9596

9697
struct ovl_file *ovl_file_alloc(struct file *realfile)
@@ -107,33 +108,65 @@ struct ovl_file *ovl_file_alloc(struct file *realfile)
107108
void ovl_file_free(struct ovl_file *of)
108109
{
109110
fput(of->realfile);
111+
if (of->upperfile)
112+
fput(of->upperfile);
110113
kfree(of);
111114
}
112115

116+
static bool ovl_is_real_file(const struct file *realfile,
117+
const struct path *realpath)
118+
{
119+
return file_inode(realfile) == d_inode(realpath->dentry);
120+
}
121+
113122
static int ovl_real_fdget_path(const struct file *file, struct fd *real,
114123
struct path *realpath)
115124
{
116125
struct ovl_file *of = file->private_data;
117126
struct file *realfile = of->realfile;
118127

119-
real->word = (unsigned long)realfile;
128+
real->word = 0;
120129

121130
if (WARN_ON_ONCE(!realpath->dentry))
122131
return -EIO;
123132

124-
/* Has it been copied up since we'd opened it? */
125-
if (unlikely(file_inode(realfile) != d_inode(realpath->dentry))) {
126-
struct file *f = ovl_open_realfile(file, realpath);
127-
if (IS_ERR(f))
128-
return PTR_ERR(f);
129-
real->word = (unsigned long)f | FDPUT_FPUT;
130-
return 0;
133+
/*
134+
* If the realfile that we want is not where the data used to be at
135+
* open time, either we'd been copied up, or it's an fsync of a
136+
* metacopied file. We need the upperfile either way, so see if it
137+
* is already opened and if it is not then open and store it.
138+
*/
139+
if (unlikely(!ovl_is_real_file(realfile, realpath))) {
140+
struct file *upperfile = READ_ONCE(of->upperfile);
141+
struct file *old;
142+
143+
if (!upperfile) { /* Nobody opened upperfile yet */
144+
upperfile = ovl_open_realfile(file, realpath);
145+
if (IS_ERR(upperfile))
146+
return PTR_ERR(upperfile);
147+
148+
/* Store the upperfile for later */
149+
old = cmpxchg_release(&of->upperfile, NULL, upperfile);
150+
if (old) { /* Someone opened upperfile before us */
151+
fput(upperfile);
152+
upperfile = old;
153+
}
154+
}
155+
/*
156+
* Stored file must be from the right inode, unless someone's
157+
* been corrupting the upper layer.
158+
*/
159+
if (WARN_ON_ONCE(!ovl_is_real_file(upperfile, realpath)))
160+
return -EIO;
161+
162+
realfile = upperfile;
131163
}
132164

133165
/* Did the flags change since open? */
134166
if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS))
135167
return ovl_change_flags(realfile, file->f_flags);
136168

169+
real->word = (unsigned long)realfile;
137170
return 0;
138171
}
139172

0 commit comments

Comments
 (0)