summaryrefslogtreecommitdiffstats
path: root/src/mtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mtab.c')
-rw-r--r--src/mtab.c208
1 files changed, 133 insertions, 75 deletions
diff --git a/src/mtab.c b/src/mtab.c
index adc3f58..7905d14 100644
--- a/src/mtab.c
+++ b/src/mtab.c
@@ -1,68 +1,59 @@
-/****************************************************************************
- * bfs *
- * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * Permission to use, copy, modify, and/or distribute this software for any *
- * purpose with or without fee is hereby granted. *
- * *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
- ****************************************************************************/
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+#include "prelude.h"
#include "mtab.h"
-#include "darray.h"
+#include "alloc.h"
+#include "bfstd.h"
#include "stat.h"
#include "trie.h"
-#include "util.h"
#include <errno.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
-#if BFS_HAS_SYS_PARAM
-# include <sys/param.h>
+#if !defined(BFS_USE_MNTENT) && BFS_USE_MNTENT_H
+# define BFS_USE_MNTENT true
+#elif !defined(BFS_USE_MNTINFO) && BSD
+# define BFS_USE_MNTINFO true
+#elif !defined(BFS_USE_MNTTAB) && __SVR4
+# define BFS_USE_MNTTAB true
#endif
-#if BFS_HAS_MNTENT
-# define BFS_MNTENT 1
-#elif BSD
-# define BFS_MNTINFO 1
-#elif __SVR4
-# define BFS_MNTTAB 1
-#endif
-
-#if BFS_MNTENT
-# include <mntent.h>
-# include <paths.h>
-# include <stdio.h>
-#elif BFS_MNTINFO
-# include <sys/mount.h>
-# include <sys/ucred.h>
-#elif BFS_MNTTAB
-# include <stdio.h>
-# include <sys/mnttab.h>
+#if BFS_USE_MNTENT
+# include <mntent.h>
+# include <paths.h>
+# include <stdio.h>
+#elif BFS_USE_MNTINFO
+# include <sys/mount.h>
+# include <sys/ucred.h>
+#elif BFS_USE_MNTTAB
+# include <stdio.h>
+# include <sys/mnttab.h>
#endif
/**
* A mount point in the table.
*/
-struct bfs_mtab_entry {
+struct bfs_mount {
/** The path to the mount point. */
char *path;
/** The filesystem type. */
char *type;
+ /** Buffer for the strings. */
+ char buf[];
};
struct bfs_mtab {
- /** The list of mount points. */
- struct bfs_mtab_entry *entries;
+ /** Mount point arena. */
+ struct varena varena;
+
+ /** The array of mount points. */
+ struct bfs_mount **mounts;
+ /** The number of mount points. */
+ size_t nmounts;
+
/** The basenames of every mount point. */
struct trie names;
@@ -75,47 +66,56 @@ struct bfs_mtab {
/**
* Add an entry to the mount table.
*/
+attr(maybe_unused)
static int bfs_mtab_add(struct bfs_mtab *mtab, const char *path, const char *type) {
- struct bfs_mtab_entry entry = {
- .path = strdup(path),
- .type = strdup(type),
- };
-
- if (!entry.path || !entry.type) {
- goto fail_entry;
+ size_t path_size = strlen(path) + 1;
+ size_t type_size = strlen(type) + 1;
+ size_t size = path_size + type_size;
+ struct bfs_mount *mount = varena_alloc(&mtab->varena, size);
+ if (!mount) {
+ return -1;
}
- if (DARRAY_PUSH(&mtab->entries, &entry) != 0) {
- goto fail_entry;
+ struct bfs_mount **ptr = RESERVE(struct bfs_mount *, &mtab->mounts, &mtab->nmounts);
+ if (!ptr) {
+ goto free;
}
+ *ptr = mount;
- if (!trie_insert_str(&mtab->names, xbasename(path))) {
- goto fail;
+ mount->path = mount->buf;
+ memcpy(mount->path, path, path_size);
+
+ mount->type = mount->buf + path_size;
+ memcpy(mount->type, type, type_size);
+
+ const char *name = path + xbaseoff(path);
+ if (!trie_insert_str(&mtab->names, name)) {
+ goto shrink;
}
return 0;
-fail_entry:
- free(entry.type);
- free(entry.path);
-fail:
+shrink:
+ --mtab->nmounts;
+free:
+ varena_free(&mtab->varena, mount, size);
return -1;
}
struct bfs_mtab *bfs_mtab_parse(void) {
- struct bfs_mtab *mtab = malloc(sizeof(*mtab));
+ struct bfs_mtab *mtab = ZALLOC(struct bfs_mtab);
if (!mtab) {
return NULL;
}
- mtab->entries = NULL;
+ VARENA_INIT(&mtab->varena, struct bfs_mount, buf);
+
trie_init(&mtab->names);
trie_init(&mtab->types);
- mtab->types_filled = false;
int error = 0;
-#if BFS_MNTENT
+#if BFS_USE_MNTENT
FILE *file = setmntent(_PATH_MOUNTED, "r");
if (!file) {
@@ -138,7 +138,7 @@ struct bfs_mtab *bfs_mtab_parse(void) {
endmntent(file);
-#elif BFS_MNTINFO
+#elif BFS_USE_MNTINFO
#if __NetBSD__
typedef struct statvfs bfs_statfs;
@@ -160,7 +160,7 @@ struct bfs_mtab *bfs_mtab_parse(void) {
}
}
-#elif BFS_MNTTAB
+#elif BFS_USE_MNTTAB
FILE *file = xfopen(MNTTAB, O_RDONLY | O_CLOEXEC);
if (!file) {
@@ -194,27 +194,89 @@ fail:
return NULL;
}
-static void bfs_mtab_fill_types(struct bfs_mtab *mtab) {
- for (size_t i = 0; i < darray_length(mtab->entries); ++i) {
- struct bfs_mtab_entry *entry = &mtab->entries[i];
+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;
+ int parent_ret = -1;
+ struct bfs_stat parent_stat;
+
+ for (size_t i = 0; i < mtab->nmounts; ++i) {
+ struct bfs_mount *mount = mtab->mounts[i];
+ const char *path = mount->path;
+ int fd = AT_FDCWD;
+
+ char *dir = xdirname(path);
+ if (!dir) {
+ goto fail;
+ }
+
+ if (parent_dir && strcmp(parent_dir, dir) == 0) {
+ // Same parent
+ free(dir);
+ } else {
+ 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(AT_FDCWD, entry->path, BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC, &sb) != 0) {
+ if (bfs_stat(fd, path, flags, &sb) != 0) {
+ continue;
+ }
+
+ 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;
+ leaf->value = mount->type;
+ } 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));
@@ -225,8 +287,7 @@ const char *bfs_fstype(const struct bfs_mtab *mtab, const struct bfs_stat *statb
}
}
-bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *path) {
- const char *name = xbasename(path);
+bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *name) {
return trie_find_str(&mtab->names, name);
}
@@ -235,11 +296,8 @@ void bfs_mtab_free(struct bfs_mtab *mtab) {
trie_destroy(&mtab->types);
trie_destroy(&mtab->names);
- for (size_t i = 0; i < darray_length(mtab->entries); ++i) {
- free(mtab->entries[i].type);
- free(mtab->entries[i].path);
- }
- darray_free(mtab->entries);
+ free(mtab->mounts);
+ varena_destroy(&mtab->varena);
free(mtab);
}