aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-12 18:59:44 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-12 18:59:44 -0500
commit3d3c6b89399a1b5e8a59ffbb8cb2a7797a9ef154 (patch)
tree81ec06b0edc6cff0d809d06228e5b2604119b33f
parent9d7459d834c28f55c82f1737f638a6c90e0c0e0f (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.c143
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
406out_dput: 449out_free_oe:
407 dput(lowerdentry);
408out_dput_upper:
409 dput(upperdentry);
410out_put_dir:
411 kfree(oe); 450 kfree(oe);
451out_put:
452 for (i = 0; i < ctr; i++)
453 dput(stack[i].dentry);
454 kfree(stack);
455out_put_upper:
456 dput(upperdentry);
412out: 457out:
413 return ERR_PTR(err); 458 return ERR_PTR(err);
414} 459}