aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2018-01-18 09:39:13 -0500
committerMiklos Szeredi <mszeredi@redhat.com>2018-01-24 05:26:05 -0500
commit4b91c30a5a19332e8dd10b601d05b72caf657730 (patch)
tree0c4048fa36d803cdcbe7c44e659174400830ef4b
parent7a9dadef9684aaf738e7ce7e2a9284cc5e165ebc (diff)
ovl: lookup connected ancestor of dir in inode cache
Decoding a dir file handle requires walking backward up to layer root and for lower dir also checking the index to see if any of the parents have been copied up. Lookup overlay ancestor dentry in inode/dentry cache by decoded real parents to shortcut looking up all the way back to layer root. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/export.c99
-rw-r--r--fs/overlayfs/inode.c21
-rw-r--r--fs/overlayfs/overlayfs.h3
3 files changed, 110 insertions, 13 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
index 361174810ce8..092e6e8c9258 100644
--- a/fs/overlayfs/export.c
+++ b/fs/overlayfs/export.c
@@ -295,6 +295,88 @@ fail:
295} 295}
296 296
297/* 297/*
298 * Lookup an indexed or hashed overlay dentry by real inode.
299 */
300static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
301 struct dentry *real,
302 struct ovl_layer *layer)
303{
304 struct dentry *this = NULL;
305 struct inode *inode;
306
307 inode = ovl_lookup_inode(sb, real, !layer->idx);
308 if (IS_ERR(inode))
309 return ERR_CAST(inode);
310 if (inode) {
311 this = d_find_any_alias(inode);
312 iput(inode);
313 }
314
315 /* TODO: use index when looking up by origin inode */
316 if (!this)
317 return NULL;
318
319 if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) {
320 dput(this);
321 this = ERR_PTR(-EIO);
322 }
323
324 return this;
325}
326
327/*
328 * Lookup an indexed or hashed overlay dentry, whose real dentry is an
329 * ancestor of @real.
330 */
331static struct dentry *ovl_lookup_real_ancestor(struct super_block *sb,
332 struct dentry *real,
333 struct ovl_layer *layer)
334{
335 struct dentry *next, *parent = NULL;
336 struct dentry *ancestor = ERR_PTR(-EIO);
337
338 if (real == layer->mnt->mnt_root)
339 return dget(sb->s_root);
340
341 /* Find the topmost indexed or hashed ancestor */
342 next = dget(real);
343 for (;;) {
344 parent = dget_parent(next);
345
346 /*
347 * Lookup a matching overlay dentry in inode/dentry
348 * cache or in index by real inode.
349 */
350 ancestor = ovl_lookup_real_inode(sb, next, layer);
351 if (ancestor)
352 break;
353
354 if (parent == layer->mnt->mnt_root) {
355 ancestor = dget(sb->s_root);
356 break;
357 }
358
359 /*
360 * If @real has been moved out of the layer root directory,
361 * we will eventully hit the real fs root. This cannot happen
362 * by legit overlay rename, so we return error in that case.
363 */
364 if (parent == next) {
365 ancestor = ERR_PTR(-EXDEV);
366 break;
367 }
368
369 dput(next);
370 next = parent;
371 }
372
373 dput(parent);
374 dput(next);
375
376 return ancestor;
377}
378
379/*
298 * Lookup a connected overlay dentry whose real dentry is @real. 380 * Lookup a connected overlay dentry whose real dentry is @real.
299 * If @real is on upper layer, we lookup a child overlay dentry with the same 381 * If @real is on upper layer, we lookup a child overlay dentry with the same
300 * path the real dentry. Otherwise, we need to consult index for lookup. 382 * path the real dentry. Otherwise, we need to consult index for lookup.
@@ -306,9 +388,10 @@ static struct dentry *ovl_lookup_real(struct super_block *sb,
306 struct dentry *connected; 388 struct dentry *connected;
307 int err = 0; 389 int err = 0;
308 390
309 /* TODO: use index when looking up by lower real dentry */ 391 connected = ovl_lookup_real_ancestor(sb, real, layer);
392 if (IS_ERR(connected))
393 return connected;
310 394
311 connected = dget(sb->s_root);
312 while (!err) { 395 while (!err) {
313 struct dentry *next, *this; 396 struct dentry *next, *this;
314 struct dentry *parent = NULL; 397 struct dentry *parent = NULL;
@@ -365,11 +448,15 @@ static struct dentry *ovl_lookup_real(struct super_block *sb,
365 * overlay rename of child away from 'connected' parent. 448 * overlay rename of child away from 'connected' parent.
366 * In this case, we need to restart the lookup from the 449 * In this case, we need to restart the lookup from the
367 * top, because we cannot trust that 'real_connected' is 450 * top, because we cannot trust that 'real_connected' is
368 * still an ancestor of 'real'. 451 * still an ancestor of 'real'. There is a good chance
452 * that the renamed overlay ancestor is now in cache, so
453 * ovl_lookup_real_ancestor() will find it and we can
454 * continue to connect exactly from where lookup failed.
369 */ 455 */
370 if (err == -ECHILD) { 456 if (err == -ECHILD) {
371 this = dget(sb->s_root); 457 this = ovl_lookup_real_ancestor(sb, real,
372 err = 0; 458 layer);
459 err = IS_ERR(this) ? PTR_ERR(this) : 0;
373 } 460 }
374 if (!err) { 461 if (!err) {
375 dput(connected); 462 dput(connected);
@@ -494,7 +581,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
494 } else if (is_deleted) { 581 } else if (is_deleted) {
495 /* Lookup deleted non-dir by origin inode */ 582 /* Lookup deleted non-dir by origin inode */
496 if (!d_is_dir(origin.dentry)) 583 if (!d_is_dir(origin.dentry))
497 inode = ovl_lookup_inode(sb, origin.dentry); 584 inode = ovl_lookup_inode(sb, origin.dentry, false);
498 err = -ESTALE; 585 err = -ESTALE;
499 if (!inode || atomic_read(&inode->i_count) == 1) 586 if (!inode || atomic_read(&inode->i_count) == 1)
500 goto out_err; 587 goto out_err;
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 416dc06835db..fcd97b783fa1 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -614,9 +614,15 @@ static int ovl_inode_set(struct inode *inode, void *data)
614} 614}
615 615
616static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, 616static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
617 struct dentry *upperdentry) 617 struct dentry *upperdentry, bool strict)
618{ 618{
619 if (S_ISDIR(inode->i_mode)) { 619 /*
620 * For directories, @strict verify from lookup path performs consistency
621 * checks, so NULL lower/upper in dentry must match NULL lower/upper in
622 * inode. Non @strict verify from NFS handle decode path passes NULL for
623 * 'unknown' lower/upper.
624 */
625 if (S_ISDIR(inode->i_mode) && strict) {
620 /* Real lower dir moved to upper layer under us? */ 626 /* Real lower dir moved to upper layer under us? */
621 if (!lowerdentry && ovl_inode_lower(inode)) 627 if (!lowerdentry && ovl_inode_lower(inode))
622 return false; 628 return false;
@@ -645,15 +651,17 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
645 return true; 651 return true;
646} 652}
647 653
648struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin) 654struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
655 bool is_upper)
649{ 656{
650 struct inode *inode, *key = d_inode(origin); 657 struct inode *inode, *key = d_inode(real);
651 658
652 inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); 659 inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key);
653 if (!inode) 660 if (!inode)
654 return NULL; 661 return NULL;
655 662
656 if (!ovl_verify_inode(inode, origin, NULL)) { 663 if (!ovl_verify_inode(inode, is_upper ? NULL : real,
664 is_upper ? real : NULL, false)) {
657 iput(inode); 665 iput(inode);
658 return ERR_PTR(-ESTALE); 666 return ERR_PTR(-ESTALE);
659 } 667 }
@@ -704,7 +712,8 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
704 * Verify that the underlying files stored in the inode 712 * Verify that the underlying files stored in the inode
705 * match those in the dentry. 713 * match those in the dentry.
706 */ 714 */
707 if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) { 715 if (!ovl_verify_inode(inode, lowerdentry, upperdentry,
716 true)) {
708 iput(inode); 717 iput(inode);
709 inode = ERR_PTR(-ESTALE); 718 inode = ERR_PTR(-ESTALE);
710 goto out; 719 goto out;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index a5d415aec131..bf17bf97c50f 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -323,7 +323,8 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
323bool ovl_is_private_xattr(const char *name); 323bool ovl_is_private_xattr(const char *name);
324 324
325struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); 325struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
326struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin); 326struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
327 bool is_upper);
327struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, 328struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
328 struct dentry *lowerdentry, struct dentry *index, 329 struct dentry *lowerdentry, struct dentry *index,
329 unsigned int numlower); 330 unsigned int numlower);