Skip to content

Commit 359f392

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: lookup index entry for copy up origin
When inodes index feature is enabled, lookup in indexdir for the index entry of lower real inode or copy up origin inode. The index entry name is the hex representation of the lower inode file handle. If the index dentry in negative, then either no lower aliases have been copied up yet, or aliases have been copied up in older kernels and are not indexed. If the index dentry for a copy up origin inode is positive, but points to an inode different than the upper inode, then either the upper inode has been copied up and not indexed or it was indexed, but since then index dir was cleared. Either way, that index cannot be used to indentify the overlay inode. If a positive dentry that matches the upper inode was found, then it is safe to use the copy up origin st_ino for upper hardlinks, because all indexed upper hardlinks are represented by the same overlay inode as the copy up origin. Set the INDEX type flag on an indexed upper dentry. A non-upper dentry may also have a positive index from copy up of another lower hardlink. This situation will be handled by following patches. Index lookup is going to be used to prevent breaking hardlinks on copy up. Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 54fb347 commit 359f392

File tree

3 files changed

+116
-2
lines changed

3 files changed

+116
-2
lines changed

fs/overlayfs/inode.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
9696

9797
WARN_ON_ONCE(stat->dev != lowerstat.dev);
9898
/*
99-
* Lower hardlinks are broken on copy up to different
99+
* Lower hardlinks may be broken on copy up to different
100100
* upper files, so we cannot use the lower origin st_ino
101101
* for those different files, even for the same fs case.
102+
* With inodes index enabled, it is safe to use st_ino
103+
* of an indexed hardlinked origin. The index validates
104+
* that the upper hardlink is not broken.
102105
*/
103-
if (is_dir || lowerstat.nlink == 1)
106+
if (is_dir || lowerstat.nlink == 1 ||
107+
ovl_test_flag(OVL_INDEX, d_inode(dentry)))
104108
stat->ino = lowerstat.ino;
105109
}
106110
stat->dev = dentry->d_sb->s_dev;

fs/overlayfs/namei.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,94 @@ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
378378
goto out;
379379
}
380380

381+
/*
382+
* Lookup in indexdir for the index entry of a lower real inode or a copy up
383+
* origin inode. The index entry name is the hex representation of the lower
384+
* inode file handle.
385+
*
386+
* If the index dentry in negative, then either no lower aliases have been
387+
* copied up yet, or aliases have been copied up in older kernels and are
388+
* not indexed.
389+
*
390+
* If the index dentry for a copy up origin inode is positive, but points
391+
* to an inode different than the upper inode, then either the upper inode
392+
* has been copied up and not indexed or it was indexed, but since then
393+
* index dir was cleared. Either way, that index cannot be used to indentify
394+
* the overlay inode.
395+
*/
396+
int ovl_get_index_name(struct dentry *origin, struct qstr *name)
397+
{
398+
int err;
399+
struct ovl_fh *fh;
400+
char *n, *s;
401+
402+
fh = ovl_encode_fh(origin, false);
403+
if (IS_ERR(fh))
404+
return PTR_ERR(fh);
405+
406+
err = -ENOMEM;
407+
n = kzalloc(fh->len * 2, GFP_TEMPORARY);
408+
if (n) {
409+
s = bin2hex(n, fh, fh->len);
410+
*name = (struct qstr) QSTR_INIT(n, s - n);
411+
err = 0;
412+
}
413+
kfree(fh);
414+
415+
return err;
416+
417+
}
418+
419+
static struct dentry *ovl_lookup_index(struct dentry *dentry,
420+
struct dentry *upper,
421+
struct dentry *origin)
422+
{
423+
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
424+
struct dentry *index;
425+
struct inode *inode;
426+
struct qstr name;
427+
int err;
428+
429+
err = ovl_get_index_name(origin, &name);
430+
if (err)
431+
return ERR_PTR(err);
432+
433+
index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
434+
if (IS_ERR(index)) {
435+
pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n"
436+
"overlayfs: mount with '-o index=off' to disable inodes index.\n",
437+
d_inode(origin)->i_ino, name.len, name.name,
438+
err);
439+
goto out;
440+
}
441+
442+
if (d_is_negative(index)) {
443+
if (upper && d_inode(origin)->i_nlink > 1) {
444+
pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n",
445+
d_inode(origin)->i_ino);
446+
goto fail;
447+
}
448+
449+
dput(index);
450+
index = NULL;
451+
} else if (upper && d_inode(index) != d_inode(upper)) {
452+
inode = d_inode(index);
453+
pr_warn_ratelimited("overlayfs: wrong index found (index ino: %lu, upper ino: %lu).\n",
454+
d_inode(index)->i_ino,
455+
d_inode(upper)->i_ino);
456+
goto fail;
457+
}
458+
459+
out:
460+
kfree(name.name);
461+
return index;
462+
463+
fail:
464+
dput(index);
465+
index = ERR_PTR(-EIO);
466+
goto out;
467+
}
468+
381469
/*
382470
* Returns next layer in stack starting from top.
383471
* Returns -1 if this is the last layer.
@@ -409,6 +497,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
409497
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
410498
struct path *stack = NULL;
411499
struct dentry *upperdir, *upperdentry = NULL;
500+
struct dentry *index = NULL;
412501
unsigned int ctr = 0;
413502
struct inode *inode = NULL;
414503
bool upperopaque = false;
@@ -506,6 +595,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
506595
}
507596
}
508597

598+
/* Lookup index by lower inode and verify it matches upper inode */
599+
if (ctr && !d.is_dir && ovl_indexdir(dentry->d_sb)) {
600+
struct dentry *origin = stack[0].dentry;
601+
602+
index = ovl_lookup_index(dentry, upperdentry, origin);
603+
if (IS_ERR(index)) {
604+
err = PTR_ERR(index);
605+
index = NULL;
606+
goto out_put;
607+
}
608+
}
609+
509610
oe = ovl_alloc_entry(ctr);
510611
err = -ENOMEM;
511612
if (!oe)
@@ -515,16 +616,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
515616
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
516617
dentry->d_fsdata = oe;
517618

619+
if (index && !upperdentry)
620+
upperdentry = dget(index);
621+
518622
if (upperdentry || ctr) {
519623
err = -ENOMEM;
520624
inode = ovl_get_inode(dentry, upperdentry);
521625
if (!inode)
522626
goto out_free_oe;
523627

524628
OVL_I(inode)->redirect = upperredirect;
629+
if (index)
630+
ovl_set_flag(OVL_INDEX, inode);
525631
}
526632

527633
revert_creds(old_cred);
634+
dput(index);
528635
kfree(stack);
529636
kfree(d.redirect);
530637
d_add(dentry, inode);
@@ -535,6 +642,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
535642
dentry->d_fsdata = NULL;
536643
kfree(oe);
537644
out_put:
645+
dput(index);
538646
for (i = 0; i < ctr; i++)
539647
dput(stack[i].dentry);
540648
kfree(stack);

fs/overlayfs/overlayfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum ovl_path_type {
2828

2929
enum ovl_flag {
3030
OVL_IMPURE,
31+
OVL_INDEX,
3132
};
3233

3334
/*
@@ -236,6 +237,7 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
236237
/* namei.c */
237238
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
238239
struct dentry *origin, bool is_upper, bool set);
240+
int ovl_get_index_name(struct dentry *origin, struct qstr *name);
239241
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
240242
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
241243
bool ovl_lower_positive(struct dentry *dentry);

0 commit comments

Comments
 (0)