Skip to content

Commit b5efccb

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: constant d_ino across copy up
When all layers are on the same fs, and iterating a directory which may contain copy up entries, call vfs_getattr() on the overlay entries to make sure that d_ino will be consistent with st_ino from stat(2). There is an overhead of lookup per upper entry in readdir. The overhead is minimal if the iterated entries are already in dcache. It is also quite useful for the common case of 'ls -l' that readdir() pre populates the dcache with the listed entries, making the following stat() calls faster. Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 31e8cce commit b5efccb

File tree

1 file changed

+111
-1
lines changed

1 file changed

+111
-1
lines changed

fs/overlayfs/readdir.c

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
#include <linux/rbtree.h>
1616
#include <linux/security.h>
1717
#include <linux/cred.h>
18+
#include <linux/ratelimit.h>
1819
#include "overlayfs.h"
1920

2021
struct ovl_cache_entry {
2122
unsigned int len;
2223
unsigned int type;
24+
u64 real_ino;
2325
u64 ino;
2426
struct list_head l_node;
2527
struct rb_node node;
@@ -44,6 +46,7 @@ struct ovl_readdir_data {
4446
struct ovl_cache_entry *first_maybe_whiteout;
4547
int count;
4648
int err;
49+
bool is_upper;
4750
bool d_type_supported;
4851
};
4952

@@ -82,6 +85,32 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
8285
return NULL;
8386
}
8487

88+
static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
89+
struct ovl_cache_entry *p)
90+
{
91+
/* Don't care if not doing ovl_iter() */
92+
if (!rdd->dentry)
93+
return false;
94+
95+
/* Always recalc d_ino for parent */
96+
if (strcmp(p->name, "..") == 0)
97+
return true;
98+
99+
/* If this is lower, then native d_ino will do */
100+
if (!rdd->is_upper)
101+
return false;
102+
103+
/*
104+
* Recalc d_ino for '.' and for all entries if dir is impure (contains
105+
* copied up entries)
106+
*/
107+
if ((p->name[0] == '.' && p->len == 1) ||
108+
ovl_test_flag(OVL_IMPURE, d_inode(rdd->dentry)))
109+
return true;
110+
111+
return false;
112+
}
113+
85114
static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
86115
const char *name, int len,
87116
u64 ino, unsigned int d_type)
@@ -97,7 +126,11 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
97126
p->name[len] = '\0';
98127
p->len = len;
99128
p->type = d_type;
129+
p->real_ino = ino;
100130
p->ino = ino;
131+
/* Defer setting d_ino for upper entry to ovl_iterate() */
132+
if (ovl_calc_d_ino(rdd, p))
133+
p->ino = 0;
101134
p->is_whiteout = false;
102135

103136
if (d_type == DT_CHR) {
@@ -290,6 +323,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
290323

291324
for (idx = 0; idx != -1; idx = next) {
292325
next = ovl_path_next(idx, dentry, &realpath);
326+
rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry;
293327

294328
if (next != -1) {
295329
err = ovl_dir_read(&realpath, &rdd);
@@ -355,11 +389,81 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
355389
return cache;
356390
}
357391

392+
/*
393+
* Set d_ino for upper entries. Non-upper entries should always report
394+
* the uppermost real inode ino and should not call this function.
395+
*
396+
* When not all layer are on same fs, report real ino also for upper.
397+
*
398+
* When all layers are on the same fs, and upper has a reference to
399+
* copy up origin, call vfs_getattr() on the overlay entry to make
400+
* sure that d_ino will be consistent with st_ino from stat(2).
401+
*/
402+
static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
403+
404+
{
405+
struct dentry *dir = path->dentry;
406+
struct dentry *this = NULL;
407+
enum ovl_path_type type;
408+
u64 ino = p->real_ino;
409+
int err = 0;
410+
411+
if (!ovl_same_sb(dir->d_sb))
412+
goto out;
413+
414+
if (p->name[0] == '.') {
415+
if (p->len == 1) {
416+
this = dget(dir);
417+
goto get;
418+
}
419+
if (p->len == 2 && p->name[1] == '.') {
420+
/* we shall not be moved */
421+
this = dget(dir->d_parent);
422+
goto get;
423+
}
424+
}
425+
this = lookup_one_len(p->name, dir, p->len);
426+
if (IS_ERR_OR_NULL(this) || !this->d_inode) {
427+
if (IS_ERR(this)) {
428+
err = PTR_ERR(this);
429+
this = NULL;
430+
goto fail;
431+
}
432+
goto out;
433+
}
434+
435+
get:
436+
type = ovl_path_type(this);
437+
if (OVL_TYPE_ORIGIN(type)) {
438+
struct kstat stat;
439+
struct path statpath = *path;
440+
441+
statpath.dentry = this;
442+
err = vfs_getattr(&statpath, &stat, STATX_INO, 0);
443+
if (err)
444+
goto fail;
445+
446+
WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev);
447+
ino = stat.ino;
448+
}
449+
450+
out:
451+
p->ino = ino;
452+
dput(this);
453+
return err;
454+
455+
fail:
456+
pr_warn_ratelimited("overlay: failed to look up (%s) for ino (%i)\n",
457+
p->name, err);
458+
goto out;
459+
}
460+
358461
static int ovl_iterate(struct file *file, struct dir_context *ctx)
359462
{
360463
struct ovl_dir_file *od = file->private_data;
361464
struct dentry *dentry = file->f_path.dentry;
362465
struct ovl_cache_entry *p;
466+
int err;
363467

364468
if (!ctx->pos)
365469
ovl_dir_reset(file);
@@ -380,9 +484,15 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
380484

381485
while (od->cursor != &od->cache->entries) {
382486
p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
383-
if (!p->is_whiteout)
487+
if (!p->is_whiteout) {
488+
if (!p->ino) {
489+
err = ovl_cache_update_ino(&file->f_path, p);
490+
if (err)
491+
return err;
492+
}
384493
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
385494
break;
495+
}
386496
od->cursor = p->l_node.next;
387497
ctx->pos++;
388498
}

0 commit comments

Comments
 (0)