aboutsummaryrefslogtreecommitdiff
path: root/src/mountinfo/mountinfo.c
diff options
context:
space:
mode:
authorWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
committerWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
commit391d12db48754861b5cecac92ee3321597ee02c1 (patch)
treeb42fad5a31ca342de7b7ecf1fb78784194c1400c /src/mountinfo/mountinfo.c
parent0efc1b133e4182bd53cde78153bd8b5cc2e99448 (diff)
migrate fully to meson build system
- drop old build system - move shared include and source files to common directory - drop "rc-" prefix from shared include and source files - move executable-specific code to individual directories under src - adjust top-level .gitignore file for new build system This closes #489.
Diffstat (limited to 'src/mountinfo/mountinfo.c')
-rw-r--r--src/mountinfo/mountinfo.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/src/mountinfo/mountinfo.c b/src/mountinfo/mountinfo.c
new file mode 100644
index 00000000..c55b12ab
--- /dev/null
+++ b/src/mountinfo/mountinfo.c
@@ -0,0 +1,488 @@
+/*
+ * mountinfo.c
+ * Obtains information about mounted filesystems.
+ */
+
+/*
+ * Copyright 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+# include <sys/ucred.h>
+# include <sys/mount.h>
+# define F_FLAGS f_flags
+#elif defined(BSD) && !defined(__GNU__)
+# include <sys/statvfs.h>
+# define statfs statvfs
+# define F_FLAGS f_flag
+#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \
+ defined(__GLIBC__)) || defined(__GNU__)
+# include <mntent.h>
+#endif
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "_usage.h"
+
+const char *applet = NULL;
+const char *procmounts = "/proc/mounts";
+const char *extraopts = "[mount1] [mount2] ...";
+const char getoptstring[] = "f:F:n:N:o:O:p:P:iste:E:" getoptstring_COMMON;
+const struct option longopts[] = {
+ { "fstype-regex", 1, NULL, 'f'},
+ { "skip-fstype-regex", 1, NULL, 'F'},
+ { "node-regex", 1, NULL, 'n'},
+ { "skip-node-regex", 1, NULL, 'N'},
+ { "options-regex", 1, NULL, 'o'},
+ { "skip-options-regex", 1, NULL, 'O'},
+ { "point-regex", 1, NULL, 'p'},
+ { "skip-point-regex", 1, NULL, 'P'},
+ { "options", 0, NULL, 'i'},
+ { "fstype", 0, NULL, 's'},
+ { "node", 0, NULL, 't'},
+ { "netdev", 0, NULL, 'e'},
+ { "nonetdev", 0, NULL, 'E'},
+ longopts_COMMON
+};
+const char * const longopts_help[] = {
+ "fstype regex to find",
+ "fstype regex to skip",
+ "node regex to find",
+ "node regex to skip",
+ "options regex to find",
+ "options regex to skip",
+ "point regex to find",
+ "point regex to skip",
+ "print options",
+ "print fstype",
+ "print node",
+ "is it a network device",
+ "is it not a network device",
+ longopts_help_COMMON
+};
+const char *usagestring = NULL;
+
+typedef enum {
+ mount_from,
+ mount_to,
+ mount_fstype,
+ mount_options
+} mount_type;
+
+typedef enum {
+ net_ignore,
+ net_yes,
+ net_no
+} net_opts;
+
+struct args {
+ regex_t *node_regex;
+ regex_t *skip_node_regex;
+ regex_t *fstype_regex;
+ regex_t *skip_fstype_regex;
+ regex_t *options_regex;
+ regex_t *skip_options_regex;
+ RC_STRINGLIST *mounts;
+ mount_type mount_type;
+ net_opts netdev;
+};
+
+static int
+process_mount(RC_STRINGLIST *list, struct args *args,
+ char *from, char *to, char *fstype, char *options,
+ int netdev)
+{
+ char *p;
+ RC_STRING *s;
+
+ errno = ENOENT;
+
+#ifdef __linux__
+ /* Skip the really silly rootfs */
+ if (strcmp(fstype, "rootfs") == 0)
+ return -1;
+#endif
+
+ if (args->netdev == net_yes &&
+ (netdev != -1 || TAILQ_FIRST(args->mounts)))
+ {
+ if (netdev != 0)
+ return 1;
+ } else if (args->netdev == net_no &&
+ (netdev != -1 || TAILQ_FIRST(args->mounts)))
+ {
+ if (netdev != 1)
+ return 1;
+ } else {
+ if (args->node_regex &&
+ regexec(args->node_regex, from, 0, NULL, 0) != 0)
+ return 1;
+ if (args->skip_node_regex &&
+ regexec(args->skip_node_regex, from, 0, NULL, 0) == 0)
+ return 1;
+
+ if (args->fstype_regex &&
+ regexec(args->fstype_regex, fstype, 0, NULL, 0) != 0)
+ return -1;
+ if (args->skip_fstype_regex &&
+ regexec(args->skip_fstype_regex, fstype, 0, NULL, 0) == 0)
+ return -1;
+
+ if (args->options_regex &&
+ regexec(args->options_regex, options, 0, NULL, 0) != 0)
+ return -1;
+ if (args->skip_options_regex &&
+ regexec(args->skip_options_regex, options, 0, NULL, 0) == 0)
+ return -1;
+ }
+
+ if (TAILQ_FIRST(args->mounts)) {
+ TAILQ_FOREACH(s, args->mounts, entries)
+ if (strcmp(s->value, to) == 0)
+ break;
+ if (!s)
+ return -1;
+ }
+
+ switch (args->mount_type) {
+ case mount_from:
+ p = from;
+ break;
+ case mount_to:
+ p = to;
+ break;
+ case mount_fstype:
+ p = fstype;
+ break;
+ case mount_options:
+ p = options;
+ break;
+ default:
+ p = NULL;
+ errno = EINVAL;
+ break;
+ }
+
+ if (p) {
+ errno = 0;
+ rc_stringlist_add(list, p);
+ return 0;
+ }
+
+ return -1;
+}
+
+#if defined(BSD) && !defined(__GNU__)
+
+/* Translate the mounted options to english
+ * This is taken directly from FreeBSD mount.c */
+static struct opt {
+ int o_opt;
+ const char *o_name;
+} optnames[] = {
+ { MNT_ASYNC, "asynchronous" },
+ { MNT_EXPORTED, "NFS exported" },
+ { MNT_LOCAL, "local" },
+ { MNT_NOATIME, "noatime" },
+ { MNT_NOEXEC, "noexec" },
+ { MNT_NOSUID, "nosuid" },
+#ifdef MNT_NOSYMFOLLOW
+ { MNT_NOSYMFOLLOW, "nosymfollow" },
+#endif
+ { MNT_QUOTA, "with quotas" },
+ { MNT_RDONLY, "read-only" },
+ { MNT_SYNCHRONOUS, "synchronous" },
+ { MNT_UNION, "union" },
+#ifdef MNT_NOCLUSTERR
+ { MNT_NOCLUSTERR, "noclusterr" },
+#endif
+#ifdef MNT_NOCLUSTERW
+ { MNT_NOCLUSTERW, "noclusterw" },
+#endif
+#ifdef MNT_SUIDDIR
+ { MNT_SUIDDIR, "suiddir" },
+#endif
+ { MNT_SOFTDEP, "soft-updates" },
+#ifdef MNT_MULTILABEL
+ { MNT_MULTILABEL, "multilabel" },
+#endif
+#ifdef MNT_ACLS
+ { MNT_ACLS, "acls" },
+#endif
+#ifdef MNT_GJOURNAL
+ { MNT_GJOURNAL, "gjournal" },
+#endif
+ { 0, NULL }
+};
+
+static RC_STRINGLIST *
+find_mounts(struct args *args)
+{
+ struct statfs *mnts;
+ int nmnts;
+ int i;
+ RC_STRINGLIST *list;
+ char *options = NULL;
+ uint64_t flags;
+ struct opt *o;
+ int netdev;
+ char *tmp;
+
+ if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0)
+ eerrorx("getmntinfo: %s", strerror (errno));
+
+ list = rc_stringlist_new();
+ for (i = 0; i < nmnts; i++) {
+ netdev = 0;
+ flags = mnts[i].F_FLAGS & MNT_VISFLAGMASK;
+ for (o = optnames; flags && o->o_opt; o++) {
+ if (flags & o->o_opt) {
+ if (o->o_opt == MNT_LOCAL)
+ netdev = 1;
+ if (!options)
+ options = xstrdup(o->o_name);
+ else {
+ xasprintf(&tmp, "%s,%s", options, o->o_name);
+ free(options);
+ options = tmp;
+ }
+ }
+ flags &= ~o->o_opt;
+ }
+
+ process_mount(list, args,
+ mnts[i].f_mntfromname,
+ mnts[i].f_mntonname,
+ mnts[i].f_fstypename,
+ options,
+ netdev);
+
+ free(options);
+ options = NULL;
+ }
+
+ return list;
+}
+
+#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \
+ defined(__GLIBC__)) || defined(__GNU__)
+static struct mntent *
+getmntfile(const char *file)
+{
+ struct mntent *ent = NULL;
+ FILE *fp;
+
+ if (!exists("/etc/fstab"))
+ return NULL;
+
+ fp = setmntent("/etc/fstab", "r");
+ while ((ent = getmntent(fp)))
+ if (strcmp(file, ent->mnt_dir) == 0)
+ break;
+ endmntent(fp);
+
+ return ent;
+}
+
+static RC_STRINGLIST *
+find_mounts(struct args *args)
+{
+ FILE *fp;
+ char *buffer;
+ size_t size;
+ char *p;
+ char *from;
+ char *to;
+ char *fst;
+ char *opts;
+ struct mntent *ent;
+ int netdev;
+ RC_STRINGLIST *list;
+
+ if ((fp = fopen(procmounts, "r")) == NULL)
+ eerrorx("getmntinfo: %s", strerror(errno));
+
+ list = rc_stringlist_new();
+
+ buffer = NULL;
+ while (getline(&buffer, &size, fp) != -1) {
+ netdev = -1;
+ p = buffer;
+ from = strsep(&p, " ");
+ to = strsep(&p, " ");
+ fst = strsep(&p, " ");
+ opts = strsep(&p, " ");
+
+ if ((ent = getmntfile(to))) {
+ if (strstr(ent->mnt_opts, "_netdev"))
+ netdev = 0;
+ else
+ netdev = 1;
+ }
+
+ process_mount(list, args, from, to, fst, opts, netdev);
+ free(buffer);
+ buffer = NULL;
+ }
+ free(buffer);
+ fclose(fp);
+
+ return list;
+}
+
+#else
+# error "Operating system not supported!"
+#endif
+
+static regex_t *
+get_regex(const char *string)
+{
+ regex_t *reg = xmalloc(sizeof (*reg));
+ int result;
+ char buffer[256];
+
+ if ((result = regcomp(reg, string, REG_EXTENDED | REG_NOSUB)) != 0)
+ {
+ regerror(result, reg, buffer, sizeof(buffer));
+ eerrorx("%s: invalid regex `%s'", applet, buffer);
+ }
+
+ return reg;
+}
+
+int main(int argc, char **argv)
+{
+ struct args args;
+ regex_t *point_regex = NULL;
+ regex_t *skip_point_regex = NULL;
+ RC_STRINGLIST *nodes;
+ RC_STRING *s;
+ char *real_path = NULL;
+ int opt;
+ int result;
+ char *this_path;
+
+#define DO_REG(_var) \
+ if (_var) free(_var); \
+ _var = get_regex(optarg);
+#define REG_FREE(_var) \
+ if (_var) { regfree(_var); free(_var); }
+
+ applet = basename_c(argv[0]);
+ memset (&args, 0, sizeof(args));
+ args.mount_type = mount_to;
+ args.netdev = net_ignore;
+ args.mounts = rc_stringlist_new();
+
+ while ((opt = getopt_long(argc, argv, getoptstring,
+ longopts, (int *) 0)) != -1)
+ {
+ switch (opt) {
+ case 'e':
+ args.netdev = net_yes;
+ break;
+ case 'E':
+ args.netdev = net_no;
+ break;
+ case 'f':
+ DO_REG(args.fstype_regex);
+ break;
+ case 'F':
+ DO_REG(args.skip_fstype_regex);
+ break;
+ case 'n':
+ DO_REG(args.node_regex);
+ break;
+ case 'N':
+ DO_REG(args.skip_node_regex);
+ break;
+ case 'o':
+ DO_REG(args.options_regex);
+ break;
+ case 'O':
+ DO_REG(args.skip_options_regex);
+ break;
+ case 'p':
+ DO_REG(point_regex);
+ break;
+ case 'P':
+ DO_REG(skip_point_regex);
+ break;
+ case 'i':
+ args.mount_type = mount_options;
+ break;
+ case 's':
+ args.mount_type = mount_fstype;
+ break;
+ case 't':
+ args.mount_type = mount_from;
+ break;
+
+ case_RC_COMMON_GETOPT
+ }
+ }
+
+ while (optind < argc) {
+ if (argv[optind][0] != '/')
+ eerrorx("%s: `%s' is not a mount point",
+ argv[0], argv[optind]);
+ this_path = argv[optind++];
+ real_path = realpath(this_path, NULL);
+ if (real_path)
+ this_path = real_path;
+ rc_stringlist_add(args.mounts, this_path);
+ free(real_path);
+ real_path = NULL;
+ }
+ nodes = find_mounts(&args);
+ rc_stringlist_free(args.mounts);
+
+ REG_FREE(args.fstype_regex);
+ REG_FREE(args.skip_fstype_regex);
+ REG_FREE(args.node_regex);
+ REG_FREE(args.skip_node_regex);
+ REG_FREE(args.options_regex);
+ REG_FREE(args.skip_options_regex);
+
+ result = EXIT_FAILURE;
+
+ /* We should report the mounts in reverse order to ease unmounting */
+ TAILQ_FOREACH_REVERSE(s, nodes, rc_stringlist, entries) {
+ if (point_regex &&
+ regexec(point_regex, s->value, 0, NULL, 0) != 0)
+ continue;
+ if (skip_point_regex &&
+ regexec(skip_point_regex, s->value, 0, NULL, 0) == 0)
+ continue;
+ if (!rc_yesno(getenv("EINFO_QUIET")))
+ printf("%s\n", s->value);
+ result = EXIT_SUCCESS;
+ }
+ rc_stringlist_free(nodes);
+
+ REG_FREE(point_regex);
+ REG_FREE(skip_point_regex);
+
+ return result;
+}