diff options
| author | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:44 -0500 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:44 -0500 |
| commit | 3d3c6b89399a1b5e8a59ffbb8cb2a7797a9ef154 (patch) | |
| tree | 81ec06b0edc6cff0d809d06228e5b2604119b33f | |
| parent | 9d7459d834c28f55c82f1737f638a6c90e0c0e0f (diff) | |
ovl: multi-layer lookup
Look up dentry in all relevant layers.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
| -rw-r--r-- | fs/overlayfs/super.c | 143 |
1 files changed, 94 insertions, 49 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c245043aa1b9..f72b82fdc1e6 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
| @@ -333,82 +333,127 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
| 333 | unsigned int flags) | 333 | unsigned int flags) |
| 334 | { | 334 | { |
| 335 | struct ovl_entry *oe; | 335 | struct ovl_entry *oe; |
| 336 | struct dentry *upperdir; | 336 | struct ovl_entry *poe = dentry->d_parent->d_fsdata; |
| 337 | struct path lowerdir; | 337 | struct path *stack = NULL; |
| 338 | struct dentry *upperdentry = NULL; | 338 | struct dentry *upperdir, *upperdentry = NULL; |
| 339 | struct dentry *lowerdentry = NULL; | 339 | unsigned int ctr = 0; |
| 340 | struct inode *inode = NULL; | 340 | struct inode *inode = NULL; |
| 341 | bool upperopaque = false; | ||
| 342 | struct dentry *this, *prev = NULL; | ||
| 343 | unsigned int i; | ||
| 341 | int err; | 344 | int err; |
| 342 | 345 | ||
| 343 | err = -ENOMEM; | 346 | upperdir = ovl_upperdentry_dereference(poe); |
| 344 | oe = ovl_alloc_entry(1); | ||
| 345 | if (!oe) | ||
| 346 | goto out; | ||
| 347 | |||
| 348 | upperdir = ovl_dentry_upper(dentry->d_parent); | ||
| 349 | ovl_path_lower(dentry->d_parent, &lowerdir); | ||
| 350 | |||
| 351 | if (upperdir) { | 347 | if (upperdir) { |
| 352 | upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); | 348 | this = ovl_lookup_real(upperdir, &dentry->d_name); |
| 353 | err = PTR_ERR(upperdentry); | 349 | err = PTR_ERR(this); |
| 354 | if (IS_ERR(upperdentry)) | 350 | if (IS_ERR(this)) |
| 355 | goto out_put_dir; | 351 | goto out; |
| 356 | 352 | ||
| 357 | if (lowerdir.dentry && upperdentry) { | 353 | /* |
| 358 | if (ovl_is_whiteout(upperdentry)) { | 354 | * If this is not the lowermost layer, check whiteout and opaque |
| 359 | dput(upperdentry); | 355 | * directory. |
| 360 | upperdentry = NULL; | 356 | */ |
| 361 | oe->opaque = true; | 357 | if (poe->numlower && this) { |
| 362 | } else if (ovl_is_opaquedir(upperdentry)) { | 358 | if (ovl_is_whiteout(this)) { |
| 363 | oe->opaque = true; | 359 | dput(this); |
| 360 | this = NULL; | ||
| 361 | upperopaque = true; | ||
| 362 | } else if (ovl_is_opaquedir(this)) { | ||
| 363 | upperopaque = true; | ||
| 364 | } | 364 | } |
| 365 | } | 365 | } |
| 366 | upperdentry = prev = this; | ||
| 366 | } | 367 | } |
| 367 | if (lowerdir.dentry && !oe->opaque) { | 368 | |
| 368 | lowerdentry = ovl_lookup_real(lowerdir.dentry, &dentry->d_name); | 369 | if (!upperopaque && poe->numlower) { |
| 369 | err = PTR_ERR(lowerdentry); | 370 | err = -ENOMEM; |
| 370 | if (IS_ERR(lowerdentry)) | 371 | stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL); |
| 371 | goto out_dput_upper; | 372 | if (!stack) |
| 373 | goto out_put_upper; | ||
| 372 | } | 374 | } |
| 373 | 375 | ||
| 374 | if (lowerdentry && upperdentry && | 376 | for (i = 0; !upperopaque && i < poe->numlower; i++) { |
| 375 | (!S_ISDIR(upperdentry->d_inode->i_mode) || | 377 | bool opaque = false; |
| 376 | !S_ISDIR(lowerdentry->d_inode->i_mode))) { | 378 | struct path lowerpath = poe->lowerstack[i]; |
| 377 | dput(lowerdentry); | 379 | |
| 378 | lowerdentry = NULL; | 380 | opaque = false; |
| 379 | oe->opaque = true; | 381 | this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name); |
| 382 | err = PTR_ERR(this); | ||
| 383 | if (IS_ERR(this)) | ||
| 384 | goto out_put; | ||
| 385 | if (!this) | ||
| 386 | continue; | ||
| 387 | |||
| 388 | /* | ||
| 389 | * If this is not the lowermost layer, check whiteout and opaque | ||
| 390 | * directory. | ||
| 391 | */ | ||
| 392 | if (i < poe->numlower - 1) { | ||
| 393 | if (ovl_is_whiteout(this)) { | ||
| 394 | dput(this); | ||
| 395 | break; | ||
| 396 | } else if (ovl_is_opaquedir(this)) { | ||
| 397 | opaque = true; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | /* | ||
| 401 | * If this is a non-directory then stop here. | ||
| 402 | * | ||
| 403 | * FIXME: check for opaqueness maybe better done in remove code. | ||
| 404 | */ | ||
| 405 | if (!S_ISDIR(this->d_inode->i_mode)) { | ||
| 406 | opaque = true; | ||
| 407 | } else if (prev && (!S_ISDIR(prev->d_inode->i_mode) || | ||
| 408 | !S_ISDIR(this->d_inode->i_mode))) { | ||
| 409 | if (prev == upperdentry) | ||
| 410 | upperopaque = true; | ||
| 411 | dput(this); | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | stack[ctr].dentry = this; | ||
| 415 | stack[ctr].mnt = lowerpath.mnt; | ||
| 416 | ctr++; | ||
| 417 | prev = this; | ||
| 418 | if (opaque) | ||
| 419 | break; | ||
| 380 | } | 420 | } |
| 381 | 421 | ||
| 382 | if (lowerdentry || upperdentry) { | 422 | oe = ovl_alloc_entry(ctr); |
| 423 | err = -ENOMEM; | ||
| 424 | if (!oe) | ||
| 425 | goto out_put; | ||
| 426 | |||
| 427 | if (upperdentry || ctr) { | ||
| 383 | struct dentry *realdentry; | 428 | struct dentry *realdentry; |
| 384 | 429 | ||
| 385 | realdentry = upperdentry ? upperdentry : lowerdentry; | 430 | realdentry = upperdentry ? upperdentry : stack[0].dentry; |
| 431 | |||
| 386 | err = -ENOMEM; | 432 | err = -ENOMEM; |
| 387 | inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, | 433 | inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, |
| 388 | oe); | 434 | oe); |
| 389 | if (!inode) | 435 | if (!inode) |
| 390 | goto out_dput; | 436 | goto out_free_oe; |
| 391 | ovl_copyattr(realdentry->d_inode, inode); | 437 | ovl_copyattr(realdentry->d_inode, inode); |
| 392 | } | 438 | } |
| 393 | 439 | ||
| 440 | oe->opaque = upperopaque; | ||
| 394 | oe->__upperdentry = upperdentry; | 441 | oe->__upperdentry = upperdentry; |
| 395 | if (lowerdentry) { | 442 | memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr); |
| 396 | oe->lowerstack[0].dentry = lowerdentry; | 443 | kfree(stack); |
| 397 | oe->lowerstack[0].mnt = lowerdir.mnt; | ||
| 398 | } else { | ||
| 399 | oe->numlower = 0; | ||
| 400 | } | ||
| 401 | dentry->d_fsdata = oe; | 444 | dentry->d_fsdata = oe; |
| 402 | d_add(dentry, inode); | 445 | d_add(dentry, inode); |
| 403 | 446 | ||
| 404 | return NULL; | 447 | return NULL; |
| 405 | 448 | ||
| 406 | out_dput: | 449 | out_free_oe: |
| 407 | dput(lowerdentry); | ||
| 408 | out_dput_upper: | ||
| 409 | dput(upperdentry); | ||
| 410 | out_put_dir: | ||
| 411 | kfree(oe); | 450 | kfree(oe); |
| 451 | out_put: | ||
| 452 | for (i = 0; i < ctr; i++) | ||
| 453 | dput(stack[i].dentry); | ||
| 454 | kfree(stack); | ||
| 455 | out_put_upper: | ||
| 456 | dput(upperdentry); | ||
| 412 | out: | 457 | out: |
| 413 | return ERR_PTR(err); | 458 | return ERR_PTR(err); |
| 414 | } | 459 | } |
