diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2023-01-22 14:21:36 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2023-01-22 14:21:36 -0500 |
commit | 869e4010433c8610ba59f9a6a310df8be228d718 (patch) | |
tree | 6054e9225ff8cd8e9a0276c7d2c8b0483b2fff16 | |
parent | d2f3a10f4373610e912e586814e26e2a06fd7d88 (diff) | |
download | bfs-869e4010433c8610ba59f9a6a310df8be228d718.tar.xz |
mtab: Mitigate the race between bfs_mtab_parse() and bfs_mtab_fill_types()
Fixes #97.
-rw-r--r-- | src/mtab.c | 37 | ||||
-rw-r--r-- | tests/gnu/fstype_umount.out | 0 | ||||
-rw-r--r-- | tests/gnu/fstype_umount.sh | 12 |
3 files changed, 47 insertions, 2 deletions
@@ -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 --- /dev/null +++ b/tests/gnu/fstype_umount.out 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 |