|
|
@@ -146,11 +146,47 @@ static int ovl_release(struct inode *inode, struct file *file)
|
|
|
|
|
|
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
|
|
{
|
|
|
- struct inode *realinode = ovl_inode_real(file_inode(file));
|
|
|
+ struct inode *inode = file_inode(file);
|
|
|
+ struct fd real;
|
|
|
+ const struct cred *old_cred;
|
|
|
+ ssize_t ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The two special cases below do not need to involve real fs,
|
|
|
+ * so we can optimizing concurrent callers.
|
|
|
+ */
|
|
|
+ if (offset == 0) {
|
|
|
+ if (whence == SEEK_CUR)
|
|
|
+ return file->f_pos;
|
|
|
+
|
|
|
+ if (whence == SEEK_SET)
|
|
|
+ return vfs_setpos(file, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ovl_real_fdget(file, &real);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- return generic_file_llseek_size(file, offset, whence,
|
|
|
- realinode->i_sb->s_maxbytes,
|
|
|
- i_size_read(realinode));
|
|
|
+ /*
|
|
|
+ * Overlay file f_pos is the master copy that is preserved
|
|
|
+ * through copy up and modified on read/write, but only real
|
|
|
+ * fs knows how to SEEK_HOLE/SEEK_DATA and real fs may impose
|
|
|
+ * limitations that are more strict than ->s_maxbytes for specific
|
|
|
+ * files, so we use the real file to perform seeks.
|
|
|
+ */
|
|
|
+ inode_lock(inode);
|
|
|
+ real.file->f_pos = file->f_pos;
|
|
|
+
|
|
|
+ old_cred = ovl_override_creds(inode->i_sb);
|
|
|
+ ret = vfs_llseek(real.file, offset, whence);
|
|
|
+ revert_creds(old_cred);
|
|
|
+
|
|
|
+ file->f_pos = real.file->f_pos;
|
|
|
+ inode_unlock(inode);
|
|
|
+
|
|
|
+ fdput(real);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void ovl_file_accessed(struct file *file)
|