summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval.c5
-rw-r--r--src/mtab.c83
-rw-r--r--src/printf.c4
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);
}