diff --git a/irmap.c b/irmap.c index aca773d4d..7d326c403 100644 --- a/irmap.c +++ b/irmap.c @@ -39,7 +39,7 @@ struct irmap { unsigned long ino; char *path; struct irmap *next; - + bool revalidate; int nr_kids; struct irmap *kids; }; @@ -69,6 +69,7 @@ static int irmap_update_stat(struct irmap *i) return -1; } + i->revalidate = false; i->dev = st.st_dev; i->ino = st.st_ino; if (!S_ISDIR(st.st_mode)) @@ -172,28 +173,65 @@ static struct irmap *irmap_scan(struct irmap *t, unsigned int dev, unsigned long return NULL; } +static int irmap_revalidate(struct irmap *c, struct irmap **p) +{ + struct stat st; + + pr_debug("Revalidate stat for %s\n", c->path); + if (fstatat(mntns_root, c->path + 1, &st, AT_SYMLINK_NOFOLLOW)) { + /* File can be (re)moved, so just treat it as invalid */ + pr_perror("Can't stat %s", c->path); + goto invalid; + } + + if (c->dev != st.st_dev) + goto invalid; + if (c->ino != st.st_ino) + goto invalid; + + c->revalidate = false; + return 0; + +invalid: + pr_debug("\t%x:%lx is invalid\n", c->dev, c->ino); + *p = c->next; + xfree(c->path); + xfree(c); + return 1; +} + char *irmap_lookup(unsigned int s_dev, unsigned long i_ino) { - struct irmap *c, *h; + struct irmap *c, *h, **p; + char *path = NULL; int hv; pr_debug("Resolving %x:%lx path\n", s_dev, i_ino); hv = irmap_hashfn(s_dev, i_ino); - for (c = cache[hv]; c; c = c->next) - if (c->dev == s_dev && c->ino == i_ino) { - pr_debug("\tFound %s in cache\n", c->path); - return c->path; - } + for (p = &cache[hv]; *p; p = &(*p)->next) { + c = *p; + if (!(c->dev == s_dev && c->ino == i_ino)) + continue; + + if (c->revalidate && irmap_revalidate(c, p)) + continue; + + pr_debug("\tFound %s in cache\n", c->path); + path = c->path; + goto out; + } for (h = hints; h->path; h++) { pr_debug("Scanning %s hint\n", h->path); c = irmap_scan(h, s_dev, i_ino); if (c) { pr_debug("\tScanned %s\n", c->path); - return c->path; + path = c->path; + goto out; } } - return NULL; +out: + return path; }