blob: bcc8273488c19afdeb7464b49035e41a19555fef [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* This file is part of libmount from util-linux project.
*
* Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
*
* libmount is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
/**
* SECTION: fs
* @title: Filesystem
* @short_description: represents one entry from fstab, mtab, or mountinfo file
*
*/
#include <ctype.h>
#include <blkid.h>
#include <stddef.h>
#include "mountP.h"
#include "strutils.h"
/**
* mnt_new_fs:
*
* The initial refcount is 1, and needs to be decremented to
* release the resources of the filesystem.
*
* Returns: newly allocated struct libmnt_fs.
*/
struct libmnt_fs *mnt_new_fs(void)
{
struct libmnt_fs *fs = calloc(1, sizeof(*fs));
if (!fs)
return NULL;
fs->refcount = 1;
INIT_LIST_HEAD(&fs->ents);
/*DBG(FS, ul_debugobj(fs, "alloc"));*/
return fs;
}
/**
* mnt_free_fs:
* @fs: fs pointer
*
* Deallocates the fs. This function does not care about reference count. Don't
* use this function directly -- it's better to use mnt_unref_fs().
*
* The reference counting is supported since util-linux v2.24.
*/
void mnt_free_fs(struct libmnt_fs *fs)
{
if (!fs)
return;
DBG(FS, ul_debugobj(fs, "free [refcount=%d]", fs->refcount));
mnt_reset_fs(fs);
free(fs);
}
/**
* mnt_reset_fs:
* @fs: fs pointer
*
* Resets (zeroize) @fs.
*/
void mnt_reset_fs(struct libmnt_fs *fs)
{
int ref;
if (!fs)
return;
ref = fs->refcount;
list_del(&fs->ents);
free(fs->source);
free(fs->bindsrc);
free(fs->tagname);
free(fs->tagval);
free(fs->root);
free(fs->swaptype);
free(fs->target);
free(fs->fstype);
free(fs->optstr);
free(fs->vfs_optstr);
free(fs->fs_optstr);
free(fs->user_optstr);
free(fs->attrs);
free(fs->opt_fields);
free(fs->comment);
memset(fs, 0, sizeof(*fs));
INIT_LIST_HEAD(&fs->ents);
fs->refcount = ref;
}
/**
* mnt_ref_fs:
* @fs: fs pointer
*
* Increments reference counter.
*/
void mnt_ref_fs(struct libmnt_fs *fs)
{
if (fs) {
fs->refcount++;
/*DBG(FS, ul_debugobj(fs, "ref=%d", fs->refcount));*/
}
}
/**
* mnt_unref_fs:
* @fs: fs pointer
*
* De-increments reference counter, on zero the @fs is automatically
* deallocated by mnt_free_fs().
*/
void mnt_unref_fs(struct libmnt_fs *fs)
{
if (fs) {
fs->refcount--;
/*DBG(FS, ul_debugobj(fs, "unref=%d", fs->refcount));*/
if (fs->refcount <= 0)
mnt_free_fs(fs);
}
}
static inline int update_str(char **dest, const char *src)
{
size_t sz;
char *x;
assert(dest);
if (!src) {
free(*dest);
*dest = NULL;
return 0; /* source (old) is empty */
}
sz = strlen(src) + 1;
x = realloc(*dest, sz);
if (!x)
return -ENOMEM;
*dest = x;
memcpy(*dest, src, sz);
return 0;
}
/* This function do NOT overwrite (replace) the string in @new, the string in
* the @new has to be NULL otherwise this is no-op */
static inline int cpy_str_at_offset(void *new, const void *old, size_t offset)
{
char **o = (char **) ((char *) old + offset);
char **n = (char **) ((char *) new + offset);
if (*n)
return 0; /* already set, don't overwrite */
return update_str(n, *o);
}
/**
* mnt_copy_fs:
* @dest: destination FS
* @src: source FS
*
* If @dest is NULL, then a new FS is allocated, if any @dest field is already
* set, then the field is NOT overwritten.
*
* This function does not copy userdata (se mnt_fs_set_userdata()). A new copy is
* not linked with any existing mnt_tab.
*
* Returns: @dest or NULL in case of error
*/
struct libmnt_fs *mnt_copy_fs(struct libmnt_fs *dest,
const struct libmnt_fs *src)
{
const struct libmnt_fs *org = dest;
if (!src)
return NULL;
if (!dest) {
dest = mnt_new_fs();
if (!dest)
return NULL;
dest->tab = NULL;
}
dest->id = src->id;
dest->parent = src->parent;
dest->devno = src->devno;
dest->tid = src->tid;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, source)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagname)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagval)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, root)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, swaptype)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, target)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fstype)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, optstr)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, vfs_optstr)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fs_optstr)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, user_optstr)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, attrs)))
goto err;
if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, bindsrc)))
goto err;
dest->freq = src->freq;
dest->passno = src->passno;
dest->flags = src->flags;
dest->size = src->size;
dest->usedsize = src->usedsize;
dest->priority = src->priority;
return dest;
err:
if (!org)
mnt_free_fs(dest);
return NULL;
}
/*
* This function copies all @fs description except information that does not
* belong to /etc/mtab (e.g. VFS and userspace mount options with MNT_NOMTAB
* mask).
*
* Returns: copy of @fs.
*/
struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs)
{
struct libmnt_fs *n = mnt_new_fs();
assert(fs);
if (!n)
return NULL;
if (strdup_between_structs(n, fs, source))
goto err;
if (strdup_between_structs(n, fs, target))
goto err;
if (strdup_between_structs(n, fs, fstype))
goto err;
if (fs->vfs_optstr) {
char *p = NULL;
mnt_optstr_get_options(fs->vfs_optstr, &p,
mnt_get_builtin_optmap(MNT_LINUX_MAP),
MNT_NOMTAB);
n->vfs_optstr = p;
}
if (fs->user_optstr) {
char *p = NULL;
mnt_optstr_get_options(fs->user_optstr, &p,
mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
MNT_NOMTAB);
n->user_optstr = p;
}
if (strdup_between_structs(n, fs, fs_optstr))
goto err;
/* we cannot copy original optstr, the new optstr has to be without
* non-mtab options -- so, let's generate a new string */
n->optstr = mnt_fs_strdup_options(n);
n->freq = fs->freq;
n->passno = fs->passno;
n->flags = fs->flags;
return n;
err:
mnt_free_fs(n);
return NULL;
}
/**
* mnt_fs_get_userdata:
* @fs: struct libmnt_file instance
*
* Returns: private data set by mnt_fs_set_userdata() or NULL.
*/
void *mnt_fs_get_userdata(struct libmnt_fs *fs)
{
if (!fs)
return NULL;
return fs->userdata;
}
/**
* mnt_fs_set_userdata:
* @fs: struct libmnt_file instance
* @data: user data
*
* The "userdata" are library independent data.
*
* Returns: 0 or negative number in case of error (if @fs is NULL).
*/
int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data)
{
if (!fs)
return -EINVAL;
fs->userdata = data;
return 0;
}
/**
* mnt_fs_get_srcpath:
* @fs: struct libmnt_file (fstab/mtab/mountinfo) fs
*
* The mount "source path" is:
* - a directory for 'bind' mounts (in fstab or mtab only)
* - a device name for standard mounts
*
* See also mnt_fs_get_tag() and mnt_fs_get_source().
*
* Returns: mount source path or NULL in case of error or when the path
* is not defined.
*/
const char *mnt_fs_get_srcpath(struct libmnt_fs *fs)
{
if (!fs)
return NULL;
/* fstab-like fs */
if (fs->tagname)
return NULL; /* the source contains a "NAME=value" */
return fs->source;
}
/**
* mnt_fs_get_source:
* @fs: struct libmnt_file (fstab/mtab/mountinfo) fs
*
* Returns: mount source. Note that the source could be unparsed TAG
* (LABEL/UUID). See also mnt_fs_get_srcpath() and mnt_fs_get_tag().
*/
const char *mnt_fs_get_source(struct libmnt_fs *fs)
{
return fs ? fs->source : NULL;
}
/*
* Used by the parser ONLY (@source has to be freed on error)
*/
int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
{
char *t = NULL, *v = NULL;
assert(fs);
if (source && blkid_parse_tag_string(source, &t, &v) == 0 &&
!mnt_valid_tagname(t)) {
/* parsable but unknown tag -- ignore */
free(t);
free(v);
t = v = NULL;
}
if (fs->source != source)
free(fs->source);
free(fs->tagname);
free(fs->tagval);
fs->source = source;
fs->tagname = t;
fs->tagval = v;
return 0;
}
/**
* mnt_fs_set_source:
* @fs: fstab/mtab/mountinfo entry
* @source: new source
*
* This function creates a private copy (strdup()) of @source.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_source(struct libmnt_fs *fs, const char *source)
{
char *p = NULL;
int rc;
if (!fs)
return -EINVAL;
if (source) {
p = strdup(source);
if (!p)
return -ENOMEM;
}
rc = __mnt_fs_set_source_ptr(fs, p);
if (rc)
free(p);
return rc;
}
/**
* mnt_fs_streq_srcpath:
* @fs: fs
* @path: source path
*
* Compares @fs source path with @path. The redundant slashes are ignored.
* This function compares strings and does not canonicalize the paths.
* See also more heavy and generic mnt_fs_match_source().
*
* Returns: 1 if @fs source path equal to @path, otherwise 0.
*/
int mnt_fs_streq_srcpath(struct libmnt_fs *fs, const char *path)
{
const char *p;
if (!fs)
return 0;
p = mnt_fs_get_srcpath(fs);
if (!mnt_fs_is_pseudofs(fs))
return streq_paths(p, path);
if (!p && !path)
return 1;
return p && path && strcmp(p, path) == 0;
}
/**
* mnt_fs_get_table:
* @fs: table entry
* @tb: table that contains @fs
*
* Returns: 0 or negative number on error (if @fs or @tb is NULL).
*
* Since: 2.34
*/
int mnt_fs_get_table(struct libmnt_fs *fs, struct libmnt_table **tb)
{
if (!fs || !tb)
return -EINVAL;
*tb = fs->tab;
return 0;
}
/**
* mnt_fs_streq_target:
* @fs: fs
* @path: mount point
*
* Compares @fs target path with @path. The redundant slashes are ignored.
* This function compares strings and does not canonicalize the paths.
* See also more generic mnt_fs_match_target().
*
* Returns: 1 if @fs target path equal to @path, otherwise 0.
*/
int mnt_fs_streq_target(struct libmnt_fs *fs, const char *path)
{
return fs && streq_paths(mnt_fs_get_target(fs), path);
}
/**
* mnt_fs_get_tag:
* @fs: fs
* @name: returns pointer to NAME string
* @value: returns pointer to VALUE string
*
* "TAG" is NAME=VALUE (e.g. LABEL=foo)
*
* The TAG is the first column in the fstab file. The TAG or "srcpath" always has
* to be set for all entries.
*
* See also mnt_fs_get_source().
*
* <informalexample>
* <programlisting>
* char *src;
* struct libmnt_fs *fs = mnt_table_find_target(tb, "/home", MNT_ITER_FORWARD);
*
* if (!fs)
* goto err;
*
* src = mnt_fs_get_srcpath(fs);
* if (!src) {
* char *tag, *val;
* if (mnt_fs_get_tag(fs, &tag, &val) == 0)
* printf("%s: %s\n", tag, val); // LABEL or UUID
* } else
* printf("device: %s\n", src); // device or bind path
* </programlisting>
* </informalexample>
*
* Returns: 0 on success or negative number in case a TAG is not defined.
*/
int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value)
{
if (fs == NULL || !fs->tagname)
return -EINVAL;
if (name)
*name = fs->tagname;
if (value)
*value = fs->tagval;
return 0;
}
/**
* mnt_fs_get_target:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: pointer to mountpoint path or NULL
*/
const char *mnt_fs_get_target(struct libmnt_fs *fs)
{
return fs ? fs->target : NULL;
}
/**
* mnt_fs_set_target:
* @fs: fstab/mtab/mountinfo entry
* @tgt: mountpoint
*
* This function creates a private copy (strdup()) of @tgt.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_target(struct libmnt_fs *fs, const char *tgt)
{
return strdup_to_struct_member(fs, target, tgt);
}
static int mnt_fs_get_flags(struct libmnt_fs *fs)
{
return fs ? fs->flags : 0;
}
/**
* mnt_fs_get_propagation:
* @fs: mountinfo entry
* @flags: returns propagation MS_* flags as present in the mountinfo file
*
* Note that this function sets @flags to zero if no propagation flags are found
* in the mountinfo file. The kernel default is MS_PRIVATE, this flag is not stored
* in the mountinfo file.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_get_propagation(struct libmnt_fs *fs, unsigned long *flags)
{
if (!fs || !flags)
return -EINVAL;
*flags = 0;
if (!fs->opt_fields)
return 0;
/*
* The optional fields format is incompatible with mount options
* ... we have to parse the field here.
*/
*flags |= strstr(fs->opt_fields, "shared:") ? MS_SHARED : MS_PRIVATE;
if (strstr(fs->opt_fields, "master:"))
*flags |= MS_SLAVE;
if (strstr(fs->opt_fields, "unbindable"))
*flags |= MS_UNBINDABLE;
return 0;
}
/**
* mnt_fs_is_kernel:
* @fs: filesystem
*
* Returns: 1 if the filesystem description is read from kernel e.g. /proc/mounts.
*/
int mnt_fs_is_kernel(struct libmnt_fs *fs)
{
return mnt_fs_get_flags(fs) & MNT_FS_KERNEL;
}
/**
* mnt_fs_is_swaparea:
* @fs: filesystem
*
* Returns: 1 if the filesystem uses "swap" as a type
*/
int mnt_fs_is_swaparea(struct libmnt_fs *fs)
{
return mnt_fs_get_flags(fs) & MNT_FS_SWAP;
}
/**
* mnt_fs_is_pseudofs:
* @fs: filesystem
*
* Returns: 1 if the filesystem is a pseudo fs type (proc, cgroups)
*/
int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
{
return mnt_fs_get_flags(fs) & MNT_FS_PSEUDO;
}
/**
* mnt_fs_is_netfs:
* @fs: filesystem
*
* Returns: 1 if the filesystem is a network filesystem
*/
int mnt_fs_is_netfs(struct libmnt_fs *fs)
{
return mnt_fs_get_flags(fs) & MNT_FS_NET;
}
/**
* mnt_fs_get_fstype:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: pointer to filesystem type.
*/
const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
{
return fs ? fs->fstype : NULL;
}
/* Used by the struct libmnt_file parser only */
int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype)
{
assert(fs);
if (fstype != fs->fstype)
free(fs->fstype);
fs->fstype = fstype;
fs->flags &= ~MNT_FS_PSEUDO;
fs->flags &= ~MNT_FS_NET;
fs->flags &= ~MNT_FS_SWAP;
/* save info about pseudo filesystems */
if (fs->fstype) {
if (mnt_fstype_is_pseudofs(fs->fstype))
fs->flags |= MNT_FS_PSEUDO;
else if (mnt_fstype_is_netfs(fs->fstype))
fs->flags |= MNT_FS_NET;
else if (!strcmp(fs->fstype, "swap"))
fs->flags |= MNT_FS_SWAP;
}
return 0;
}
/**
* mnt_fs_set_fstype:
* @fs: fstab/mtab/mountinfo entry
* @fstype: filesystem type
*
* This function creates a private copy (strdup()) of @fstype.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype)
{
char *p = NULL;
if (!fs)
return -EINVAL;
if (fstype) {
p = strdup(fstype);
if (!p)
return -ENOMEM;
}
return __mnt_fs_set_fstype_ptr(fs, p);
}
/*
* Merges @vfs and @fs options strings into a new string.
* This function cares about 'ro/rw' options. The 'ro' is
* always used if @vfs or @fs is read-only.
* For example:
*
* mnt_merge_optstr("rw,noexec", "ro,journal=update")
*
* returns: "ro,noexec,journal=update"
*
* mnt_merge_optstr("rw,noexec", "rw,journal=update")
*
* returns: "rw,noexec,journal=update"
*/
static char *merge_optstr(const char *vfs, const char *fs)
{
char *res, *p;
size_t sz;
int ro = 0, rw = 0;
if (!vfs && !fs)
return NULL;
if (!vfs || !fs)
return strdup(fs ? fs : vfs);
if (!strcmp(vfs, fs))
return strdup(vfs); /* e.g. "aaa" and "aaa" */
/* leave space for the leading "r[ow],", "," and the trailing zero */
sz = strlen(vfs) + strlen(fs) + 5;
res = malloc(sz);
if (!res)
return NULL;
p = res + 3; /* make a room for rw/ro flag */
snprintf(p, sz - 3, "%s,%s", vfs, fs);
/* remove 'rw' flags */
rw += !mnt_optstr_remove_option(&p, "rw"); /* from vfs */
rw += !mnt_optstr_remove_option(&p, "rw"); /* from fs */
/* remove 'ro' flags if necessary */
if (rw != 2) {
ro += !mnt_optstr_remove_option(&p, "ro");
if (ro + rw < 2)
ro += !mnt_optstr_remove_option(&p, "ro");
}
if (!strlen(p))
memcpy(res, ro ? "ro" : "rw", 3);
else
memcpy(res, ro ? "ro," : "rw,", 3);
return res;
}
/**
* mnt_fs_strdup_options:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Merges all mount options (VFS, FS and userspace) to one options string
* and returns the result. This function does not modify @fs.
*
* Returns: pointer to string (can be freed by free(3)) or NULL in case of error.
*/
char *mnt_fs_strdup_options(struct libmnt_fs *fs)
{
char *res;
if (!fs)
return NULL;
errno = 0;
if (fs->optstr)
return strdup(fs->optstr);
res = merge_optstr(fs->vfs_optstr, fs->fs_optstr);
if (!res && errno)
return NULL;
if (fs->user_optstr &&
mnt_optstr_append_option(&res, fs->user_optstr, NULL)) {
free(res);
res = NULL;
}
return res;
}
/**
* mnt_fs_get_options:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: pointer to string or NULL in case of error.
*/
const char *mnt_fs_get_options(struct libmnt_fs *fs)
{
return fs ? fs->optstr : NULL;
}
/**
* mnt_fs_get_optional_fields
* @fs: mountinfo entry pointer
*
* Returns: pointer to string with mountinfo optional fields
* or NULL in case of error.
*/
const char *mnt_fs_get_optional_fields(struct libmnt_fs *fs)
{
return fs ? fs->opt_fields : NULL;
}
/**
* mnt_fs_set_options:
* @fs: fstab/mtab/mountinfo entry pointer
* @optstr: options string
*
* Splits @optstr to VFS, FS and userspace mount options and updates relevant
* parts of @fs.
*
* Returns: 0 on success, or negative number in case of error.
*/
int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr)
{
char *v = NULL, *f = NULL, *u = NULL, *n = NULL;
if (!fs)
return -EINVAL;
if (optstr) {
int rc = mnt_split_optstr(optstr, &u, &v, &f, 0, 0);
if (rc)
return rc;
n = strdup(optstr);
if (!n) {
free(u);
free(v);
free(f);
return -ENOMEM;
}
}
free(fs->fs_optstr);
free(fs->vfs_optstr);
free(fs->user_optstr);
free(fs->optstr);
fs->fs_optstr = f;
fs->vfs_optstr = v;
fs->user_optstr = u;
fs->optstr = n;
return 0;
}
/**
* mnt_fs_append_options:
* @fs: fstab/mtab/mountinfo entry
* @optstr: mount options
*
* Parses (splits) @optstr and appends results to VFS, FS and userspace lists
* of options.
*
* If @optstr is NULL, then @fs is not modified and 0 is returned.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr)
{
char *v = NULL, *f = NULL, *u = NULL;
int rc;
if (!fs)
return -EINVAL;
if (!optstr)
return 0;
rc = mnt_split_optstr(optstr, &u, &v, &f, 0, 0);
if (rc)
return rc;
if (!rc && v)
rc = mnt_optstr_append_option(&fs->vfs_optstr, v, NULL);
if (!rc && f)
rc = mnt_optstr_append_option(&fs->fs_optstr, f, NULL);
if (!rc && u)
rc = mnt_optstr_append_option(&fs->user_optstr, u, NULL);
if (!rc)
rc = mnt_optstr_append_option(&fs->optstr, optstr, NULL);
free(v);
free(f);
free(u);
return rc;
}
/**
* mnt_fs_prepend_options:
* @fs: fstab/mtab/mountinfo entry
* @optstr: mount options
*
* Parses (splits) @optstr and prepends the results to VFS, FS and userspace lists
* of options.
*
* If @optstr is NULL, then @fs is not modified and 0 is returned.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr)
{
char *v = NULL, *f = NULL, *u = NULL;
int rc;
if (!fs)
return -EINVAL;
if (!optstr)
return 0;
rc = mnt_split_optstr(optstr, &u, &v, &f, 0, 0);
if (rc)
return rc;
if (!rc && v)
rc = mnt_optstr_prepend_option(&fs->vfs_optstr, v, NULL);
if (!rc && f)
rc = mnt_optstr_prepend_option(&fs->fs_optstr, f, NULL);
if (!rc && u)
rc = mnt_optstr_prepend_option(&fs->user_optstr, u, NULL);
if (!rc)
rc = mnt_optstr_prepend_option(&fs->optstr, optstr, NULL);
free(v);
free(f);
free(u);
return rc;
}
/*
* mnt_fs_get_fs_options:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: pointer to superblock (fs-depend) mount option string or NULL.
*/
const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
{
return fs ? fs->fs_optstr : NULL;
}
/**
* mnt_fs_get_vfs_options:
* @fs: fstab/mtab entry pointer
*
* Returns: pointer to fs-independent (VFS) mount option string or NULL.
*/
const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs)
{
return fs ? fs->vfs_optstr : NULL;
}
/**
* mnt_fs_get_user_options:
* @fs: fstab/mtab entry pointer
*
* Returns: pointer to userspace mount option string or NULL.
*/
const char *mnt_fs_get_user_options(struct libmnt_fs *fs)
{
return fs ? fs->user_optstr : NULL;
}
/**
* mnt_fs_get_attributes:
* @fs: fstab/mtab entry pointer
*
* Returns: pointer to attributes string or NULL.
*/
const char *mnt_fs_get_attributes(struct libmnt_fs *fs)
{
return fs ? fs->attrs : NULL;
}
/**
* mnt_fs_set_attributes:
* @fs: fstab/mtab/mountinfo entry
* @optstr: options string
*
* Sets mount attributes. The attributes are mount(2) and mount(8) independent
* options, these options are not sent to the kernel and are not interpreted by
* libmount. The attributes are stored in /run/mount/utab only.
*
* The attributes are managed by libmount in userspace only. It's possible
* that information stored in userspace will not be available for libmount
* after CLONE_FS unshare. Be careful, and don't use attributes if possible.
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr)
{
return strdup_to_struct_member(fs, attrs, optstr);
}
/**
* mnt_fs_append_attributes
* @fs: fstab/mtab/mountinfo entry
* @optstr: options string
*
* Appends mount attributes. (See mnt_fs_set_attributes()).
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_append_attributes(struct libmnt_fs *fs, const char *optstr)
{
if (!fs)
return -EINVAL;
if (!optstr)
return 0;
return mnt_optstr_append_option(&fs->attrs, optstr, NULL);
}
/**
* mnt_fs_prepend_attributes
* @fs: fstab/mtab/mountinfo entry
* @optstr: options string
*
* Prepends mount attributes. (See mnt_fs_set_attributes()).
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_prepend_attributes(struct libmnt_fs *fs, const char *optstr)
{
if (!fs)
return -EINVAL;
if (!optstr)
return 0;
return mnt_optstr_prepend_option(&fs->attrs, optstr, NULL);
}
/**
* mnt_fs_get_freq:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: dump frequency in days.
*/
int mnt_fs_get_freq(struct libmnt_fs *fs)
{
return fs ? fs->freq : 0;
}
/**
* mnt_fs_set_freq:
* @fs: fstab/mtab entry pointer
* @freq: dump frequency in days
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_freq(struct libmnt_fs *fs, int freq)
{
if (!fs)
return -EINVAL;
fs->freq = freq;
return 0;
}
/**
* mnt_fs_get_passno:
* @fs: fstab/mtab entry pointer
*
* Returns: "pass number on parallel fsck".
*/
int mnt_fs_get_passno(struct libmnt_fs *fs)
{
return fs ? fs->passno: 0;
}
/**
* mnt_fs_set_passno:
* @fs: fstab/mtab entry pointer
* @passno: pass number
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_passno(struct libmnt_fs *fs, int passno)
{
if (!fs)
return -EINVAL;
fs->passno = passno;
return 0;
}
/**
* mnt_fs_get_root:
* @fs: /proc/self/mountinfo entry
*
* Returns: root of the mount within the filesystem or NULL
*/
const char *mnt_fs_get_root(struct libmnt_fs *fs)
{
return fs ? fs->root : NULL;
}
/**
* mnt_fs_set_root:
* @fs: mountinfo entry
* @path: root path
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_root(struct libmnt_fs *fs, const char *path)
{
return strdup_to_struct_member(fs, root, path);
}
/**
* mnt_fs_get_swaptype:
* @fs: /proc/swaps entry
*
* Returns: swap type or NULL
*/
const char *mnt_fs_get_swaptype(struct libmnt_fs *fs)
{
return fs ? fs->swaptype : NULL;
}
/**
* mnt_fs_get_size:
* @fs: /proc/swaps entry
*
* Returns: size
*/
off_t mnt_fs_get_size(struct libmnt_fs *fs)
{
return fs ? fs->size : 0;
}
/**
* mnt_fs_get_usedsize:
* @fs: /proc/swaps entry
*
* Returns: used size
*/
off_t mnt_fs_get_usedsize(struct libmnt_fs *fs)
{
return fs ? fs->usedsize : 0;
}
/**
* mnt_fs_get_priority:
* @fs: /proc/swaps entry
*
* Returns: priority
*/
int mnt_fs_get_priority(struct libmnt_fs *fs)
{
return fs ? fs->priority : 0;
}
/**
* mnt_fs_set_priority:
* @fs: /proc/swaps entry
* @prio: priority
*
* Since: 2.28
*
* Returns: 0 or -1 in case of error
*/
int mnt_fs_set_priority(struct libmnt_fs *fs, int prio)
{
if (!fs)
return -EINVAL;
fs->priority = prio;
return 0;
}
/**
* mnt_fs_get_bindsrc:
* @fs: /run/mount/utab entry
*
* Returns: full path that was used for mount(2) on MS_BIND
*/
const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs)
{
return fs ? fs->bindsrc : NULL;
}
/**
* mnt_fs_set_bindsrc:
* @fs: filesystem
* @src: path
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src)
{
return strdup_to_struct_member(fs, bindsrc, src);
}
/**
* mnt_fs_get_id:
* @fs: /proc/self/mountinfo entry
*
* Returns: mount ID (unique identifier of the mount) or negative number in case of error.
*/
int mnt_fs_get_id(struct libmnt_fs *fs)
{
return fs ? fs->id : -EINVAL;
}
/**
* mnt_fs_get_parent_id:
* @fs: /proc/self/mountinfo entry
*
* Returns: parent mount ID or negative number in case of error.
*/
int mnt_fs_get_parent_id(struct libmnt_fs *fs)
{
return fs ? fs->parent : -EINVAL;
}
/**
* mnt_fs_get_devno:
* @fs: /proc/self/mountinfo entry
*
* Returns: value of st_dev for files on filesystem or 0 in case of error.
*/
dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
{
return fs ? fs->devno : 0;
}
/**
* mnt_fs_get_tid:
* @fs: /proc/tid/mountinfo entry
*
* Returns: TID (task ID) for filesystems read from the mountinfo file
*/
pid_t mnt_fs_get_tid(struct libmnt_fs *fs)
{
return fs ? fs->tid : 0;
}
/**
* mnt_fs_get_option:
* @fs: fstab/mtab/mountinfo entry pointer
* @name: option name
* @value: returns pointer to the beginning of the value (e.g. name=VALUE) or NULL
* @valsz: returns size of options value or 0
*
* Returns: 0 on success, 1 when @name not found or negative number in case of error.
*/
int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
char **value, size_t *valsz)
{
char rc = 1;
if (!fs)
return -EINVAL;
if (fs->fs_optstr)
rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
if (rc == 1 && fs->vfs_optstr)
rc = mnt_optstr_get_option(fs->vfs_optstr, name, value, valsz);
if (rc == 1 && fs->user_optstr)
rc = mnt_optstr_get_option(fs->user_optstr, name, value, valsz);
return rc;
}
/**
* mnt_fs_get_attribute:
* @fs: fstab/mtab/mountinfo entry pointer
* @name: option name
* @value: returns pointer to the beginning of the value (e.g. name=VALUE) or NULL
* @valsz: returns size of options value or 0
*
* Returns: 0 on success, 1 when @name not found or negative number in case of error.
*/
int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name,
char **value, size_t *valsz)
{
char rc = 1;
if (!fs)
return -EINVAL;
if (fs->attrs)
rc = mnt_optstr_get_option(fs->attrs, name, value, valsz);
return rc;
}
/**
* mnt_fs_get_comment:
* @fs: fstab/mtab/mountinfo entry pointer
*
* Returns: 0 on success, 1 when not found the @name or negative number in case of error.
*/
const char *mnt_fs_get_comment(struct libmnt_fs *fs)
{
if (!fs)
return NULL;
return fs->comment;
}
/**
* mnt_fs_set_comment:
* @fs: fstab entry pointer
* @comm: comment string
*
* Note that the comment has to be terminated by '\n' (new line), otherwise
* the whole filesystem entry will be written as a comment to the tabfile (e.g.
* fstab).
*
* Returns: 0 on success or <0 in case of error.
*/
int mnt_fs_set_comment(struct libmnt_fs *fs, const char *comm)
{
return strdup_to_struct_member(fs, comment, comm);
}
/**
* mnt_fs_append_comment:
* @fs: fstab entry pointer
* @comm: comment string
*
* See also mnt_fs_set_comment().
*
* Returns: 0 on success or <0 in case of error.
*/
int mnt_fs_append_comment(struct libmnt_fs *fs, const char *comm)
{
if (!fs)
return -EINVAL;
return append_string(&fs->comment, comm);
}
/**
* mnt_fs_match_target:
* @fs: filesystem
* @target: mountpoint path
* @cache: tags/paths cache or NULL
*
* Possible are three attempts:
* 1) compare @target with @fs->target
*
* 2) realpath(@target) with @fs->target
*
* 3) realpath(@target) with realpath(@fs->target) if @fs is not from
* /proc/self/mountinfo.
*
* However, if mnt_cache_set_targets(cache, mtab) was called, and the
* path @target or @fs->target is found in the @mtab, the canonicalization is
* is not performed (see mnt_resolve_target()).
*
* The 2nd and 3rd attempts are not performed when @cache is NULL.
*
* Returns: 1 if @fs target is equal to @target, else 0.
*/
int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
struct libmnt_cache *cache)
{
int rc = 0;
if (!fs || !target || !fs->target)
return 0;
/* 1) native paths */
rc = mnt_fs_streq_target(fs, target);
if (!rc && cache) {
/* 2) - canonicalized and non-canonicalized */
char *cn = mnt_resolve_target(target, cache);
rc = (cn && mnt_fs_streq_target(fs, cn));
/* 3) - canonicalized and canonicalized */
if (!rc && cn && !mnt_fs_is_kernel(fs) && !mnt_fs_is_swaparea(fs)) {
char *tcn = mnt_resolve_target(fs->target, cache);
rc = (tcn && strcmp(cn, tcn) == 0);
}
}
return rc;
}
/**
* mnt_fs_match_source:
* @fs: filesystem
* @source: tag or path (device or so) or NULL
* @cache: tags/paths cache or NULL
*
* Four attempts are possible:
* 1) compare @source with @fs->source
* 2) compare realpath(@source) with @fs->source
* 3) compare realpath(@source) with realpath(@fs->source)
* 4) compare realpath(@source) with evaluated tag from @fs->source
*
* The 2nd, 3rd and 4th attempts are not performed when @cache is NULL. The
* 2nd and 3rd attempts are not performed if @fs->source is tag.
*
* Returns: 1 if @fs source is equal to @source, else 0.
*/
int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
struct libmnt_cache *cache)
{
char *cn;
const char *src, *t, *v;
if (!fs)
return 0;
/* 1) native paths... */
if (mnt_fs_streq_srcpath(fs, source) == 1)
return 1;
if (!source || !fs->source)
return 0;
/* ... and tags */
if (fs->tagname && strcmp(source, fs->source) == 0)
return 1;
if (!cache)
return 0;
if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO))
return 0;
cn = mnt_resolve_spec(source, cache);
if (!cn)
return 0;
/* 2) canonicalized and native */
src = mnt_fs_get_srcpath(fs);
if (src && mnt_fs_streq_srcpath(fs, cn))
return 1;
/* 3) canonicalized and canonicalized */
if (src) {
src = mnt_resolve_path(src, cache);
if (src && !strcmp(cn, src))
return 1;
}
if (src || mnt_fs_get_tag(fs, &t, &v))
/* src path does not match and the tag is not defined */
return 0;
/* read @source's tags to the cache */
if (mnt_cache_read_tags(cache, cn) < 0) {
if (errno == EACCES) {
/* we don't have permissions to read TAGs from
* @source, but can translate the @fs tag to devname.
*
* (because libblkid uses udev symlinks and this is
* accessible for non-root uses)
*/
char *x = mnt_resolve_tag(t, v, cache);
if (x && !strcmp(x, cn))
return 1;
}
return 0;
}
/* 4) has the @source a tag that matches with the tag from @fs ? */
if (mnt_cache_device_has_tag(cache, cn, t, v))
return 1;
return 0;
}
/**
* mnt_fs_match_fstype:
* @fs: filesystem
* @types: filesystem name or comma delimited list of filesystems
*
* For more details see mnt_match_fstype().
*
* Returns: 1 if @fs type is matching to @types, else 0. The function returns
* 0 when types is NULL.
*/
int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types)
{
return mnt_match_fstype(fs->fstype, types);
}
/**
* mnt_fs_match_options:
* @fs: filesystem
* @options: comma delimited list of options (and nooptions)
*
* For more details see mnt_match_options().
*
* Returns: 1 if @fs type is matching to @options, else 0. The function returns
* 0 when types is NULL.
*/
int mnt_fs_match_options(struct libmnt_fs *fs, const char *options)
{
return mnt_match_options(mnt_fs_get_options(fs), options);
}
/**
* mnt_fs_print_debug
* @fs: fstab/mtab/mountinfo entry
* @file: file stream
*
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
{
if (!fs || !file)
return -EINVAL;
fprintf(file, "------ fs:\n");
fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
if (mnt_fs_get_options(fs))
fprintf(file, "optstr: %s\n", mnt_fs_get_options(fs));
if (mnt_fs_get_vfs_options(fs))
fprintf(file, "VFS-optstr: %s\n", mnt_fs_get_vfs_options(fs));
if (mnt_fs_get_fs_options(fs))
fprintf(file, "FS-opstr: %s\n", mnt_fs_get_fs_options(fs));
if (mnt_fs_get_user_options(fs))
fprintf(file, "user-optstr: %s\n", mnt_fs_get_user_options(fs));
if (mnt_fs_get_optional_fields(fs))
fprintf(file, "optional-fields: '%s'\n", mnt_fs_get_optional_fields(fs));
if (mnt_fs_get_attributes(fs))
fprintf(file, "attributes: %s\n", mnt_fs_get_attributes(fs));
if (mnt_fs_get_root(fs))
fprintf(file, "root: %s\n", mnt_fs_get_root(fs));
if (mnt_fs_get_swaptype(fs))
fprintf(file, "swaptype: %s\n", mnt_fs_get_swaptype(fs));
if (mnt_fs_get_size(fs))
fprintf(file, "size: %jd\n", mnt_fs_get_size(fs));
if (mnt_fs_get_usedsize(fs))
fprintf(file, "usedsize: %jd\n", mnt_fs_get_usedsize(fs));
if (mnt_fs_get_priority(fs))
fprintf(file, "priority: %d\n", mnt_fs_get_priority(fs));
if (mnt_fs_get_bindsrc(fs))
fprintf(file, "bindsrc: %s\n", mnt_fs_get_bindsrc(fs));
if (mnt_fs_get_freq(fs))
fprintf(file, "freq: %d\n", mnt_fs_get_freq(fs));
if (mnt_fs_get_passno(fs))
fprintf(file, "pass: %d\n", mnt_fs_get_passno(fs));
if (mnt_fs_get_id(fs))
fprintf(file, "id: %d\n", mnt_fs_get_id(fs));
if (mnt_fs_get_parent_id(fs))
fprintf(file, "parent: %d\n", mnt_fs_get_parent_id(fs));
if (mnt_fs_get_devno(fs))
fprintf(file, "devno: %d:%d\n", major(mnt_fs_get_devno(fs)),
minor(mnt_fs_get_devno(fs)));
if (mnt_fs_get_tid(fs))
fprintf(file, "tid: %d\n", mnt_fs_get_tid(fs));
if (mnt_fs_get_comment(fs))
fprintf(file, "comment: '%s'\n", mnt_fs_get_comment(fs));
return 0;
}
/**
* mnt_free_mntent:
* @mnt: mount entry
*
* Deallocates the "mntent.h" mount entry.
*/
void mnt_free_mntent(struct mntent *mnt)
{
if (mnt) {
free(mnt->mnt_fsname);
free(mnt->mnt_dir);
free(mnt->mnt_type);
free(mnt->mnt_opts);
free(mnt);
}
}
/**
* mnt_fs_to_mntent:
* @fs: filesystem
* @mnt: mount description (as described in mntent.h)
*
* Copies the information from @fs to struct mntent @mnt. If @mnt is already set,
* then the struct mntent items are reallocated and updated. See also
* mnt_free_mntent().
*
* Returns: 0 on success and a negative number in case of error.
*/
int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt)
{
int rc;
struct mntent *m;
if (!fs || !mnt)
return -EINVAL;
m = *mnt;
if (!m) {
m = calloc(1, sizeof(*m));
if (!m)
return -ENOMEM;
}
if ((rc = update_str(&m->mnt_fsname, mnt_fs_get_source(fs))))
goto err;
if ((rc = update_str(&m->mnt_dir, mnt_fs_get_target(fs))))
goto err;
if ((rc = update_str(&m->mnt_type, mnt_fs_get_fstype(fs))))
goto err;
errno = 0;
m->mnt_opts = mnt_fs_strdup_options(fs);
if (!m->mnt_opts && errno) {
rc = -errno;
goto err;
}
m->mnt_freq = mnt_fs_get_freq(fs);
m->mnt_passno = mnt_fs_get_passno(fs);
if (!m->mnt_fsname) {
m->mnt_fsname = strdup("none");
if (!m->mnt_fsname)
goto err;
}
*mnt = m;
return 0;
err:
if (m != *mnt)
mnt_free_mntent(m);
return rc;
}