aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/file.c44
1 files changed, 40 insertions, 4 deletions
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 50e4407398d8..ddfd93f13cc5 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -144,11 +144,47 @@ static int ovl_release(struct inode *inode, struct file *file)
144 144
145static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) 145static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
146{ 146{
147 struct inode *realinode = ovl_inode_real(file_inode(file)); 147 struct inode *inode = file_inode(file);
148 struct fd real;
149 const struct cred *old_cred;
150 ssize_t ret;
151
152 /*
153 * The two special cases below do not need to involve real fs,
154 * so we can optimizing concurrent callers.
155 */
156 if (offset == 0) {
157 if (whence == SEEK_CUR)
158 return file->f_pos;
159
160 if (whence == SEEK_SET)
161 return vfs_setpos(file, 0, 0);
162 }
163
164 ret = ovl_real_fdget(file, &real);
165 if (ret)
166 return ret;
148 167
149 return generic_file_llseek_size(file, offset, whence, 168 /*
150 realinode->i_sb->s_maxbytes, 169 * Overlay file f_pos is the master copy that is preserved
151 i_size_read(realinode)); 170 * through copy up and modified on read/write, but only real
171 * fs knows how to SEEK_HOLE/SEEK_DATA and real fs may impose
172 * limitations that are more strict than ->s_maxbytes for specific
173 * files, so we use the real file to perform seeks.
174 */
175 inode_lock(inode);
176 real.file->f_pos = file->f_pos;
177
178 old_cred = ovl_override_creds(inode->i_sb);
179 ret = vfs_llseek(real.file, offset, whence);
180 revert_creds(old_cred);
181
182 file->f_pos = real.file->f_pos;
183 inode_unlock(inode);
184
185 fdput(real);
186
187 return ret;
152} 188}
153 189
154static void ovl_file_accessed(struct file *file) 190static void ovl_file_accessed(struct file *file)