summaryrefslogtreecommitdiffstats
path: root/src/mtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mtab.c')
-rw-r--r--src/mtab.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/mtab.c b/src/mtab.c
new file mode 100644
index 0000000..adc3f58
--- /dev/null
+++ b/src/mtab.c
@@ -0,0 +1,246 @@
+/****************************************************************************
+ * 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. *
+ ****************************************************************************/
+
+#include "mtab.h"
+#include "darray.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>
+#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>
+#endif
+
+/**
+ * A mount point in the table.
+ */
+struct bfs_mtab_entry {
+ /** The path to the mount point. */
+ char *path;
+ /** The filesystem type. */
+ char *type;
+};
+
+struct bfs_mtab {
+ /** The list of mount points. */
+ struct bfs_mtab_entry *entries;
+ /** The basenames of every mount point. */
+ struct trie names;
+
+ /** A map from device ID to fstype (populated lazily). */
+ struct trie types;
+ /** Whether the types map has been populated. */
+ bool types_filled;
+};
+
+/**
+ * Add an entry to the mount table.
+ */
+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;
+ }
+
+ if (DARRAY_PUSH(&mtab->entries, &entry) != 0) {
+ goto fail_entry;
+ }
+
+ if (!trie_insert_str(&mtab->names, xbasename(path))) {
+ goto fail;
+ }
+
+ return 0;
+
+fail_entry:
+ free(entry.type);
+ free(entry.path);
+fail:
+ return -1;
+}
+
+struct bfs_mtab *bfs_mtab_parse(void) {
+ struct bfs_mtab *mtab = malloc(sizeof(*mtab));
+ if (!mtab) {
+ return NULL;
+ }
+
+ mtab->entries = NULL;
+ trie_init(&mtab->names);
+ trie_init(&mtab->types);
+ mtab->types_filled = false;
+
+ int error = 0;
+
+#if BFS_MNTENT
+
+ FILE *file = setmntent(_PATH_MOUNTED, "r");
+ if (!file) {
+ // In case we're in a chroot or something with /proc but no /etc/mtab
+ error = errno;
+ file = setmntent("/proc/mounts", "r");
+ }
+ if (!file) {
+ goto fail;
+ }
+
+ struct mntent *mnt;
+ while ((mnt = getmntent(file))) {
+ if (bfs_mtab_add(mtab, mnt->mnt_dir, mnt->mnt_type) != 0) {
+ error = errno;
+ endmntent(file);
+ goto fail;
+ }
+ }
+
+ endmntent(file);
+
+#elif BFS_MNTINFO
+
+#if __NetBSD__
+ typedef struct statvfs bfs_statfs;
+#else
+ typedef struct statfs bfs_statfs;
+#endif
+
+ bfs_statfs *mntbuf;
+ int size = getmntinfo(&mntbuf, MNT_WAIT);
+ if (size < 0) {
+ error = errno;
+ goto fail;
+ }
+
+ for (bfs_statfs *mnt = mntbuf; mnt < mntbuf + size; ++mnt) {
+ if (bfs_mtab_add(mtab, mnt->f_mntonname, mnt->f_fstypename) != 0) {
+ error = errno;
+ goto fail;
+ }
+ }
+
+#elif BFS_MNTTAB
+
+ FILE *file = xfopen(MNTTAB, O_RDONLY | O_CLOEXEC);
+ if (!file) {
+ error = errno;
+ goto fail;
+ }
+
+ struct mnttab mnt;
+ while (getmntent(file, &mnt) == 0) {
+ if (bfs_mtab_add(mtab, mnt.mnt_mountp, mnt.mnt_fstype) != 0) {
+ error = errno;
+ fclose(file);
+ goto fail;
+ }
+ }
+
+ fclose(file);
+
+#else
+
+ error = ENOTSUP;
+ goto fail;
+
+#endif
+
+ return mtab;
+
+fail:
+ bfs_mtab_free(mtab);
+ errno = error;
+ 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];
+
+ struct bfs_stat sb;
+ if (bfs_stat(AT_FDCWD, entry->path, BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC, &sb) != 0) {
+ continue;
+ }
+
+ struct trie_leaf *leaf = trie_insert_mem(&mtab->types, &sb.dev, sizeof(sb.dev));
+ if (leaf) {
+ leaf->value = entry->type;
+ }
+ }
+
+ mtab->types_filled = true;
+}
+
+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);
+ }
+
+ const struct trie_leaf *leaf = trie_find_mem(&mtab->types, &statbuf->dev, sizeof(statbuf->dev));
+ if (leaf) {
+ return leaf->value;
+ } else {
+ return "unknown";
+ }
+}
+
+bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *path) {
+ const char *name = xbasename(path);
+ return trie_find_str(&mtab->names, name);
+}
+
+void bfs_mtab_free(struct bfs_mtab *mtab) {
+ if (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);
+ }
+}