/** * 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_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 of mounting (according to * IDs in /proc/self/mountinfo). * * Returns: 0 on success, negative number in case of error or 1 at the 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 || !is_mountinfo(tb)) return -EINVAL; DBG(TAB, ul_debugobj(tb, "lookup next child of '%s'", mnt_fs_get_target(parent))); parent_id = mnt_fs_get_id(parent); /* 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 an 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) return 1; /* end of iterator */ /* set the iterator to the @chld for the next call */ mnt_table_set_iter(tb, itr, *chld); 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; }