diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2016-03-26 16:14:37 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2016-03-26 16:14:37 -0400 |
commit | d101a125954eae1d397adda94ca6319485a50493 (patch) | |
tree | 0f5853af51446f389c60f1a25f3875ef8a75ba41 /fs/overlayfs | |
parent | c9af28fdd44922a6c10c9f8315718408af98e315 (diff) |
fs: add file_dentry()
This series fixes bugs in nfs and ext4 due to 4bacc9c9234c ("overlayfs:
Make f_path always point to the overlay and f_inode to the underlay").
Regular files opened on overlayfs will result in the file being opened on
the underlying filesystem, while f_path points to the overlayfs
mount/dentry.
This confuses filesystems which get the dentry from struct file and assume
it's theirs.
Add a new helper, file_dentry() [*], to get the filesystem's own dentry
from the file. This checks file->f_path.dentry->d_flags against
DCACHE_OP_REAL, and returns file->f_path.dentry if DCACHE_OP_REAL is not
set (this is the common, non-overlayfs case).
In the uncommon case it will call into overlayfs's ->d_real() to get the
underlying dentry, matching file_inode(file).
The reason we need to check against the inode is that if the file is copied
up while being open, d_real() would return the upper dentry, while the open
file comes from the lower dentry.
[*] If possible, it's better simply to use file_inode() instead.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Tested-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
Reviewed-by: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: <stable@vger.kernel.org> # v4.2
Cc: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Daniel Axtens <dja@axtens.net>
Diffstat (limited to 'fs/overlayfs')
-rw-r--r-- | fs/overlayfs/super.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index ef64984c9bbc..5d972e6cd3fe 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -295,6 +295,37 @@ static void ovl_dentry_release(struct dentry *dentry) | |||
295 | } | 295 | } |
296 | } | 296 | } |
297 | 297 | ||
298 | static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) | ||
299 | { | ||
300 | struct dentry *real; | ||
301 | |||
302 | if (d_is_dir(dentry)) { | ||
303 | if (!inode || inode == d_inode(dentry)) | ||
304 | return dentry; | ||
305 | goto bug; | ||
306 | } | ||
307 | |||
308 | real = ovl_dentry_upper(dentry); | ||
309 | if (real && (!inode || inode == d_inode(real))) | ||
310 | return real; | ||
311 | |||
312 | real = ovl_dentry_lower(dentry); | ||
313 | if (!real) | ||
314 | goto bug; | ||
315 | |||
316 | if (!inode || inode == d_inode(real)) | ||
317 | return real; | ||
318 | |||
319 | /* Handle recursion */ | ||
320 | if (real->d_flags & DCACHE_OP_REAL) | ||
321 | return real->d_op->d_real(real, inode); | ||
322 | |||
323 | bug: | ||
324 | WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry, | ||
325 | inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0); | ||
326 | return dentry; | ||
327 | } | ||
328 | |||
298 | static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags) | 329 | static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags) |
299 | { | 330 | { |
300 | struct ovl_entry *oe = dentry->d_fsdata; | 331 | struct ovl_entry *oe = dentry->d_fsdata; |
@@ -339,11 +370,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) | |||
339 | static const struct dentry_operations ovl_dentry_operations = { | 370 | static const struct dentry_operations ovl_dentry_operations = { |
340 | .d_release = ovl_dentry_release, | 371 | .d_release = ovl_dentry_release, |
341 | .d_select_inode = ovl_d_select_inode, | 372 | .d_select_inode = ovl_d_select_inode, |
373 | .d_real = ovl_d_real, | ||
342 | }; | 374 | }; |
343 | 375 | ||
344 | static const struct dentry_operations ovl_reval_dentry_operations = { | 376 | static const struct dentry_operations ovl_reval_dentry_operations = { |
345 | .d_release = ovl_dentry_release, | 377 | .d_release = ovl_dentry_release, |
346 | .d_select_inode = ovl_d_select_inode, | 378 | .d_select_inode = ovl_d_select_inode, |
379 | .d_real = ovl_d_real, | ||
347 | .d_revalidate = ovl_dentry_revalidate, | 380 | .d_revalidate = ovl_dentry_revalidate, |
348 | .d_weak_revalidate = ovl_dentry_weak_revalidate, | 381 | .d_weak_revalidate = ovl_dentry_weak_revalidate, |
349 | }; | 382 | }; |