Skip to content

Commit 59be097

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: implement index dir copy up
Implement a copy up method for non-dir objects using index dir to prevent breaking lower hardlinks on copy up. This method requires that the inodes index dir feature was enabled and that all underlying fs support file handle encoding/decoding. On the first lower hardlink copy up, upper file is created in index dir, named after the hex representation of the lower origin inode file handle. On the second lower hardlink copy up, upper file is found in index dir, by the same lower handle key. On either case, the upper indexed inode is then linked to the copy up upper path. The index entry remains linked for future lower hardlink copy up and for lower to upper inode map, that is needed for exporting overlayfs to NFS. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent fd210b7 commit 59be097

File tree

3 files changed

+103
-37
lines changed

3 files changed

+103
-37
lines changed

fs/overlayfs/copy_up.c

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -316,27 +316,51 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
316316
return err;
317317
}
318318

319+
static int ovl_link_up(struct dentry *parent, struct dentry *dentry)
320+
{
321+
int err;
322+
struct dentry *upper;
323+
struct dentry *upperdir = ovl_dentry_upper(parent);
324+
struct inode *udir = d_inode(upperdir);
325+
326+
inode_lock_nested(udir, I_MUTEX_PARENT);
327+
upper = lookup_one_len(dentry->d_name.name, upperdir,
328+
dentry->d_name.len);
329+
err = PTR_ERR(upper);
330+
if (!IS_ERR(upper)) {
331+
err = ovl_do_link(ovl_dentry_upper(dentry), udir, upper, true);
332+
dput(upper);
333+
334+
if (!err)
335+
ovl_dentry_set_upper_alias(dentry);
336+
}
337+
inode_unlock(udir);
338+
339+
return err;
340+
}
341+
319342
struct ovl_copy_up_ctx {
320343
struct dentry *parent;
321344
struct dentry *dentry;
322345
struct path lowerpath;
323346
struct kstat stat;
324347
struct kstat pstat;
325348
const char *link;
326-
struct dentry *upperdir;
349+
struct dentry *destdir;
350+
struct qstr destname;
327351
struct dentry *workdir;
328352
bool tmpfile;
353+
bool origin;
329354
};
330355

331356
static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
332357
struct dentry **newdentry)
333358
{
334359
int err;
335360
struct dentry *upper;
336-
struct inode *udir = d_inode(c->upperdir);
361+
struct inode *udir = d_inode(c->destdir);
337362

338-
upper = lookup_one_len(c->dentry->d_name.name, c->upperdir,
339-
c->dentry->d_name.len);
363+
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
340364
if (IS_ERR(upper))
341365
return PTR_ERR(upper);
342366

@@ -345,11 +369,8 @@ static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
345369
else
346370
err = ovl_do_rename(d_inode(c->workdir), temp, udir, upper, 0);
347371

348-
/* Restore timestamps on parent (best effort) */
349-
if (!err) {
350-
ovl_set_timestamps(c->upperdir, &c->pstat);
372+
if (!err)
351373
*newdentry = dget(c->tmpfile ? upper : temp);
352-
}
353374
dput(upper);
354375

355376
return err;
@@ -439,7 +460,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
439460
* Don't set origin when we are breaking the association with a lower
440461
* hard link.
441462
*/
442-
if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1) {
463+
if (c->origin) {
443464
err = ovl_set_origin(c->dentry, c->lowerpath.dentry, temp);
444465
if (err)
445466
return err;
@@ -450,7 +471,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
450471

451472
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
452473
{
453-
struct inode *udir = c->upperdir->d_inode;
474+
struct inode *udir = c->destdir->d_inode;
454475
struct dentry *newdentry = NULL;
455476
struct dentry *temp = NULL;
456477
int err;
@@ -473,7 +494,6 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
473494
if (err)
474495
goto out_cleanup;
475496

476-
ovl_dentry_set_upper_alias(c->dentry);
477497
ovl_inode_update(d_inode(c->dentry), newdentry);
478498
out:
479499
dput(temp);
@@ -498,24 +518,57 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
498518
{
499519
int err;
500520
struct ovl_fs *ofs = c->dentry->d_sb->s_fs_info;
521+
bool indexed = false;
501522

502-
/* Mark parent "impure" because it may now contain non-pure upper */
503-
err = ovl_set_impure(c->parent, c->upperdir);
504-
if (err)
505-
return err;
523+
if (ovl_indexdir(c->dentry->d_sb) && !S_ISDIR(c->stat.mode) &&
524+
c->stat.nlink > 1)
525+
indexed = true;
526+
527+
if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || indexed)
528+
c->origin = true;
529+
530+
if (indexed) {
531+
c->destdir = ovl_indexdir(c->dentry->d_sb);
532+
err = ovl_get_index_name(c->lowerpath.dentry, &c->destname);
533+
if (err)
534+
return err;
535+
} else {
536+
/*
537+
* Mark parent "impure" because it may now contain non-pure
538+
* upper
539+
*/
540+
err = ovl_set_impure(c->parent, c->destdir);
541+
if (err)
542+
return err;
543+
}
506544

507545
/* Should we copyup with O_TMPFILE or with workdir? */
508546
if (S_ISREG(c->stat.mode) && ofs->tmpfile) {
509547
c->tmpfile = true;
510-
return ovl_copy_up_locked(c);
548+
err = ovl_copy_up_locked(c);
549+
} else {
550+
err = -EIO;
551+
if (lock_rename(c->workdir, c->destdir) != NULL) {
552+
pr_err("overlayfs: failed to lock workdir+upperdir\n");
553+
} else {
554+
err = ovl_copy_up_locked(c);
555+
unlock_rename(c->workdir, c->destdir);
556+
}
511557
}
512558

513-
err = -EIO;
514-
if (lock_rename(c->workdir, c->upperdir) != NULL) {
515-
pr_err("overlayfs: failed to lock workdir+upperdir\n");
516-
} else {
517-
err = ovl_copy_up_locked(c);
518-
unlock_rename(c->workdir, c->upperdir);
559+
if (indexed) {
560+
if (!err)
561+
ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
562+
kfree(c->destname.name);
563+
} else if (!err) {
564+
struct inode *udir = d_inode(c->destdir);
565+
566+
/* Restore timestamps on parent (best effort) */
567+
inode_lock(udir);
568+
ovl_set_timestamps(c->destdir, &c->pstat);
569+
inode_unlock(udir);
570+
571+
ovl_dentry_set_upper_alias(c->dentry);
519572
}
520573

521574
return err;
@@ -543,7 +596,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
543596
return err;
544597

545598
ovl_path_upper(parent, &parentpath);
546-
ctx.upperdir = parentpath.dentry;
599+
ctx.destdir = parentpath.dentry;
600+
ctx.destname = dentry->d_name;
547601

548602
err = vfs_getattr(&parentpath, &ctx.pstat,
549603
STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT);
@@ -567,7 +621,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
567621
if (err > 0)
568622
err = 0;
569623
} else {
570-
err = ovl_do_copy_up(&ctx);
624+
if (!ovl_dentry_upper(dentry))
625+
err = ovl_do_copy_up(&ctx);
626+
if (!err && !ovl_dentry_has_upper_alias(dentry))
627+
err = ovl_link_up(parent, dentry);
571628
ovl_copy_up_end(dentry);
572629
}
573630
do_delayed_call(&done);
@@ -583,18 +640,30 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
583640
while (!err) {
584641
struct dentry *next;
585642
struct dentry *parent;
586-
enum ovl_path_type type = ovl_path_type(dentry);
587643

588-
if (OVL_TYPE_UPPER(type))
644+
/*
645+
* Check if copy-up has happened as well as for upper alias (in
646+
* case of hard links) is there.
647+
*
648+
* Both checks are lockless:
649+
* - false negatives: will recheck under oi->lock
650+
* - false positives:
651+
* + ovl_dentry_upper() uses memory barriers to ensure the
652+
* upper dentry is up-to-date
653+
* + ovl_dentry_has_upper_alias() relies on locking of
654+
* upper parent i_rwsem to prevent reordering copy-up
655+
* with rename.
656+
*/
657+
if (ovl_dentry_upper(dentry) &&
658+
ovl_dentry_has_upper_alias(dentry))
589659
break;
590660

591661
next = dget(dentry);
592662
/* find the topmost dentry not yet copied up */
593663
for (;;) {
594664
parent = dget_parent(next);
595665

596-
type = ovl_path_type(parent);
597-
if (OVL_TYPE_UPPER(type))
666+
if (ovl_dentry_upper(parent))
598667
break;
599668

600669
dput(next);

fs/overlayfs/inode.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,13 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
305305
return acl;
306306
}
307307

308-
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
309-
struct dentry *realdentry)
308+
static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
310309
{
311-
if (OVL_TYPE_UPPER(type))
310+
if (ovl_dentry_upper(dentry) &&
311+
ovl_dentry_has_upper_alias(dentry))
312312
return false;
313313

314-
if (special_file(realdentry->d_inode->i_mode))
314+
if (special_file(d_inode(dentry)->i_mode))
315315
return false;
316316

317317
if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC))
@@ -323,11 +323,8 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
323323
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
324324
{
325325
int err = 0;
326-
struct path realpath;
327-
enum ovl_path_type type;
328326

329-
type = ovl_path_real(dentry, &realpath);
330-
if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
327+
if (ovl_open_need_copy_up(dentry, file_flags)) {
331328
err = ovl_want_write(dentry);
332329
if (!err) {
333330
err = ovl_copy_up_flags(dentry, file_flags);

fs/overlayfs/util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ int ovl_copy_up_start(struct dentry *dentry)
302302
int err;
303303

304304
err = mutex_lock_interruptible(&oi->lock);
305-
if (!err && ovl_dentry_upper(dentry)) {
305+
if (!err && ovl_dentry_has_upper_alias(dentry)) {
306306
err = 1; /* Already copied up */
307307
mutex_unlock(&oi->lock);
308308
}

0 commit comments

Comments
 (0)