From 869e4010433c8610ba59f9a6a310df8be228d718 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 22 Jan 2023 14:21:36 -0500 Subject: mtab: Mitigate the race between bfs_mtab_parse() and bfs_mtab_fill_types() Fixes #97. --- src/mtab.c | 37 +++++++++++++++++++++++++++++++++++-- tests/gnu/fstype_umount.out | 0 tests/gnu/fstype_umount.sh | 12 ++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/gnu/fstype_umount.out create mode 100644 tests/gnu/fstype_umount.sh diff --git a/src/mtab.c b/src/mtab.c index 39676e5..35ae51d 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -193,18 +193,51 @@ fail: } static void bfs_mtab_fill_types(struct bfs_mtab *mtab) { + const enum bfs_stat_flags flags = BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC; + 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; + char *dir = xdirname(path); + if (dir) { + fd = open(dir, O_SEARCH | O_CLOEXEC | O_DIRECTORY); + } + if (fd >= 0) { + path += xbaseoff(path); + } else { + fd = AT_FDCWD; + } + struct bfs_stat sb; - if (bfs_stat(AT_FDCWD, entry->path, BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC, &sb) != 0) { - continue; + if (bfs_stat(fd, path, flags, &sb) != 0) { + goto next; + } + + if (fd >= 0) { + struct bfs_stat parent; + if (bfs_stat(fd, NULL, flags, &parent) == 0) { + if (parent.dev == sb.dev) { + // Not a mount point any more (or a bind mount, but with the same fstype) + goto next; + } + } } 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); + } } mtab->types_filled = true; diff --git a/tests/gnu/fstype_umount.out b/tests/gnu/fstype_umount.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/gnu/fstype_umount.sh b/tests/gnu/fstype_umount.sh new file mode 100644 index 0000000..e817831 --- /dev/null +++ b/tests/gnu/fstype_umount.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +clean_scratch + +mkdir scratch/tmp +bfs_sudo mount -t tmpfs tmpfs scratch/tmp || skip +trap "bfs_sudo umount -R scratch/tmp" EXIT + +mkdir scratch/tmp/ram +bfs_sudo mount -t ramfs ramfs scratch/tmp/ram || skip + +bfs_diff scratch/tmp -path scratch/tmp -exec "${SUDO[@]}" umount scratch/tmp/ram \; , -fstype ramfs -print -- cgit v1.2.3