/** * mnt_table_get_root_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @root: returns pointer to the root filesystem (/) * * The function uses parent ID from mountinfo file to determine root filesystem * (the filesystem with the smallest ID). The function is designed mostly for * applications where is necessary to sort mountpoints by IDs to get the tree * of the mountpoints (e.g. findmnt default output). * * If you're not sure than use * * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD); * * this is more robust and usable for arbitrary tab file (including fstab). * * Returns: 0 on success or less then zero case of error. */ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) { struct libmnt_iter itr; struct libmnt_fs *fs; int root_id = 0; assert(tb); assert(root); if (!tb || !root) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup root fs")); mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { int id = mnt_fs_get_parent_id(fs); if (!id) break; /* @tab is not mountinfo file? */ if (!*root || id < root_id) { *root = fs; root_id = id; } } return root_id ? 0 : -EINVAL; }
/** * mnt_table_get_root_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @root: returns pointer to the root filesystem (/) * * The function uses the parent ID from the mountinfo file to determine the root filesystem * (the filesystem with the smallest ID). The function is designed mostly for * applications where it is necessary to sort mountpoints by IDs to get the tree * of the mountpoints (e.g. findmnt default output). * * If you're not sure, then use * * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD); * * this is more robust and usable for arbitrary tab files (including fstab). * * Returns: 0 on success or negative number in case of error. */ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) { struct libmnt_iter itr; struct libmnt_fs *fs; int root_id = 0; assert(tb); assert(root); if (!tb || !root || !is_mountinfo(tb)) return -EINVAL; DBG(TAB, ul_debugobj(tb, "lookup root fs")); *root = NULL; mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { int id = mnt_fs_get_parent_id(fs); if (!*root || id < root_id) { *root = fs; root_id = id; } } return *root ? 0 : -EINVAL; }
/** * mnt_table_parse_mtab: * @tb: table * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL * * This function parses /etc/mtab or /proc/self/mountinfo + * /run/mount/utabs or /proc/mounts. * * See also mnt_table_set_parser_errcb(). * * Returns: 0 on success or negative number in case of error. */ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) { int rc; const char *utab = NULL; struct libmnt_table *u_tb; assert(tb); if (mnt_has_regular_mtab(&filename, NULL)) { DBG(TAB, mnt_debug_h(tb, "force %s usage", filename)); rc = mnt_table_parse_file(tb, filename); if (!rc) return 0; filename = NULL; /* failed */ } /* * useless /etc/mtab * -- read kernel information from /proc/self/mountinfo */ tb->fmt = MNT_FMT_MOUNTINFO; rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO); if (rc) { /* hmm, old kernel? ...try /proc/mounts */ tb->fmt = MNT_FMT_MTAB; return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); } if (mnt_table_get_nents(tb) == 0) return 0; /* empty, ignore utab */ /* * try to read the user specific information from /run/mount/utabs */ utab = mnt_get_utab_path(); if (!utab || is_file_empty(utab)) return 0; u_tb = mnt_new_table(); if (!u_tb) return -ENOMEM; u_tb->fmt = MNT_FMT_UTAB; mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data); if (mnt_table_parse_file(u_tb, utab) == 0) { struct libmnt_fs *u_fs; struct libmnt_iter itr; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); /* merge user options into mountinfo from the kernel */ while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) mnt_table_merge_user_fs(tb, u_fs); } mnt_unref_table(u_tb); return 0; }
static int update_table(struct libmnt_update *upd, struct libmnt_table *tb) { FILE *f; int rc, fd; char *uq = NULL; if (!tb || !upd->filename) return -EINVAL; DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename)); fd = mnt_open_uniq_filename(upd->filename, &uq); if (fd < 0) return fd; /* error */ f = fdopen(fd, "w"); if (f) { struct stat st; struct libmnt_iter itr; struct libmnt_fs *fs; int fd; mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (upd->userspace_only) rc = fprintf_utab_fs(f, fs); else rc = fprintf_mtab_fs(f, fs); if (rc) { DBG(UPDATE, mnt_debug_h(upd, "%s: write entry failed: %m", uq)); goto leave; } } if (fflush(f) != 0) { rc = -errno; DBG(UPDATE, mnt_debug_h(upd, "%s: fflush failed: %m", uq)); goto leave; } fd = fileno(f); rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; if (!rc && stat(upd->filename, &st) == 0) /* Copy uid/gid from the present file before renaming. */ rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0; fclose(f); rc = rename(uq, upd->filename) ? -errno : 0; } else { rc = -errno; close(fd); } leave: unlink(uq); /* be paranoid */ free(uq); return rc; }
/** * mnt_context_next_umount: * @cxt: context * @itr: iterator * @fs: returns the current filesystem * @mntrc: returns the return code from mnt_context_umount() * @ignored: returns 1 for not matching * * This function tries to umount the next filesystem from mtab (as returned by * mnt_context_get_mtab()). * * You can filter out filesystems by: * mnt_context_set_options_pattern() to simulate umount -a -O pattern * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern * * If the filesystem is not mounted or does not match the defined criteria, * then the function mnt_context_next_umount() returns zero, but the @ignored is * non-zero. Note that the root filesystem is always ignored. * * If umount(2) syscall or umount.type helper failed, then the * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero. * Use also mnt_context_get_status() to check if the filesystem was * successfully umounted. * * Returns: 0 on success, * <0 in case of error (!= umount(2) errors) * 1 at the end of the list. */ int mnt_context_next_umount(struct libmnt_context *cxt, struct libmnt_iter *itr, struct libmnt_fs **fs, int *mntrc, int *ignored) { struct libmnt_table *mtab; const char *tgt; int rc; if (ignored) *ignored = 0; if (mntrc) *mntrc = 0; if (!cxt || !fs || !itr) return -EINVAL; rc = mnt_context_get_mtab(cxt, &mtab); cxt->mtab = NULL; /* do not reset mtab */ mnt_reset_context(cxt); cxt->mtab = mtab; if (rc) return rc; do { rc = mnt_table_next_fs(mtab, itr, fs); if (rc != 0) return rc; /* no more filesystems (or error) */ tgt = mnt_fs_get_target(*fs); } while (!tgt); DBG(CXT, ul_debugobj(cxt, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt, mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern)); /* ignore filesystems which don't match options patterns */ if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || /* ignore filesystems which don't match type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) *ignored = 1; DBG(CXT, ul_debugobj(cxt, "next-umount: not-match")); return 0; } rc = mnt_context_set_fs(cxt, *fs); if (rc) return rc; rc = mnt_context_umount(cxt); if (mntrc) *mntrc = rc; return 0; }
/* Check, if there already exists a mounted loop device on the mountpoint node * with the same parameters. */ static int is_mounted_same_loopfile(struct libmnt_context *cxt, const char *target, const char *backing_file, uint64_t offset) { struct libmnt_table *tb; struct libmnt_iter itr; struct libmnt_fs *fs; struct libmnt_cache *cache; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!target || !backing_file || mnt_context_get_mtab(cxt, &tb)) return 0; DBG(CXT, mnt_debug_h(cxt, "checking if %s mounted on %s", backing_file, target)); cache = mnt_context_get_cache(cxt); mnt_reset_iter(&itr, MNT_ITER_BACKWARD); /* Search for mountpoint node in mtab, procceed if any of these has the * loop option set or the device is a loop device */ while (mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *src = mnt_fs_get_source(fs); const char *opts = mnt_fs_get_user_options(fs); char *val; size_t len; int res = 0; if (!src || !mnt_fs_match_target(fs, target, cache)) continue; if (strncmp(src, "/dev/loop", 9) == 0) { res = loopdev_is_used((char *) src, backing_file, offset, LOOPDEV_FL_OFFSET); } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { val = strndup(val, len); res = loopdev_is_used((char *) val, backing_file, offset, LOOPDEV_FL_OFFSET); free(val); } if (res) { DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); return 1; } } return 0; }
/** * mnt_table_next_child_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @itr: iterator * @parent: parental FS * @chld: returns the next child filesystem * * Note that filesystems are returned in the order how was mounted (according to * IDs in /proc/self/mountinfo). * * Returns: 0 on success, negative number in case of error or 1 at end of list. */ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *parent, struct libmnt_fs **chld) { struct libmnt_fs *fs; int parent_id, lastchld_id = 0, chld_id = 0; if (!tb || !itr || !parent) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup next child of '%s'", mnt_fs_get_target(parent))); parent_id = mnt_fs_get_id(parent); if (!parent_id) return -EINVAL; /* get ID of the previously returned child */ if (itr->head && itr->p != itr->head) { MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents); lastchld_id = mnt_fs_get_id(fs); } *chld = NULL; mnt_reset_iter(itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, itr, &fs) == 0) { int id; if (mnt_fs_get_parent_id(fs) != parent_id) continue; id = mnt_fs_get_id(fs); /* avoid infinite loop. This only happens in rare cases * such as in early userspace when the rootfs is its own parent */ if (id == parent_id) continue; if ((!lastchld_id || id > lastchld_id) && (!*chld || id < chld_id)) { *chld = fs; chld_id = id; } } if (!chld_id) return 1; /* end of iterator */ /* set the iterator to the @chld for the next call */ mnt_table_set_iter(tb, itr, *chld); return 0; }
/** * mnt_table_parse_mtab: * @tb: table * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL * * This function parses /etc/mtab or /proc/self/mountinfo + * /run/mount/utabs or /proc/mounts. * * See also mnt_table_set_parser_errcb(). * * Returns: 0 on success or negative number in case of error. */ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) { int rc; const char *utab = NULL; if (mnt_has_regular_mtab(&filename, NULL)) { DBG(TAB, mnt_debug_h(tb, "force %s usage", filename)); rc = mnt_table_parse_file(tb, filename); if (!rc) return 0; filename = NULL; /* failed */ } /* * useless /etc/mtab * -- read kernel information from /proc/self/mountinfo */ tb->fmt = MNT_FMT_MOUNTINFO; rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO); if (rc) { /* hmm, old kernel? ...try /proc/mounts */ tb->fmt = MNT_FMT_MTAB; return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); } /* * try to read user specific information from /run/mount/utabs */ utab = mnt_get_utab_path(); if (utab) { struct libmnt_table *u_tb = __mnt_new_table_from_file(utab, MNT_FMT_UTAB); if (u_tb) { struct libmnt_fs *u_fs; struct libmnt_iter itr; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); /* merge user options into mountinfo from kernel */ while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) mnt_table_merge_user_fs(tb, u_fs); mnt_free_table(u_tb); } } return 0; }
/* * This function uses @uf to found corresponding record in @tb, then the record * from @tb is updated (user specific mount options are added). * * Note that @uf must contain only user specific mount options instead of * VFS options (note that FS options are ignored). * * Returns modified filesystem (from @tb) or NULL. */ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf) { struct libmnt_fs *fs; struct libmnt_iter itr; const char *optstr, *src, *target, *root, *attrs; assert(tb); assert(uf); if (!tb || !uf) return NULL; DBG(TAB, mnt_debug_h(tb, "merging user fs")); src = mnt_fs_get_srcpath(uf); target = mnt_fs_get_target(uf); optstr = mnt_fs_get_user_options(uf); attrs = mnt_fs_get_attributes(uf); root = mnt_fs_get_root(uf); if (!src || !target || !root || (!attrs && !optstr)) return NULL; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *s = mnt_fs_get_srcpath(fs), *t = mnt_fs_get_target(fs), *r = mnt_fs_get_root(fs); if (fs->flags & MNT_FS_MERGED) continue; if (s && t && r && !strcmp(t, target) && !strcmp(s, src) && !strcmp(r, root)) break; } if (fs) { DBG(TAB, mnt_debug_h(tb, "found fs -- appending user optstr")); mnt_fs_append_options(fs, optstr); mnt_fs_append_attributes(fs, attrs); mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf)); fs->flags |= MNT_FS_MERGED; DBG(TAB, mnt_debug_h(tb, "found fs:")); DBG(TAB, mnt_fs_print_debug(fs, stderr)); } return fs; }
/* * This function works like mnt_resolve_tag(), but it's able to read UUiD/LABEL * from regular swap files too (according to entries in /proc/swaps). Note that * mnt_resolve_tag() and mnt_resolve_spec() works with system visible block * devices only. */ static char *swapoff_resolve_tag(const char *name, const char *value, struct libmnt_cache *cache) { char *path; struct libmnt_table *tb; struct libmnt_iter *itr; struct libmnt_fs *fs; /* this is usual case for block devices (and it's really fast as it uses * udev /dev/disk/by-* symlinks by default */ path = mnt_resolve_tag(name, value, cache); if (path) return path; /* try regular files from /proc/swaps */ tb = get_swaps(); if (!tb) return NULL; itr = mnt_new_iter(MNT_ITER_BACKWARD); if (!itr) err(EXIT_FAILURE, _("failed to initialize libmount iterator")); while (tb && mnt_table_next_fs(tb, itr, &fs) == 0) { blkid_probe pr = NULL; const char *src = mnt_fs_get_source(fs); const char *type = mnt_fs_get_swaptype(fs); const char *data = NULL; if (!src || !type || strcmp(type, "file") != 0) continue; pr = get_swap_prober(src); if (!pr) continue; blkid_probe_lookup_value(pr, name, &data, NULL); if (data && strcmp(data, value) == 0) path = xstrdup(src); blkid_free_probe(pr); if (path) break; } mnt_free_iter(itr); return path; }
static int get_debugfs(char **path) { struct libmnt_context *cxt; struct libmnt_table *tb; struct libmnt_iter *itr = NULL; struct libmnt_fs *fs; int found = 0, ret; cxt = mnt_new_context(); if (!cxt) err(EXIT_FAILURE, "libmount context allocation failed"); itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) err(EXIT_FAILURE, "failed to initialize libmount iterator"); if (mnt_context_get_mtab(cxt, &tb)) err(EXIT_FAILURE, "failed to read mtab"); while (mnt_table_next_fs(tb, itr, &fs) == 0) { const char *type = mnt_fs_get_fstype(fs); if (!strcmp(type, "debugfs")) { found = 1; break; } } if (found) { ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); if (ret < 0) err(EXIT_FAILURE, "failed to format string"); } mnt_free_iter(itr); mnt_free_context(cxt); if (!found) return -1; return 0; }
/** * mnt_resolve_target: * @path: "native" path, a potential mount point * @cache: cache for results or NULL. * * Like mnt_resolve_path(), unless @cache is not NULL and * mnt_cache_set_targets(cache, mtab) was called: if @path is found in the * cached @mtab and the matching entry was provided by the kernel, assume that * @path is already canonicalized. By avoiding a call to realpath(2) on * known mount points, there is a lower risk of stepping on a stale mount * point, which can result in an application freeze. This is also faster in * general, as stat(2) on a mount point is slower than on a regular file. * * Returns: absolute path or NULL in case of error. The result has to be * deallocated by free() if @cache is NULL. */ char *mnt_resolve_target(const char *path, struct libmnt_cache *cache) { char *p = NULL; /*DBG(CACHE, ul_debugobj(cache, "resolving target %s", path));*/ if (!cache || !cache->mtab) return mnt_resolve_path(path, cache); p = (char *) cache_find_path(cache, path); if (p) return p; else { struct libmnt_iter itr; struct libmnt_fs *fs = NULL; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); while (mnt_table_next_fs(cache->mtab, &itr, &fs) == 0) { if (!mnt_fs_is_kernel(fs) || mnt_fs_is_swaparea(fs) || !mnt_fs_streq_target(fs, path)) continue; p = strdup(path); if (!p) return NULL; /* ENOMEM */ if (cache_add_entry(cache, p, p, MNT_CACHE_ISPATH)) { free(p); return NULL; /* ENOMEM */ } break; } } if (!p) p = canonicalize_path_and_cache(path, cache); return p; }
/* Check if there is something important in the utab file. The parsed utab is * stored in context->utab and deallocated by mnt_free_context(). * * This function exists to avoid (if possible) /proc/self/mountinfo usage, so * don't use thigs like mnt_resolve_target(), mnt_context_get_mtab() etc here. * See lookup_umount_fs() for more details. */ static int has_utab_entry(struct libmnt_context *cxt, const char *target) { struct libmnt_cache *cache = NULL; struct libmnt_fs *fs; struct libmnt_iter itr; char *cn = NULL; int rc = 0; assert(cxt); if (!cxt->utab) { const char *path = mnt_get_utab_path(); if (!path || is_file_empty(path)) return 0; cxt->utab = mnt_new_table(); if (!cxt->utab) return 0; cxt->utab->fmt = MNT_FMT_UTAB; if (mnt_table_parse_file(cxt->utab, path)) return 0; } /* paths in utab are canonicalized */ cache = mnt_context_get_cache(cxt); cn = mnt_resolve_path(target, cache); mnt_reset_iter(&itr, MNT_ITER_BACKWARD); while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) { if (mnt_fs_streq_target(fs, cn)) { rc = 1; break; } } if (!cache) free(cn); return rc; }
/** * mnt_context_next_mount: * @cxt: context * @itr: iterator * @fs: returns the current filesystem * @mntrc: returns the return code from mnt_context_mount() * @ignored: returns 1 for not matching and 2 for already mounted filesystems * * This function tries to mount the next filesystem from fstab (as returned by * mnt_context_get_fstab()). See also mnt_context_set_fstab(). * * You can filter out filesystems by: * mnt_context_set_options_pattern() to simulate mount -a -O pattern * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern * * If the filesystem is already mounted or does not match defined criteria, * then the mnt_context_next_mount() function returns zero, but the @ignored is * non-zero. Note that the root filesystem and filesystems with "noauto" option * are always ignored. * * If mount(2) syscall or mount.type helper failed, then the * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero. * Use also mnt_context_get_status() to check if the filesystem was * successfully mounted. * * Returns: 0 on success, * <0 in case of error (!= mount(2) errors) * 1 at the end of the list. */ int mnt_context_next_mount(struct libmnt_context *cxt, struct libmnt_iter *itr, struct libmnt_fs **fs, int *mntrc, int *ignored) { struct libmnt_table *fstab, *mtab; const char *o, *tgt; int rc, mounted = 0; if (ignored) *ignored = 0; if (mntrc) *mntrc = 0; if (!cxt || !fs || !itr) return -EINVAL; mtab = cxt->mtab; cxt->mtab = NULL; /* do not reset mtab */ mnt_reset_context(cxt); cxt->mtab = mtab; rc = mnt_context_get_fstab(cxt, &fstab); if (rc) return rc; rc = mnt_table_next_fs(fstab, itr, fs); if (rc != 0) return rc; /* more filesystems (or error) */ o = mnt_fs_get_user_options(*fs); tgt = mnt_fs_get_target(*fs); DBG(CXT, mnt_debug_h(cxt, "next-mount: trying %s", tgt)); /* ignore swap */ if (mnt_fs_is_swaparea(*fs) || /* ignore root filesystem */ (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) || /* ignore noauto filesystems */ (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) || /* ignore filesystems not match with options patterns */ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || /* ignore filesystems not match with type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) *ignored = 1; DBG(CXT, mnt_debug_h(cxt, "next-mount: not-match " "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern)); return 0; } /* ignore already mounted filesystems */ rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted); if (rc) return rc; if (mounted) { if (ignored) *ignored = 2; return 0; } if (mnt_context_is_fork(cxt)) { rc = mnt_fork_context(cxt); if (rc) return rc; /* fork error */ if (mnt_context_is_parent(cxt)) { return 0; /* parent */ } } /* child or non-forked */ rc = mnt_context_set_fs(cxt, *fs); if (!rc) { rc = mnt_context_mount(cxt); if (mntrc) *mntrc = rc; } if (mnt_context_is_child(cxt)) { DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc)); DBG_FLUSH; exit(rc); } return 0; }
/* default filename is /proc/self/mountinfo */ int __mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename, struct libmnt_table *u_tb) { int rc = 0, priv_utab = 0; assert(tb); if (filename) DBG(TAB, ul_debugobj(tb, "%s reuested as mtab", filename)); #ifdef USE_LIBMOUNT_SUPPORT_MTAB if (mnt_has_regular_mtab(&filename, NULL)) { DBG(TAB, ul_debugobj(tb, "force mtab usage [filename=%s]", filename)); rc = mnt_table_parse_file(tb, filename); /* * If @filename forces us to read from /proc then also read * utab file to merge userspace mount options. */ if (rc == 0 && is_mountinfo(tb)) goto read_utab; if (!rc) return 0; filename = NULL; /* failed */ } else filename = NULL; /* mtab useless */ #endif if (!filename || strcmp(filename, _PATH_PROC_MOUNTINFO) == 0) { filename = _PATH_PROC_MOUNTINFO; tb->fmt = MNT_FMT_MOUNTINFO; DBG(TAB, ul_debugobj(tb, "mtab parse: #1 read mountinfo")); } else tb->fmt = MNT_FMT_GUESS; rc = mnt_table_parse_file(tb, filename); if (rc) { /* hmm, old kernel? ...try /proc/mounts */ tb->fmt = MNT_FMT_MTAB; return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); } if (!is_mountinfo(tb)) return 0; #ifdef USE_LIBMOUNT_SUPPORT_MTAB read_utab: #endif DBG(TAB, ul_debugobj(tb, "mtab parse: #2 read utab")); if (mnt_table_get_nents(tb) == 0) return 0; /* empty, ignore utab */ /* * try to read the user specific information from /run/mount/utabs */ if (!u_tb) { const char *utab = mnt_get_utab_path(); if (!utab || is_file_empty(utab)) return 0; u_tb = mnt_new_table(); if (!u_tb) return -ENOMEM; u_tb->fmt = MNT_FMT_UTAB; mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data); rc = mnt_table_parse_file(u_tb, utab); priv_utab = 1; } DBG(TAB, ul_debugobj(tb, "mtab parse: #3 merge utab")); if (rc == 0) { struct libmnt_fs *u_fs; struct libmnt_iter itr; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); /* merge user options into mountinfo from the kernel */ while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) mnt_table_merge_user_fs(tb, u_fs); } if (priv_utab) mnt_unref_table(u_tb); return 0; }