From 18492f8142274081adeceb7a5757f86721687799 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 25 Jan 2023 13:33:52 -0500 Subject: mtab: Keep parent dirs open during fill_types() --- src/eval.c | 5 ++++ src/mtab.c | 83 ++++++++++++++++++++++++++++++++++++++++-------------------- src/printf.c | 4 +++ 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/eval.c b/src/eval.c index 32b2e0e..d297af1 100644 --- a/src/eval.c +++ b/src/eval.c @@ -509,6 +509,11 @@ bool eval_fstype(const struct bfs_expr *expr, struct bfs_eval *state) { } const char *type = bfs_fstype(mtab, statbuf); + if (!type) { + eval_report_error(state); + return false; + } + return strcmp(type, expr->argv[1]) == 0; } diff --git a/src/mtab.c b/src/mtab.c index a997183..68b4c07 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -192,60 +192,89 @@ fail: return NULL; } -static void bfs_mtab_fill_types(struct bfs_mtab *mtab) { +static int bfs_mtab_fill_types(struct bfs_mtab *mtab) { const enum bfs_stat_flags flags = BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC; + int ret = -1; + + // It's possible that /path/to/mount was unmounted between bfs_mtab_parse() and bfs_mtab_fill_types(). + // In that case, the dev_t of /path/to/mount will be the same as /path/to, which should not get its + // fstype from the old mount record of /path/to/mount. + // + // Detect this by comparing the st_dev of the parent (/path/to) and child (/path/to/mount). Only when + // they differ can the filesystem type actually change between them. As a minor optimization, we keep + // the parent directory open in case multiple mounts have the same parent (e.g. /mnt). + char *parent_dir = NULL; + int parent_fd = -1; + struct bfs_stat parent_stat; + int parent_ret; for (size_t i = 0; i < darray_length(mtab->entries); ++i) { struct bfs_mtab_entry *entry = &mtab->entries[i]; - - // It's possible that /path/to/mount was unmounted between bfs_mtab_parse() and bfs_mtab_fill_types(). - // In that case, the dev_t of /path/to/mount will be the same as /path/to, which should not get its - // fstype from the old mount record of /path/to/mount. - int fd = -1; const char *path = entry->path; + int fd = AT_FDCWD; + char *dir = xdirname(path); - if (dir) { - fd = open(dir, O_SEARCH | O_CLOEXEC | O_DIRECTORY); + if (!dir) { + goto fail; } - if (fd >= 0) { - path += xbaseoff(path); + + if (parent_dir && strcmp(parent_dir, dir) == 0) { + // Same parent + free(dir); } else { - fd = AT_FDCWD; + free(parent_dir); + parent_dir = dir; + + if (parent_fd >= 0) { + xclose(parent_fd); + } + parent_fd = open(parent_dir, O_SEARCH, O_CLOEXEC, O_DIRECTORY); + + parent_ret = -1; + if (parent_fd >= 0) { + parent_ret = bfs_stat(parent_fd, NULL, flags, &parent_stat); + } + } + + if (parent_fd >= 0) { + fd = parent_fd; + path += xbaseoff(path); } struct bfs_stat sb; if (bfs_stat(fd, path, flags, &sb) != 0) { - goto next; + continue; } - if (fd >= 0) { - struct bfs_stat parent; - if (bfs_stat(fd, NULL, flags, &parent) == 0) { - if (parent.dev == sb.dev && parent.ino != sb.ino) { - // Not a mount point any more (or a bind mount, but with the same fstype) - goto next; - } - } + if (parent_ret == 0 && parent_stat.dev == sb.dev && parent_stat.ino != sb.ino) { + // Not a mount point any more (or a bind mount, but with the same fstype) + continue; } struct trie_leaf *leaf = trie_insert_mem(&mtab->types, &sb.dev, sizeof(sb.dev)); if (leaf) { leaf->value = entry->type; - } - - next: - free(dir); - if (fd >= 0) { - xclose(fd); + } else { + goto fail; } } mtab->types_filled = true; + ret = 0; + +fail: + if (parent_fd >= 0) { + xclose(parent_fd); + } + free(parent_dir); + return ret; } const char *bfs_fstype(const struct bfs_mtab *mtab, const struct bfs_stat *statbuf) { if (!mtab->types_filled) { - bfs_mtab_fill_types((struct bfs_mtab *)mtab); + if (bfs_mtab_fill_types((struct bfs_mtab *)mtab) != 0) { + return NULL; + } } const struct trie_leaf *leaf = trie_find_mem(&mtab->types, &statbuf->dev, sizeof(statbuf->dev)); diff --git a/src/printf.c b/src/printf.c index 5af8362..7c0c8db 100644 --- a/src/printf.c +++ b/src/printf.c @@ -255,6 +255,10 @@ static int bfs_printf_F(CFILE *cfile, const struct bfs_printf *directive, const } const char *type = bfs_fstype(directive->ptr, statbuf); + if (!type) { + return -1; + } + return dyn_fprintf(cfile->file, directive, type); } -- cgit v1.2.3