diff options
Diffstat (limited to 'fs')
-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 | } |