Compare commits
44 commits
master
...
topic/fsin
Author | SHA1 | Date | |
---|---|---|---|
|
117a4baffd | ||
|
7003b502b6 | ||
|
e523affd6f | ||
|
c791058243 | ||
|
bf715d42af | ||
|
7baaf3c251 | ||
|
522d134700 | ||
|
5a9a9ab7ad | ||
|
f3d523724b | ||
|
dbf6243597 | ||
|
386d42fa5d | ||
|
e1f7681834 | ||
|
98fd10f63f | ||
|
cb201c9c8b | ||
|
8386d586b4 | ||
|
b372c1b637 | ||
|
c0cffb7c0f | ||
|
d15875e29c | ||
|
783d41e43d | ||
|
25f56a9455 | ||
|
abd584bee0 | ||
|
4b5ec23c5a | ||
|
b8ed57bdf3 | ||
|
1728c6238d | ||
|
1cdddcfa32 | ||
|
464ee2c8fe | ||
|
aa312e00ae | ||
|
a31dcde3f9 | ||
|
0f728eee37 | ||
|
446c847415 | ||
|
7ecabf3a9c | ||
|
bb881ebf61 | ||
|
3fd6302906 | ||
|
a41fc98ad0 | ||
|
e6a848597c | ||
|
3708880a48 | ||
|
de1aa60bdb | ||
|
27c6c71003 | ||
|
5fe931f872 | ||
|
f6e42e342d | ||
|
3ee27dc5b3 | ||
|
01aede7316 | ||
|
2bf3ef7666 | ||
|
8aa73754e0 |
19 changed files with 1759 additions and 145 deletions
16
configure.ac
16
configure.ac
|
@ -246,12 +246,14 @@ AC_CHECK_HEADERS([ \
|
|||
linux/falloc.h \
|
||||
linux/watchdog.h \
|
||||
linux/fd.h \
|
||||
linux/fsinfo.h \
|
||||
linux/raw.h \
|
||||
linux/tiocl.h \
|
||||
linux/version.h \
|
||||
linux/securebits.h \
|
||||
linux/net_namespace.h \
|
||||
linux/capability.h \
|
||||
linux/watch_queue.h \
|
||||
locale.h \
|
||||
mntent.h \
|
||||
net/if.h \
|
||||
|
@ -389,6 +391,8 @@ dnl
|
|||
have_linux_version_h=$ac_cv_header_linux_version_h
|
||||
have_linux_blkzoned_h=$ac_cv_header_linux_blkzoned_h
|
||||
have_linux_btrfs_h=$ac_cv_header_linux_btrfs_h
|
||||
have_linux_fsinfo_h=$ac_cv_header_linux_fsinfo_h
|
||||
have_linux_watch_queue_h=$ac_cv_header_linux_watch_queue_h
|
||||
have_linux_raw_h=$ac_cv_header_linux_raw_h
|
||||
have_linux_securebits_h=$ac_cv_header_linux_securebits_h
|
||||
have_linux_capability_h=$ac_cv_header_linux_capability_h
|
||||
|
@ -479,6 +483,7 @@ AC_CHECK_FUNCS([ \
|
|||
explicit_bzero \
|
||||
fmemopen \
|
||||
fsync \
|
||||
fsinfo \
|
||||
utimensat \
|
||||
getdomainname \
|
||||
getdtablesize \
|
||||
|
@ -521,6 +526,7 @@ AC_CHECK_FUNCS([ \
|
|||
vwarnx \
|
||||
warn \
|
||||
warnx \
|
||||
watch_mount \
|
||||
])
|
||||
AC_FUNC_FSEEKO
|
||||
|
||||
|
@ -1131,6 +1137,16 @@ AS_IF([test "x$build_libmount" = xyes], [
|
|||
],[
|
||||
AC_MSG_WARN([libmount will be compiled without namespaces support])
|
||||
])
|
||||
AS_IF([test "x$have_linux_fsinfo_h" = "xyes"], [
|
||||
AC_DEFINE([USE_LIBMOUNT_SUPPORT_FSINFO], [1], [Define to 1 if want to support fsinfo.])
|
||||
],[
|
||||
AC_MSG_WARN([libmount will be compiled without fsinfo support])
|
||||
])
|
||||
AS_IF([test "x$have_linux_watch_queue_h" = "xyes"], [
|
||||
AC_DEFINE([USE_LIBMOUNT_SUPPORT_WATCHQUEUE], [1], [Define to 1 if want to support watch-queue.])
|
||||
],[
|
||||
AC_MSG_WARN([libmount will be compiled without watch-queue support])
|
||||
])
|
||||
])
|
||||
|
||||
AC_SUBST([LIBMOUNT_VERSION])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
if BUILD_LIBMOUNT
|
||||
|
||||
include libmount/src/Makemodule.am
|
||||
include libmount/samples/Makemodule.am
|
||||
include libmount/python/Makemodule.am
|
||||
|
||||
if ENABLE_GTK_DOC
|
||||
|
|
|
@ -261,6 +261,7 @@ mnt_fs_set_bindsrc
|
|||
mnt_fs_set_comment
|
||||
mnt_fs_set_freq
|
||||
mnt_fs_set_fstype
|
||||
mnt_fs_set_id
|
||||
mnt_fs_set_options
|
||||
mnt_fs_set_passno
|
||||
mnt_fs_set_priority
|
||||
|
|
11
libmount/samples/Makemodule.am
Normal file
11
libmount/samples/Makemodule.am
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
check_PROGRAMS += \
|
||||
sample-mount-watchqueue
|
||||
|
||||
sample_mount_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \
|
||||
-I$(ul_libmount_incdir)
|
||||
sample_mount_ldadd = $(LDADD) libmount.la
|
||||
|
||||
sample_mount_watchqueue_SOURCES = libmount/samples/watchqueue.c
|
||||
sample_mount_watchqueue_LDADD = $(sample_mount_ldadd) libcommon.la
|
||||
sample_mount_watchqueue_CFLAGS = $(sample_mount_cflags)
|
69
libmount/samples/watchqueue.c
Normal file
69
libmount/samples/watchqueue.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <libmount.h>
|
||||
|
||||
int main(void) {
|
||||
struct libmnt_monitor *mn;
|
||||
struct libmnt_fs *fs, *aux;
|
||||
|
||||
mnt_init_debug(0);
|
||||
|
||||
fs = mnt_new_fs();
|
||||
aux = mnt_new_fs();
|
||||
mn = mnt_new_monitor();
|
||||
if (!fs || ! mn || !aux)
|
||||
goto err;
|
||||
|
||||
if (mnt_monitor_enable_kernelwatch(mn, 1))
|
||||
goto err;
|
||||
|
||||
do {
|
||||
printf("waiting for changes...\n");
|
||||
if (mnt_monitor_wait(mn, -1) < 0)
|
||||
break;
|
||||
|
||||
printf(" notification detected\n");
|
||||
while (mnt_monitor_next_change(mn, NULL, NULL) == 0) {
|
||||
void *data;
|
||||
ssize_t sz;
|
||||
|
||||
data = mnt_monitor_event_data(mn, MNT_MONITOR_TYPE_KERNELWATCH, &sz);
|
||||
do {
|
||||
int child;
|
||||
|
||||
if (!mnt_kernelwatch_is_valid(data, sz) ||
|
||||
!mnt_kernelwatch_is_mount(data))
|
||||
break;
|
||||
|
||||
mnt_reset_fs(fs);
|
||||
mnt_fs_set_id(fs, mnt_kernelwatch_get_mount_id(data));
|
||||
mnt_fs_enable_fsinfo(fs, 1);
|
||||
|
||||
child = mnt_kernelwatch_get_aux_id(data);
|
||||
if (child) {
|
||||
mnt_reset_fs(aux);
|
||||
mnt_fs_set_id(aux, child);
|
||||
mnt_fs_enable_fsinfo(aux, 1);
|
||||
|
||||
printf(" fs [id=%d]: %s [modified child %3d: %s]\n",
|
||||
mnt_fs_get_id(fs), mnt_fs_get_target(fs),
|
||||
mnt_fs_get_id(aux), mnt_fs_get_target(aux));
|
||||
} else
|
||||
printf(" fs [id=%d]: %s\n",
|
||||
mnt_fs_get_id(fs), mnt_fs_get_target(fs));
|
||||
|
||||
|
||||
data = mnt_kernelwatch_next_data(data, &sz);
|
||||
} while (data);
|
||||
fflush(stdout);
|
||||
}
|
||||
} while (1);
|
||||
|
||||
mnt_unref_fs(fs);
|
||||
mnt_unref_monitor(mn);
|
||||
return 0;
|
||||
err:
|
||||
err(EXIT_FAILURE, "initialization failed");
|
||||
}
|
|
@ -11,8 +11,10 @@ libmount_la_SOURCES = \
|
|||
libmount/src/mountP.h \
|
||||
libmount/src/cache.c \
|
||||
libmount/src/fs.c \
|
||||
libmount/src/fsinfo.c \
|
||||
libmount/src/init.c \
|
||||
libmount/src/iter.c \
|
||||
libmount/src/kernelwatch.c \
|
||||
libmount/src/lock.c \
|
||||
libmount/src/optmap.c \
|
||||
libmount/src/optstr.c \
|
||||
|
|
|
@ -217,8 +217,8 @@ static int has_utab_entry(struct libmnt_context *cxt, const char *target)
|
|||
|
||||
assert(cxt);
|
||||
|
||||
if (!cxt->utab) {
|
||||
const char *path = mnt_get_utab_path();
|
||||
if (!cxt->utab && mnt_context_utab_writable(cxt)) {
|
||||
const char *path = cxt->utab_path;
|
||||
|
||||
if (!path || is_file_empty(path))
|
||||
return 0;
|
||||
|
@ -334,6 +334,49 @@ static int lookup_umount_fs_by_mountinfo(struct libmnt_context *cxt, const char
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* returns: 1 not found; <0 on error; 1 success */
|
||||
static int lookup_umount_fs_by_fsinfo(struct libmnt_context *cxt)
|
||||
{
|
||||
struct libmnt_fs *fs;
|
||||
|
||||
DBG(CXT, ul_debugobj(cxt, " lookup by fsinfo"));
|
||||
|
||||
assert(cxt);
|
||||
fs = cxt->fs;
|
||||
|
||||
assert(fs);
|
||||
assert(mnt_fs_get_target(fs));
|
||||
|
||||
/*
|
||||
* It's better to try to fetch someting we really need (e.g. fstype)
|
||||
* than call mnt_has_fsinfo(). If fsinfo() syscall is not supported
|
||||
* than library automatically calls mnt_fs_enable_fsinfo(false) and
|
||||
* the next mnt_fs_has_fsinfo() returns 0.
|
||||
*/
|
||||
mnt_fs_enable_fsinfo(fs, 1);
|
||||
mnt_fs_get_fstype(fs);
|
||||
if (!mnt_fs_has_fsinfo(fs))
|
||||
return 1;
|
||||
|
||||
/* merge utab to fs */
|
||||
if (has_utab_entry(cxt, mnt_fs_get_target(fs))) {
|
||||
struct libmnt_fs *uf;
|
||||
|
||||
uf = mnt_table_find_triplet(cxt->utab,
|
||||
mnt_fs_get_srcpath(fs),
|
||||
mnt_fs_get_target(fs),
|
||||
mnt_fs_get_root(fs),
|
||||
MNT_ITER_BACKWARD);
|
||||
if (uf) {
|
||||
mnt_fs_merge_utab(fs, uf);
|
||||
DBG(CXT, ul_debugobj(cxt, " utab applied by fsinfo"));
|
||||
}
|
||||
}
|
||||
|
||||
cxt->flags |= MNT_FL_TAB_APPLIED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This finction search for FS according to cxt->fs->target,
|
||||
* apply result to cxt->fs and it's umount replacement to
|
||||
* mnt_context_apply_fstab(), use mnt_context_tab_applied()
|
||||
|
@ -358,6 +401,11 @@ static int lookup_umount_fs(struct libmnt_context *cxt)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* try get fs by fsinfo() */
|
||||
rc = lookup_umount_fs_by_fsinfo(cxt);
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
|
||||
/* try get fs type by statfs() */
|
||||
rc = lookup_umount_fs_by_statfs(cxt, tgt);
|
||||
if (rc <= 0)
|
||||
|
|
|
@ -19,10 +19,120 @@
|
|||
#include <ctype.h>
|
||||
#include <blkid.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
#include "mountP.h"
|
||||
#include "strutils.h"
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
/*
|
||||
* Map libmnt_fs struct members to fsinfo() FSINFO_ATTR_* requests.
|
||||
*/
|
||||
struct fs_member_fsinfo {
|
||||
size_t member;
|
||||
unsigned int request;
|
||||
};
|
||||
|
||||
static const struct fs_member_fsinfo fsinfo_map[] =
|
||||
{
|
||||
{ offsetof(struct libmnt_fs, devno), FSINFO_ATTR_IDS },
|
||||
{ offsetof(struct libmnt_fs, fs_optstr),FSINFO_ATTR_CONFIGURATION },
|
||||
{ offsetof(struct libmnt_fs, fstype), FSINFO_ATTR_IDS },
|
||||
{ offsetof(struct libmnt_fs, id), FSINFO_ATTR_MOUNT_INFO },
|
||||
{ offsetof(struct libmnt_fs, parent), FSINFO_ATTR_MOUNT_TOPOLOGY },
|
||||
{ offsetof(struct libmnt_fs, propagation), FSINFO_ATTR_MOUNT_TOPOLOGY },
|
||||
{ offsetof(struct libmnt_fs, root), FSINFO_ATTR_MOUNT_PATH },
|
||||
{ offsetof(struct libmnt_fs, source), FSINFO_ATTR_SOURCE },
|
||||
{ offsetof(struct libmnt_fs, target), FSINFO_ATTR_MOUNT_POINT_FULL },
|
||||
{ offsetof(struct libmnt_fs, vfs_optstr), FSINFO_ATTR_MOUNT_INFO }
|
||||
};
|
||||
|
||||
/* Unfortunatel, we cannot use FSINFO_ATTR_* in fs->fsinfo_done mask. Let's
|
||||
* define our own magical constants.
|
||||
*/
|
||||
static inline unsigned int fsinfo_req2mask(unsigned int req)
|
||||
{
|
||||
switch (req) {
|
||||
case FSINFO_ATTR_IDS:
|
||||
return (1 << 0);
|
||||
case FSINFO_ATTR_CONFIGURATION:
|
||||
return (1 << 1);
|
||||
case FSINFO_ATTR_MOUNT_INFO:
|
||||
return (1 << 2);
|
||||
case FSINFO_ATTR_MOUNT_TOPOLOGY:
|
||||
return (1 << 3);
|
||||
case FSINFO_ATTR_MOUNT_PATH:
|
||||
return (1 << 4);
|
||||
case FSINFO_ATTR_SOURCE:
|
||||
return (1 << 5);
|
||||
case FSINFO_ATTR_MOUNT_POINT_FULL:
|
||||
return (1 << 6);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* converts libmnt_fs struct member offset to FSINFO_ATTR_ request ID */
|
||||
static unsigned int fsinfo_off2req(size_t member_offset)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsinfo_map); i++)
|
||||
if (fsinfo_map[i].member == member_offset)
|
||||
return fsinfo_map[i].request;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define mnt_fs_fetch_member_fsinfo(_fs, _member) __extension__ ({ \
|
||||
const int __r = fsinfo_off2req(offsetof(__typeof__(*(_fs)), _member)); \
|
||||
if (__r >= 0) \
|
||||
DBG(FS, ul_debugobj(_fs, "fsinfo for '%s'", #_member)); \
|
||||
__r >= 0 ? mnt_fs_fetch_fsinfo(_fs, __r) : __r; \
|
||||
})
|
||||
|
||||
struct mount_attr_map_entry {
|
||||
unsigned int attr;
|
||||
unsigned int ms_opt;
|
||||
};
|
||||
|
||||
static const struct mount_attr_map_entry mount_attr_map[] =
|
||||
{
|
||||
{MOUNT_ATTR_RDONLY, MS_RDONLY},
|
||||
{MOUNT_ATTR_NOSUID, MS_NOSUID},
|
||||
{MOUNT_ATTR_NODEV, MS_NODEV},
|
||||
{MOUNT_ATTR_NOEXEC, MS_NOEXEC},
|
||||
{MOUNT_ATTR_NOATIME, MS_NOATIME},
|
||||
{MOUNT_ATTR_STRICTATIME, MS_STRICTATIME},
|
||||
{MOUNT_ATTR_NODIRATIME, MS_NODIRATIME},
|
||||
};
|
||||
|
||||
/* convert fsinfo MOUNT_ATTR_ to MS_ request ID */
|
||||
static unsigned int fsinfo_mount_attr2ms(unsigned int attr)
|
||||
{
|
||||
unsigned int ms_opts = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mount_attr_map); i++) {
|
||||
/* proc tables don't show "strictatime" */
|
||||
if (attr & MOUNT_ATTR_STRICTATIME)
|
||||
continue;
|
||||
if (attr & mount_attr_map[i].attr)
|
||||
ms_opts |= mount_attr_map[i].ms_opt;
|
||||
}
|
||||
|
||||
/* If neither MOUNT_ATTR_NOATIME or MOUNT_ATTR_STRICTATIME is
|
||||
* set then it's "relatime".
|
||||
*/
|
||||
if (!(attr & MOUNT_ATTR__ATIME))
|
||||
ms_opts |= MS_RELATIME;
|
||||
|
||||
return ms_opts;
|
||||
}
|
||||
#else /* !USE_LIBMOUNT_SUPPORT_FSINFO */
|
||||
#define mnt_fs_fetch_member_fsinfo(_fs, _member) __extension__ ({ \
|
||||
(0); \
|
||||
})
|
||||
#endif
|
||||
|
||||
/**
|
||||
* mnt_new_fs:
|
||||
*
|
||||
|
@ -347,6 +457,8 @@ const char *mnt_fs_get_srcpath(struct libmnt_fs *fs)
|
|||
/* fstab-like fs */
|
||||
if (fs->tagname)
|
||||
return NULL; /* the source contains a "NAME=value" */
|
||||
if (!fs->source && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, source);
|
||||
return fs->source;
|
||||
}
|
||||
|
||||
|
@ -359,6 +471,8 @@ const char *mnt_fs_get_srcpath(struct libmnt_fs *fs)
|
|||
*/
|
||||
const char *mnt_fs_get_source(struct libmnt_fs *fs)
|
||||
{
|
||||
if (!fs->source && !fs->tagname && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, source);
|
||||
return fs ? fs->source : NULL;
|
||||
}
|
||||
|
||||
|
@ -535,7 +649,11 @@ int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value)
|
|||
*/
|
||||
const char *mnt_fs_get_target(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->target : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
if (!fs->target && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, target);
|
||||
return fs->target;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -557,38 +675,41 @@ static int mnt_fs_get_flags(struct libmnt_fs *fs)
|
|||
return fs ? fs->flags : 0;
|
||||
}
|
||||
|
||||
int __mnt_fs_set_propagation_from_string(struct libmnt_fs *fs, const char *str)
|
||||
{
|
||||
assert(fs);
|
||||
|
||||
fs->propagation = 0;
|
||||
if (!str)
|
||||
return 0;
|
||||
/*
|
||||
* The optional fields format is incompatible with mount options
|
||||
* ... we have to parse the field here.
|
||||
*/
|
||||
fs->propagation |= strstr(str, "shared:") ? MS_SHARED : MS_PRIVATE;
|
||||
|
||||
if (strstr(str, "master:"))
|
||||
fs->propagation |= MS_SLAVE;
|
||||
if (strstr(str, "unbindable"))
|
||||
fs->propagation |= MS_UNBINDABLE;
|
||||
return 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)
|
||||
if (!fs)
|
||||
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;
|
||||
|
||||
if (!fs->propagation && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, propagation);
|
||||
if (fs && flags)
|
||||
*flags = fs->propagation;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -644,7 +765,11 @@ int mnt_fs_is_netfs(struct libmnt_fs *fs)
|
|||
*/
|
||||
const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->fstype : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
if (!fs->fstype && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, fstype);
|
||||
return fs->fstype;
|
||||
}
|
||||
|
||||
/* Used by the struct libmnt_file parser only */
|
||||
|
@ -769,7 +894,7 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
|
|||
if (fs->optstr)
|
||||
return strdup(fs->optstr);
|
||||
|
||||
res = merge_optstr(fs->vfs_optstr, fs->fs_optstr);
|
||||
res = merge_optstr(mnt_fs_get_vfs_options(fs), mnt_fs_get_fs_options(fs));
|
||||
if (!res && errno)
|
||||
return NULL;
|
||||
if (fs->user_optstr &&
|
||||
|
@ -788,7 +913,14 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
|
|||
*/
|
||||
const char *mnt_fs_get_options(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->optstr : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
|
||||
if (!fs->optstr && mnt_fs_has_fsinfo(fs))
|
||||
/* in on-demand mode we don't fill optstr by default */
|
||||
fs->optstr = mnt_fs_strdup_options(fs);
|
||||
|
||||
return fs->optstr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -937,7 +1069,11 @@ int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr)
|
|||
*/
|
||||
const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->fs_optstr : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
if (!fs->fs_optstr && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, fs_optstr);
|
||||
return fs->fs_optstr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -948,7 +1084,11 @@ const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
|
|||
*/
|
||||
const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->vfs_optstr : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
if (!fs->vfs_optstr && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, vfs_optstr);
|
||||
return fs->vfs_optstr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1090,7 +1230,11 @@ int mnt_fs_set_passno(struct libmnt_fs *fs, int passno)
|
|||
*/
|
||||
const char *mnt_fs_get_root(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->root : NULL;
|
||||
if (!fs)
|
||||
return NULL;
|
||||
if (!fs->root && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, root);
|
||||
return fs->root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1197,7 +1341,26 @@ int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src)
|
|||
*/
|
||||
int mnt_fs_get_id(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->id : -EINVAL;
|
||||
if (!fs)
|
||||
return 0;
|
||||
if (!fs->id && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, id);
|
||||
return fs->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_fs_set_id:
|
||||
* @fs: filesystem instance
|
||||
* @id: new ID
|
||||
*
|
||||
* Returns: 0 on success or negative number in case of error.
|
||||
*/
|
||||
int mnt_fs_set_id(struct libmnt_fs *fs, int id)
|
||||
{
|
||||
if (!fs)
|
||||
return -EINVAL;
|
||||
fs->id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1208,7 +1371,11 @@ int mnt_fs_get_id(struct libmnt_fs *fs)
|
|||
*/
|
||||
int mnt_fs_get_parent_id(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->parent : -EINVAL;
|
||||
if (!fs)
|
||||
return 0;
|
||||
if (!fs->parent && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, parent);
|
||||
return fs->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1219,7 +1386,11 @@ int mnt_fs_get_parent_id(struct libmnt_fs *fs)
|
|||
*/
|
||||
dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->devno : 0;
|
||||
if (!fs)
|
||||
return 0;
|
||||
if (!fs->devno && mnt_fs_has_fsinfo(fs))
|
||||
mnt_fs_fetch_member_fsinfo(fs, devno);
|
||||
return fs->devno;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1249,9 +1420,9 @@ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
|
|||
|
||||
if (!fs)
|
||||
return -EINVAL;
|
||||
if (fs->fs_optstr)
|
||||
if (mnt_fs_get_fs_options(fs))
|
||||
rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
|
||||
if (rc == 1 && fs->vfs_optstr)
|
||||
if (rc == 1 && mnt_fs_get_vfs_options(fs))
|
||||
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);
|
||||
|
@ -1352,7 +1523,7 @@ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
|
|||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!fs || !target || !fs->target)
|
||||
if (!fs || !target || !mnt_fs_get_target(fs))
|
||||
return 0;
|
||||
|
||||
/* 1) native paths */
|
||||
|
@ -1469,7 +1640,7 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
|
|||
*/
|
||||
int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types)
|
||||
{
|
||||
return mnt_match_fstype(fs->fstype, types);
|
||||
return mnt_match_fstype(mnt_fs_get_fstype(fs), types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1622,3 +1793,212 @@ err:
|
|||
mnt_free_mntent(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Merge @uf from utab to @fs
|
||||
*/
|
||||
int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
assert(fs);
|
||||
assert(uf);
|
||||
|
||||
p = mnt_fs_get_user_options(uf);
|
||||
if (p)
|
||||
mnt_fs_append_options(fs, p);
|
||||
|
||||
p = mnt_fs_get_attributes(uf);
|
||||
if (p)
|
||||
mnt_fs_append_attributes(fs, p);
|
||||
|
||||
p = mnt_fs_get_bindsrc(uf);
|
||||
if (p)
|
||||
mnt_fs_set_bindsrc(fs, p);
|
||||
|
||||
fs->flags |= MNT_FS_MERGED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_fs_has_fsinfo:
|
||||
* @fs: libmnt_fs instance
|
||||
*
|
||||
* Returns: 1 is fsinfo enabled in @fs or 0.
|
||||
*/
|
||||
int mnt_fs_has_fsinfo(struct libmnt_fs *fs)
|
||||
{
|
||||
return fs ? fs->fsinfo_enabled : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_fs_enable_fsinfo:
|
||||
* @fs: libmnt_fs instance
|
||||
* @enable: 1 or 0
|
||||
*
|
||||
* Enable on-demand fsinfo() use. See also mnt_has_fsinfo(). @fs target or ID
|
||||
* has to be set in the @fs. The fsinfo for the @fs maybe automaticaly disabled
|
||||
* later if kernel does not suppor this feature.
|
||||
*/
|
||||
int mnt_fs_enable_fsinfo(struct libmnt_fs *fs, int enable)
|
||||
{
|
||||
assert(fs);
|
||||
fs->fsinfo_enabled = enable ? 1 : 0;
|
||||
/*DBG(FS, ul_debugobj(fs, "fsinfo %s", enable ? "ENABLED" : "DISABLED"));*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs __attribute__((__unused__)),
|
||||
int request __attribute__((__unused__)))
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#else /* USE_LIBMOUNT_SUPPORT_FSINFO */
|
||||
|
||||
static int fsinfo_buf2fs(struct libmnt_fs *fs,
|
||||
int request, char *buf, size_t len)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (request) {
|
||||
case FSINFO_ATTR_SOURCE:
|
||||
buf[len] = '\0';
|
||||
rc = mnt_fs_set_source(fs, *buf ? buf : NULL);
|
||||
break;
|
||||
case FSINFO_ATTR_CONFIGURATION:
|
||||
buf[len] = '\0';
|
||||
rc = strdup_to_struct_member(fs, fs_optstr, *buf ? buf : NULL);
|
||||
break;
|
||||
case FSINFO_ATTR_MOUNT_PATH:
|
||||
buf[len] = '\0';
|
||||
rc = strdup_to_struct_member(fs, root, *buf ? buf : NULL);
|
||||
break;
|
||||
case FSINFO_ATTR_MOUNT_POINT_FULL:
|
||||
buf[len] = '\0';
|
||||
rc = strdup_to_struct_member(fs, target, *buf ? buf : NULL);
|
||||
break;
|
||||
case FSINFO_ATTR_IDS:
|
||||
{
|
||||
struct fsinfo_ids *x = (struct fsinfo_ids *) buf;
|
||||
/* devno */
|
||||
fs->devno = makedev(x->f_dev_major, x->f_dev_minor);
|
||||
/* FS-type */
|
||||
mnt_fs_set_fstype(fs, x->f_fs_name);
|
||||
break;
|
||||
}
|
||||
case FSINFO_ATTR_MOUNT_INFO:
|
||||
{
|
||||
struct fsinfo_mount_info *x = (struct fsinfo_mount_info *) buf;
|
||||
unsigned int ms_opts;
|
||||
/* mount ID */
|
||||
fs->id = x->mnt_id;
|
||||
/* VFS options (convert it to strings) */
|
||||
free(fs->vfs_optstr);
|
||||
fs->vfs_optstr = NULL;
|
||||
ms_opts = fsinfo_mount_attr2ms(x->attr);
|
||||
rc = mnt_optstr_apply_flags(&fs->vfs_optstr, ms_opts,
|
||||
mnt_get_builtin_optmap(MNT_LINUX_MAP));
|
||||
break;
|
||||
}
|
||||
case FSINFO_ATTR_MOUNT_TOPOLOGY:
|
||||
{
|
||||
struct fsinfo_mount_topology *x = (struct fsinfo_mount_topology *) buf;
|
||||
fs->parent = x->parent_id;
|
||||
fs->propagation = x->propagation;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(request);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* fetch and apply to fs */
|
||||
static int __fs_fetch_fsinfo(struct libmnt_fs *fs,
|
||||
char *query,
|
||||
unsigned int request,
|
||||
unsigned int flags,
|
||||
unsigned int at_flags)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
size_t sz = sizeof(buf);
|
||||
int rc;
|
||||
struct fsinfo_params params = {
|
||||
.request = request,
|
||||
.flags = flags,
|
||||
.at_flags = at_flags
|
||||
};
|
||||
|
||||
rc = mnt_fsinfo(query, ¶ms, sizeof(params), buf, &sz);
|
||||
if (rc == -ENOSYS)
|
||||
mnt_fs_enable_fsinfo(fs, 0);
|
||||
else if (rc == 0)
|
||||
rc = fsinfo_buf2fs(fs, request, buf, sz);
|
||||
|
||||
fs->fsinfo_done |= fsinfo_req2mask(request);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call fsinfo() for specified @request and apply result to @fs. If the @request
|
||||
* is -1 than call all fsinfo() requests to fill complete @fs.
|
||||
*/
|
||||
int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs, int request)
|
||||
{
|
||||
int rc = 0;
|
||||
char idstr[sizeof(stringify_value(UINT_MAX))];
|
||||
|
||||
if (!fs->target && !fs->id)
|
||||
return -EINVAL;
|
||||
|
||||
/* don't call the same request more than once */
|
||||
if (fs->fsinfo_done & fsinfo_req2mask(request))
|
||||
return 0;
|
||||
|
||||
/* ask for ID if missing */
|
||||
if (fs->target && !fs->id) {
|
||||
int req = fsinfo_off2req(offsetof(struct libmnt_fs, id));
|
||||
rc = __fs_fetch_fsinfo(fs, fs->target,
|
||||
req,
|
||||
FSINFO_FLAGS_QUERY_PATH,
|
||||
AT_NO_AUTOMOUNT);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (!fs->id)
|
||||
return -EINVAL;
|
||||
if (req == request)
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
snprintf(idstr, sizeof(idstr), "%u", fs->id);
|
||||
|
||||
if (request > -1)
|
||||
/* one request */
|
||||
rc = __fs_fetch_fsinfo(fs, idstr, request,
|
||||
FSINFO_FLAGS_QUERY_MOUNT, 0);
|
||||
else {
|
||||
/* fetch all */
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsinfo_map); i++) {
|
||||
int req = fsinfo_map[i].request;
|
||||
|
||||
if (fs->fsinfo_done & fsinfo_req2mask(req))
|
||||
continue;
|
||||
rc = __fs_fetch_fsinfo(fs, idstr, req,
|
||||
FSINFO_FLAGS_QUERY_MOUNT, 0);
|
||||
if (rc < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
fs->flags |= MNT_FS_KERNEL;
|
||||
return rc;
|
||||
}
|
||||
#endif /* USE_LIBMOUNT_SUPPORT_FSINFO */
|
||||
|
|
212
libmount/src/fsinfo.c
Normal file
212
libmount/src/fsinfo.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* This file is part of libmount from util-linux project.
|
||||
*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
#include "mountP.h"
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
/* libc fallback */
|
||||
#ifndef HAVE_FSINFO
|
||||
# include <sys/syscall.h>
|
||||
# ifndef __NR_fsinfo
|
||||
# define __NR_fsinfo -1
|
||||
# endif
|
||||
static ssize_t fsinfo(int dfd, const char *filename,
|
||||
struct fsinfo_params *params, size_t params_size,
|
||||
void *result_buffer, size_t result_buf_size)
|
||||
{
|
||||
return syscall(__NR_fsinfo, dfd, filename,
|
||||
params, params_size,
|
||||
result_buffer, result_buf_size);
|
||||
}
|
||||
#endif /* HAVE_FSINFO */
|
||||
|
||||
static int have_fsinfo = -1;
|
||||
|
||||
int mnt_get_target_id(const char *path, unsigned int *id, unsigned int flags)
|
||||
{
|
||||
struct fsinfo_mount_info info;
|
||||
struct fsinfo_params params = {
|
||||
.flags = FSINFO_FLAGS_QUERY_PATH,
|
||||
.request = FSINFO_ATTR_MOUNT_INFO,
|
||||
.at_flags = flags,
|
||||
};
|
||||
int rc = 0;
|
||||
|
||||
DBG(UTILS, ul_debug("fsinfo: reading ID for %s", path));
|
||||
errno = 0;
|
||||
if (fsinfo(AT_FDCWD, path,
|
||||
¶ms, sizeof(params), &info, sizeof(info)) < 0)
|
||||
rc = -errno;
|
||||
else
|
||||
*id = info.mnt_id;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call fsinfo(), fill @buf with the result, on success update @bufsz
|
||||
* to the real result size.
|
||||
*/
|
||||
int mnt_fsinfo(const char *query,
|
||||
struct fsinfo_params *params,
|
||||
size_t params_size,
|
||||
char *buf,
|
||||
size_t *bufsz)
|
||||
{
|
||||
ssize_t res;
|
||||
int rc = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(bufsz);
|
||||
assert(params);
|
||||
|
||||
DBG(UTILS, ul_debug("fsinfo(2) [query=%s, request=0x%02x, flags=0x%02x, at_flags=0x%02x]",
|
||||
query, params->request, params->flags, params->at_flags));
|
||||
errno = 0;
|
||||
res = fsinfo(AT_FDCWD, query, params, params_size, buf, *bufsz);
|
||||
if (res < 0) {
|
||||
rc = res;
|
||||
errno = -res;
|
||||
} else if ((size_t) res > *bufsz) {
|
||||
DBG(UTILS, ul_debug("fsinfo(2) ENAMETOOLONG: result=%lu, expected=%lu", res, *bufsz));
|
||||
rc = -ENAMETOOLONG;
|
||||
} else if (rc == 0)
|
||||
*bufsz = res;
|
||||
if (have_fsinfo == -1)
|
||||
have_fsinfo = rc == -ENOSYS ? 0 : 1;
|
||||
|
||||
if (rc)
|
||||
DBG(UTILS, ul_debug("fsinfo(2) [res=%ld]", res));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void *fsinfo_alloc_attrs(unsigned int id,
|
||||
unsigned int attr, unsigned int Nth,
|
||||
size_t *size)
|
||||
{
|
||||
char idstr[sizeof(stringify_value(UINT_MAX))];
|
||||
struct fsinfo_params params = {
|
||||
.flags = FSINFO_FLAGS_QUERY_MOUNT,
|
||||
.request = attr,
|
||||
.Nth = Nth,
|
||||
};
|
||||
size_t bufsz = BUFSIZ;
|
||||
void *buf = NULL;
|
||||
|
||||
snprintf(idstr, sizeof(idstr), "%u", id);
|
||||
|
||||
errno = 0;
|
||||
for (;;) {
|
||||
ssize_t ret;
|
||||
void *tmp;
|
||||
|
||||
tmp = realloc(buf, bufsz);
|
||||
if (!tmp) {
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
buf = tmp;
|
||||
|
||||
errno = 0;
|
||||
ret = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, bufsz);
|
||||
if (ret < 0) {
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((size_t) ret <= bufsz) {
|
||||
*size = (size_t) ret;
|
||||
return buf; /* sucess */
|
||||
}
|
||||
bufsz = (ret + BUFSIZ - 1) & ~(BUFSIZ - 1);
|
||||
}
|
||||
|
||||
return NULL; /* error */
|
||||
}
|
||||
|
||||
/* Returns: 0 on success, <0 on error */
|
||||
static int mnt_fsinfo_get_id_list(unsigned int id, unsigned int attr,
|
||||
struct fsinfo_mount_child **mounts,
|
||||
size_t *count)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
*mounts = fsinfo_alloc_attrs(id, attr, 0, &size);
|
||||
if (!*mounts)
|
||||
return -errno;
|
||||
|
||||
/* Don't include mount for id itself when getting children */
|
||||
if (attr == FSINFO_ATTR_MOUNT_CHILDREN)
|
||||
*count = size / sizeof((*mounts)[0]) - 1;
|
||||
else
|
||||
*count = size / sizeof((*mounts)[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns: 0 on success, <0 on error */
|
||||
int mnt_fsinfo_get_children(unsigned int id,
|
||||
struct fsinfo_mount_child **mounts,
|
||||
size_t *count)
|
||||
{
|
||||
assert(id);
|
||||
assert(mounts);
|
||||
assert(count);
|
||||
|
||||
DBG(UTILS, ul_debug("fsinfo: reading children for id=%u", id));
|
||||
return mnt_fsinfo_get_id_list(id, FSINFO_ATTR_MOUNT_CHILDREN, mounts, count);
|
||||
}
|
||||
|
||||
/* Returns: 0 on success, <0 on error */
|
||||
int mnt_fsinfo_get_mounts(unsigned int id,
|
||||
struct fsinfo_mount_child **mounts,
|
||||
size_t *count)
|
||||
{
|
||||
assert(id);
|
||||
assert(mounts);
|
||||
assert(count);
|
||||
|
||||
DBG(UTILS, ul_debug("fsinfo: reading all mounts for id=%u", id));
|
||||
return mnt_fsinfo_get_id_list(id, FSINFO_ATTR_MOUNT_ALL, mounts, count);
|
||||
}
|
||||
#endif /* USE_LIBMOUNT_SUPPORT_FSINFO */
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
|
||||
/**
|
||||
* mnt_has_fsinfo:
|
||||
*
|
||||
* Returns: 1 is fsinfo() syscall is supported, or 0
|
||||
*/
|
||||
int mnt_has_fsinfo(void)
|
||||
{
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
if (have_fsinfo == -1) {
|
||||
struct fsinfo_mount_info info;
|
||||
struct fsinfo_params params = {
|
||||
.flags = FSINFO_FLAGS_QUERY_PATH,
|
||||
.request = FSINFO_ATTR_MOUNT_INFO,
|
||||
};
|
||||
size_t sz = sizeof(info);
|
||||
long rc;
|
||||
|
||||
rc = mnt_fsinfo("/", ¶ms, sizeof(params), (char *)&info, &sz);
|
||||
if (rc != 1)
|
||||
have_fsinfo = 1;
|
||||
else
|
||||
have_fsinfo = 0;
|
||||
}
|
||||
return have_fsinfo;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ UL_DEBUG_DEFINE_MASKNAMES(libmount) =
|
|||
{ "monitor", MNT_DEBUG_MONITOR, "mount tables monitor" },
|
||||
{ "btrfs", MNT_DEBUG_BTRFS, "btrfs specific routines" },
|
||||
{ "verity", MNT_DEBUG_VERITY, "verity specific routines" },
|
||||
{ "watch", MNT_DEBUG_WATCH, "kernel watch-queue" },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
|
187
libmount/src/kernelwatch.c
Normal file
187
libmount/src/kernelwatch.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* This file is part of libmount from util-linux project.
|
||||
*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
#include "mountP.h"
|
||||
|
||||
#define _LINUX_FCNTL_H /* WORKAROUND to build against non-distro headers */
|
||||
#include <linux/watch_queue.h>
|
||||
|
||||
#ifndef USE_LIBMOUNT_SUPPORT_WATCHQUEUE
|
||||
int mnt_kernelwatch_is_mount(void *data __attribute__((__unused__)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int mnt_kernelwatch_get_mount_id(void *data __attribute__((__unused__)))
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
int mnt_kernelwatch_get_operation(void *data __attribute__((__unused__)))
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
void *mnt_kernelwatch_next_data(void *data __attribute__((__unused__)),
|
||||
ssize_t *datasz __attribute__((__unused__)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
int mnt_kernelwatch_is_valid(void *data __attribute__((__unused__)),
|
||||
size_t datasz __attribute__((__unused__)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* USE_LIBMOUNT_SUPPORT_WATCHQUEUE */
|
||||
|
||||
#define MNT_KERNELWATCH_MSG_MINSZ sizeof(struct watch_notification)
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_is_valid:
|
||||
* @data: event data
|
||||
*
|
||||
* Returns: 1 if data seems valid as notification, or 0.
|
||||
*/
|
||||
int mnt_kernelwatch_is_valid(void *data, size_t datasz)
|
||||
{
|
||||
struct watch_notification *n = (struct watch_notification *) data;
|
||||
size_t len;
|
||||
|
||||
if (!n || !datasz) {
|
||||
DBG(WATCH, ul_debugobj(data, "no data"));
|
||||
return 0;
|
||||
}
|
||||
len = n->info & WATCH_INFO_LENGTH;
|
||||
if (len < MNT_KERNELWATCH_MSG_MINSZ || len > datasz) {
|
||||
DBG(WATCH, ul_debugobj(data, "invalid watch_notification.len"));
|
||||
return 0;
|
||||
}
|
||||
DBG(WATCH, ul_debugobj(data, "valid"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_is_mount:
|
||||
* @data: event data
|
||||
*
|
||||
* Returns: 1 if data contains mount node notification or 0.
|
||||
*
|
||||
* Since: v2.37
|
||||
*/
|
||||
int mnt_kernelwatch_is_mount(void *data)
|
||||
{
|
||||
const struct watch_notification *n;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
n = (const struct watch_notification *) data;
|
||||
return n->type == WATCH_TYPE_MOUNT_NOTIFY;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_get_mount_id
|
||||
* @data: event data
|
||||
*
|
||||
* This is ID of the mount node which trigers the notification, note that
|
||||
* for example new mount, move or umount are triggered by parental filesystem.
|
||||
*
|
||||
* Returns: mount ID or <0 on error.
|
||||
*
|
||||
* Since: v2.37
|
||||
*/
|
||||
int mnt_kernelwatch_get_mount_id(void *data)
|
||||
{
|
||||
if (data && mnt_kernelwatch_is_mount(data)) {
|
||||
const struct mount_notification *m = (const struct mount_notification *) data;
|
||||
return m->triggered_on;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_get_aux_id
|
||||
* @data: event data
|
||||
*
|
||||
* This is ID of the mount note which is added/moved/removed or 0.
|
||||
*
|
||||
* Returns: mount ID or <0 on error.
|
||||
*
|
||||
* Since: v2.37
|
||||
*/
|
||||
int mnt_kernelwatch_get_aux_id(void *data)
|
||||
{
|
||||
if (data && mnt_kernelwatch_is_mount(data)) {
|
||||
const struct mount_notification *m = (const struct mount_notification *) data;
|
||||
return m->auxiliary_mount;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_get_operation
|
||||
* @data: event data
|
||||
*
|
||||
* See mount_notification_subtype NOTIFY_MOUNT_* in linux/watch_queue.h.
|
||||
*
|
||||
* Returns: operation indentifier or <0 on error.
|
||||
*
|
||||
* Since: v2.37
|
||||
*/
|
||||
int mnt_kernelwatch_get_operation(void *data)
|
||||
{
|
||||
const struct watch_notification *n;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
n = (const struct watch_notification *) data;
|
||||
return n->subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_kernelwatch_next_data
|
||||
* @data: event data
|
||||
* @datasz: the remaining size of the data
|
||||
*
|
||||
* Returns: pointer to the next kernel watch_queue message or NULL if not more messages in the buffer.
|
||||
*
|
||||
* Since: v2.37
|
||||
*/
|
||||
void *mnt_kernelwatch_next_data(void *data, ssize_t *datasz)
|
||||
{
|
||||
struct watch_notification *n;
|
||||
size_t len;
|
||||
char *p;
|
||||
|
||||
/* check the current message */
|
||||
if (!mnt_kernelwatch_is_valid(data, *datasz))
|
||||
return NULL;
|
||||
|
||||
p = (char *) data;
|
||||
n = (struct watch_notification *) data;
|
||||
|
||||
/* go to the next message */
|
||||
len = n->info & WATCH_INFO_LENGTH;
|
||||
*datasz -= len;
|
||||
|
||||
if (*datasz < (ssize_t) MNT_KERNELWATCH_MSG_MINSZ) {
|
||||
DBG(WATCH, ul_debugobj(p, "no more messages"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p += len;
|
||||
|
||||
/* check the next message */
|
||||
if (!mnt_kernelwatch_is_valid(p, *datasz))
|
||||
return NULL;
|
||||
|
||||
DBG(WATCH, ul_debugobj(p, "next message"));
|
||||
return p;
|
||||
}
|
||||
#endif /* USE_LIBMOUNT_SUPPORT_WATCHQUEUE */
|
|
@ -488,6 +488,7 @@ extern int mnt_fs_set_root(struct libmnt_fs *fs, const char *path);
|
|||
extern const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs);
|
||||
extern int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src);
|
||||
extern int mnt_fs_get_id(struct libmnt_fs *fs);
|
||||
extern int mnt_fs_set_id(struct libmnt_fs *fs, int id);
|
||||
extern int mnt_fs_get_parent_id(struct libmnt_fs *fs);
|
||||
extern dev_t mnt_fs_get_devno(struct libmnt_fs *fs);
|
||||
extern pid_t mnt_fs_get_tid(struct libmnt_fs *fs);
|
||||
|
@ -518,6 +519,12 @@ extern int mnt_fs_is_pseudofs(struct libmnt_fs *fs);
|
|||
extern void mnt_free_mntent(struct mntent *mnt);
|
||||
extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt);
|
||||
|
||||
extern int mnt_fs_enable_fsinfo(struct libmnt_fs *fs, int enable);
|
||||
extern int mnt_fs_has_fsinfo(struct libmnt_fs *fs);
|
||||
|
||||
/* fsinfo.c */
|
||||
extern int mnt_has_fsinfo(void);
|
||||
|
||||
/* tab-parse.c */
|
||||
extern struct libmnt_table *mnt_new_table_from_file(const char *filename)
|
||||
__ul_attribute__((warn_unused_result));
|
||||
|
@ -525,6 +532,7 @@ extern struct libmnt_table *mnt_new_table_from_dir(const char *dirname)
|
|||
__ul_attribute__((warn_unused_result));
|
||||
extern int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f,
|
||||
const char *filename);
|
||||
extern int mnt_table_parse_fsinfo(struct libmnt_table *tb);
|
||||
extern int mnt_table_parse_file(struct libmnt_table *tb, const char *filename);
|
||||
extern int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname);
|
||||
|
||||
|
@ -655,7 +663,8 @@ extern int mnt_tabdiff_next_change(struct libmnt_tabdiff *df,
|
|||
/* monitor.c */
|
||||
enum {
|
||||
MNT_MONITOR_TYPE_USERSPACE = 1, /* userspace mount options */
|
||||
MNT_MONITOR_TYPE_KERNEL /* kernel mount table */
|
||||
MNT_MONITOR_TYPE_KERNEL, /* kernel mount table poll */
|
||||
MNT_MONITOR_TYPE_KERNELWATCH /* kernel mount/superblock watch */
|
||||
};
|
||||
|
||||
extern struct libmnt_monitor *mnt_new_monitor(void);
|
||||
|
@ -663,6 +672,7 @@ extern void mnt_ref_monitor(struct libmnt_monitor *mn);
|
|||
extern void mnt_unref_monitor(struct libmnt_monitor *mn);
|
||||
|
||||
extern int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable);
|
||||
extern int mnt_monitor_enable_kernelwatch(struct libmnt_monitor *mn, int enable);
|
||||
extern int mnt_monitor_enable_userspace(struct libmnt_monitor *mn,
|
||||
int enable, const char *filename);
|
||||
|
||||
|
@ -674,6 +684,17 @@ extern int mnt_monitor_next_change(struct libmnt_monitor *mn,
|
|||
const char **filename, int *type);
|
||||
extern int mnt_monitor_event_cleanup(struct libmnt_monitor *mn);
|
||||
|
||||
extern int mnt_monitor_keep_data(struct libmnt_monitor *mn, int type, int enable);
|
||||
extern void *mnt_monitor_event_data(struct libmnt_monitor *mn, int type, ssize_t *bufsz);
|
||||
|
||||
/* kernelwatch.c */
|
||||
extern int mnt_kernelwatch_is_valid(void *data, size_t datasz);
|
||||
extern int mnt_kernelwatch_is_mount(void *data);
|
||||
extern int mnt_kernelwatch_get_mount_id(void *data);
|
||||
extern int mnt_kernelwatch_get_aux_id(void *data);
|
||||
extern int mnt_kernelwatch_get_operation(void *data);
|
||||
extern void *mnt_kernelwatch_next_data(void *data, ssize_t *datasz);
|
||||
|
||||
|
||||
/* context.c */
|
||||
|
||||
|
|
|
@ -356,3 +356,20 @@ MOUNT_2_35 {
|
|||
mnt_context_get_target_prefix;
|
||||
mnt_context_set_target_prefix;
|
||||
} MOUNT_2.34;
|
||||
|
||||
MOUNT_2_36 {
|
||||
mnt_fs_enable_fsinfo;
|
||||
mnt_fs_has_fsinfo;
|
||||
mnt_has_fsinfo;
|
||||
mnt_table_parse_fsinfo;
|
||||
mnt_monitor_enable_kernelwatch;
|
||||
mnt_monitor_event_data;
|
||||
mnt_kernelwatch_is_mount;
|
||||
mnt_kernelwatch_is_valid;
|
||||
mnt_kernelwatch_get_mount_id;
|
||||
mnt_kernelwatch_get_aux_id;
|
||||
mnt_kernelwatch_get_operation;
|
||||
mnt_kernelwatch_next_data;
|
||||
mnt_fs_set_id;
|
||||
} MOUNT_2_35;
|
||||
|
||||
|
|
|
@ -35,28 +35,44 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "fileutils.h"
|
||||
#include "mountP.h"
|
||||
#include "pathnames.h"
|
||||
#include "fileutils.h"
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_WATCHQUEUE
|
||||
#define _LINUX_FCNTL_H /* WORKAROUND to build against non-distro headers */
|
||||
# include <sys/ioctl.h>
|
||||
# include <linux/unistd.h>
|
||||
# include <linux/watch_queue.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct monitor_opers;
|
||||
|
||||
/*
|
||||
* The libmount supports multiple ways (channels) how to monitor mount table
|
||||
* changes -- each way is represented by one mount monitor entry.
|
||||
*/
|
||||
struct monitor_entry {
|
||||
int fd; /* private entry file descriptor */
|
||||
char *path; /* path to the monitored file */
|
||||
int type; /* MNT_MONITOR_TYPE_* */
|
||||
uint32_t events; /* wanted epoll events */
|
||||
|
||||
char *buf; /* buffer to read from kernel */
|
||||
size_t bufsz; /* buffer size */
|
||||
ssize_t bufrsz; /* last read() size */
|
||||
|
||||
const struct monitor_opers *opers;
|
||||
|
||||
unsigned int enable : 1,
|
||||
changed : 1;
|
||||
unsigned int enabled : 1,
|
||||
keep_data : 1, /* return read() buffer to caller */
|
||||
changed : 1; /* change detected */
|
||||
|
||||
struct list_head ents;
|
||||
struct list_head ents; /* libmnt_monitor->ents list item */
|
||||
};
|
||||
|
||||
struct libmnt_monitor {
|
||||
|
@ -69,7 +85,7 @@ struct libmnt_monitor {
|
|||
struct monitor_opers {
|
||||
int (*op_get_fd)(struct libmnt_monitor *, struct monitor_entry *);
|
||||
int (*op_close_fd)(struct libmnt_monitor *, struct monitor_entry *);
|
||||
int (*op_event_verify)(struct libmnt_monitor *, struct monitor_entry *);
|
||||
int (*op_event_read)(struct libmnt_monitor *, struct monitor_entry *);
|
||||
};
|
||||
|
||||
static int monitor_modify_epoll(struct libmnt_monitor *mn,
|
||||
|
@ -117,6 +133,7 @@ static void free_monitor_entry(struct monitor_entry *me)
|
|||
if (me->fd >= 0)
|
||||
close(me->fd);
|
||||
free(me->path);
|
||||
free(me->buf);
|
||||
free(me);
|
||||
}
|
||||
|
||||
|
@ -146,7 +163,7 @@ void mnt_unref_monitor(struct libmnt_monitor *mn)
|
|||
}
|
||||
}
|
||||
|
||||
static struct monitor_entry *monitor_new_entry(struct libmnt_monitor *mn)
|
||||
static struct monitor_entry *monitor_new_entry(struct libmnt_monitor *mn, size_t bufsz)
|
||||
{
|
||||
struct monitor_entry *me;
|
||||
|
||||
|
@ -159,7 +176,17 @@ static struct monitor_entry *monitor_new_entry(struct libmnt_monitor *mn)
|
|||
list_add_tail(&me->ents, &mn->ents);
|
||||
|
||||
me->fd = -1;
|
||||
me->bufsz = bufsz;
|
||||
|
||||
if (me->bufsz) {
|
||||
me->buf = malloc(bufsz);
|
||||
if (!me->buf) {
|
||||
free(me);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DBG(MONITOR, ul_debugobj(me, "alloc entry"));
|
||||
return me;
|
||||
}
|
||||
|
||||
|
@ -237,7 +264,7 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
|
|||
errno = 0;
|
||||
wd = inotify_add_watch(me->fd, filename, IN_CLOSE_NOWRITE);
|
||||
if (wd >= 0) {
|
||||
DBG(MONITOR, ul_debug(" added inotify watch for %s [fd=%d]", filename, wd));
|
||||
DBG(MONITOR, ul_debugobj(me, " added lock inotify-watch [%s, fd=%d]", filename, wd));
|
||||
rc = 0;
|
||||
if (final)
|
||||
*final = 1;
|
||||
|
@ -249,6 +276,9 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* If the lock file does not exist yet, then watch the first
|
||||
* avalable parental directory.
|
||||
*/
|
||||
while (strchr(filename, '/')) {
|
||||
stripoff_last_component(filename);
|
||||
if (!*filename)
|
||||
|
@ -258,7 +288,7 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
|
|||
errno = 0;
|
||||
wd = inotify_add_watch(me->fd, filename, IN_CREATE|IN_ISDIR);
|
||||
if (wd >= 0) {
|
||||
DBG(MONITOR, ul_debug(" added inotify watch for %s [fd=%d]", filename, wd));
|
||||
DBG(MONITOR, ul_debugobj(me, " added dir inotify-watch [%s, fd=%d]", filename, wd));
|
||||
rc = 0;
|
||||
if (fd)
|
||||
*fd = wd;
|
||||
|
@ -273,18 +303,17 @@ done:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int userspace_monitor_get_fd(struct libmnt_monitor *mn,
|
||||
static int userspace_monitor_get_fd(struct libmnt_monitor *mn __attribute__((__unused__)),
|
||||
struct monitor_entry *me)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!me || me->enable == 0) /* not-initialized or disabled */
|
||||
if (!me || me->enabled == 0) /* not-initialized or disabled */
|
||||
return -EINVAL;
|
||||
if (me->fd >= 0)
|
||||
return me->fd; /* already initialized */
|
||||
|
||||
assert(me->path);
|
||||
DBG(MONITOR, ul_debugobj(mn, " open userspace monitor for %s", me->path));
|
||||
|
||||
me->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (me->fd < 0)
|
||||
|
@ -293,47 +322,50 @@ static int userspace_monitor_get_fd(struct libmnt_monitor *mn,
|
|||
if (userspace_add_watch(me, NULL, NULL) < 0)
|
||||
goto err;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(me, " new userspace monitor [%s, inotify fd=%d]",
|
||||
me->path, me->fd));
|
||||
return me->fd;
|
||||
err:
|
||||
rc = -errno;
|
||||
if (me->fd >= 0)
|
||||
close(me->fd);
|
||||
me->fd = -1;
|
||||
DBG(MONITOR, ul_debugobj(mn, "failed to create userspace monitor [rc=%d]", rc));
|
||||
DBG(MONITOR, ul_debugobj(me, "failed to create userspace monitor [rc=%d]", rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify and drain inotify buffer
|
||||
*/
|
||||
static int userspace_event_verify(struct libmnt_monitor *mn,
|
||||
static int userspace_monitor_read(struct libmnt_monitor *mn,
|
||||
struct monitor_entry *me)
|
||||
{
|
||||
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
|
||||
int status = 0;
|
||||
int last_removed = -1;
|
||||
|
||||
if (!me || me->fd < 0)
|
||||
return 0;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "drain and verify userspace monitor inotify"));
|
||||
DBG(MONITOR, ul_debugobj(me, "drain/verify userspace monitor"));
|
||||
|
||||
/* the me->fd is non-blocking */
|
||||
do {
|
||||
ssize_t len;
|
||||
char *p;
|
||||
const struct inotify_event *e;
|
||||
|
||||
len = read(me->fd, buf, sizeof(buf));
|
||||
if (len < 0)
|
||||
DBG(MONITOR, ul_debugobj(me, " reading inotify events"));
|
||||
me->bufrsz = read(me->fd, me->buf, me->bufsz);
|
||||
if (me->bufrsz <= 0)
|
||||
break;
|
||||
|
||||
for (p = buf; p < buf + len;
|
||||
for (p = me->buf; p < me->buf + me->bufrsz;
|
||||
p += sizeof(struct inotify_event) + e->len) {
|
||||
|
||||
int fd = -1;
|
||||
|
||||
e = (const struct inotify_event *) p;
|
||||
DBG(MONITOR, ul_debugobj(mn, " inotify event 0x%x [%s]\n", e->mask, e->len ? e->name : ""));
|
||||
DBG(MONITOR, ul_debugobj(me, " inotify event 0x%p [mask=0x%x, name=%s]\n",
|
||||
e, e->mask, e->len ? e->name : ""));
|
||||
|
||||
if (e->mask & IN_CLOSE_NOWRITE)
|
||||
status = 1;
|
||||
|
@ -341,12 +373,24 @@ static int userspace_event_verify(struct libmnt_monitor *mn,
|
|||
/* event on lock file */
|
||||
userspace_add_watch(me, &status, &fd);
|
||||
|
||||
if (fd != e->wd) {
|
||||
DBG(MONITOR, ul_debugobj(mn, " removing watch [fd=%d]", e->wd));
|
||||
if (fd != e->wd && e->wd != last_removed) {
|
||||
/*
|
||||
* If the new watch (fd) is diffrent than already used e->wd
|
||||
* than remove old watch. This is necessary for example when
|
||||
* we move watch from /run/mount to /run/mount/utab/lock.
|
||||
*
|
||||
* The directory watch usually generates more events,
|
||||
* call inotify_rm_watch() only first time.
|
||||
*/
|
||||
DBG(MONITOR, ul_debugobj(me, " removing watch [fd=%d]", e->wd));
|
||||
inotify_rm_watch(me->fd, e->wd);
|
||||
last_removed = e->wd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status == 1 && me->keep_data)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "%s", status == 1 ? " success" : " nothing"));
|
||||
|
@ -357,9 +401,9 @@ static int userspace_event_verify(struct libmnt_monitor *mn,
|
|||
* userspace monitor operations
|
||||
*/
|
||||
static const struct monitor_opers userspace_opers = {
|
||||
.op_get_fd = userspace_monitor_get_fd,
|
||||
.op_close_fd = userspace_monitor_close_fd,
|
||||
.op_event_verify = userspace_event_verify
|
||||
.op_get_fd = userspace_monitor_get_fd,
|
||||
.op_close_fd = userspace_monitor_close_fd,
|
||||
.op_event_read = userspace_monitor_read
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -368,7 +412,7 @@ static const struct monitor_opers userspace_opers = {
|
|||
* @enable: 0 or 1
|
||||
* @filename: overwrites default
|
||||
*
|
||||
* Enables or disables userspace monitoring. If the userspace monitor does not
|
||||
* Enables or disables userspace mount table monitoring. If the userspace monitor does not
|
||||
* exist and enable=1 then allocates new resources necessary for the monitor.
|
||||
*
|
||||
* If the top-level monitor has been already created (by mnt_monitor_get_fd()
|
||||
|
@ -376,11 +420,15 @@ static const struct monitor_opers userspace_opers = {
|
|||
*
|
||||
* The @filename is used only the first time when you enable the monitor. It's
|
||||
* impossible to have more than one userspace monitor. The recommended is to
|
||||
* use NULL as filename.
|
||||
* use NULL as @filename.
|
||||
*
|
||||
* The userspace monitor is unsupported for systems with classic regular
|
||||
* /etc/mtab file.
|
||||
*
|
||||
* This monitor type is able to return inotify event data (as read() from
|
||||
* kernel), but it's disabled by default as it does not provide any details
|
||||
* about changed filesystems. See mnt_monitor_keep_data() for more details.
|
||||
*
|
||||
* Return: 0 on success and <0 on error
|
||||
*/
|
||||
int mnt_monitor_enable_userspace(struct libmnt_monitor *mn, int enable, const char *filename)
|
||||
|
@ -410,7 +458,7 @@ int mnt_monitor_enable_userspace(struct libmnt_monitor *mn, int enable, const ch
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
me = monitor_new_entry(mn);
|
||||
me = monitor_new_entry(mn, sizeof(struct inotify_event) + NAME_MAX + 1);
|
||||
if (!me)
|
||||
goto err;
|
||||
|
||||
|
@ -450,7 +498,7 @@ static int kernel_monitor_get_fd(struct libmnt_monitor *mn,
|
|||
{
|
||||
int rc;
|
||||
|
||||
if (!me || me->enable == 0) /* not-initialized or disabled */
|
||||
if (!me || me->enabled == 0) /* not-initialized or disabled */
|
||||
return -EINVAL;
|
||||
if (me->fd >= 0)
|
||||
return me->fd; /* already initialized */
|
||||
|
@ -511,7 +559,7 @@ int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable)
|
|||
DBG(MONITOR, ul_debugobj(mn, "allocate new kernel monitor"));
|
||||
|
||||
/* create a new entry */
|
||||
me = monitor_new_entry(mn);
|
||||
me = monitor_new_entry(mn, 0);
|
||||
if (!me)
|
||||
goto err;
|
||||
|
||||
|
@ -543,6 +591,202 @@ err:
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* kernel mount-watch monitor
|
||||
*/
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_WATCHQUEUE
|
||||
|
||||
#ifndef HAVE_WATCH_MOUNT
|
||||
# include <sys/syscall.h>
|
||||
# ifndef __NR_watch_mount
|
||||
# define __NR_watch_mount -1
|
||||
# endif
|
||||
static int watch_mount(int dfd, const char *filename, int at_flags, int fd, int id)
|
||||
{
|
||||
return syscall(__NR_watch_mount, dfd, filename, at_flags, fd, id);
|
||||
}
|
||||
#endif /* HAVE_WATCH_MOUNT */
|
||||
|
||||
/* watch specifig tags */
|
||||
enum {
|
||||
MNT_KERNELWATCH_TAG_ROOTMOUNT = 0x01
|
||||
};
|
||||
|
||||
#define MNT_KERNELWATCH_QUEUE_SIZE 256 /* number of messages 1..512 */
|
||||
|
||||
static int kernelwatch_monitor_close_fd(struct libmnt_monitor *mn __attribute__((__unused__)),
|
||||
struct monitor_entry *me)
|
||||
{
|
||||
assert(me);
|
||||
|
||||
if (me->fd >= 0)
|
||||
close(me->fd);
|
||||
me->fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kernelwatch_monitor_get_fd(struct libmnt_monitor *mn __attribute__((__unused__)),
|
||||
struct monitor_entry *me)
|
||||
{
|
||||
int rc;
|
||||
int pipefd[2];
|
||||
|
||||
if (!me || me->enabled == 0) /* not-initialized or disabled */
|
||||
return -EINVAL;
|
||||
if (me->fd >= 0)
|
||||
return me->fd; /* already initialized */
|
||||
|
||||
if (pipe2(pipefd, O_NOTIFICATION_PIPE | O_CLOEXEC | O_NONBLOCK) == -1)
|
||||
goto err;
|
||||
|
||||
/* this is kernel design punk -- you need only one fd, but get two */
|
||||
close(pipefd[1]);
|
||||
|
||||
me->fd = pipefd[0];
|
||||
if (me->fd < 0)
|
||||
goto err;
|
||||
|
||||
if (ioctl(me->fd, IOC_WATCH_QUEUE_SET_SIZE, MNT_KERNELWATCH_QUEUE_SIZE) == -1)
|
||||
goto err;
|
||||
|
||||
if (watch_mount(AT_FDCWD, "/", 0, me->fd, MNT_KERNELWATCH_TAG_ROOTMOUNT) == -1)
|
||||
goto err;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(me, " new kernelwatch monitor [watch fd=%d]", me->fd));
|
||||
return me->fd;
|
||||
err:
|
||||
rc = -errno;
|
||||
if (me->fd >= 0)
|
||||
close(me->fd);
|
||||
me->fd = -1;
|
||||
DBG(MONITOR, ul_debugobj(me, "failed to create kernelwatch monitor [rc=%d]", rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify and drain inotify buffer
|
||||
*/
|
||||
static int kernelwatch_monitor_read(struct libmnt_monitor *mn,
|
||||
struct monitor_entry *me)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (!me || me->fd < 0)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
char *p;
|
||||
ssize_t rest;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(me, "reading kernelwatch monitor"));
|
||||
|
||||
me->bufrsz = read(me->fd, me->buf, me->bufsz);
|
||||
if (me->bufrsz <= 0
|
||||
|| me->bufrsz > (ssize_t) me->bufsz) {
|
||||
DBG(MONITOR, ul_debugobj(me, " no data [rc=%zd]", me->bufrsz));
|
||||
break;
|
||||
}
|
||||
rest = me->bufrsz;
|
||||
p = me->buf;
|
||||
|
||||
do {
|
||||
if (!mnt_kernelwatch_is_valid(p, rest))
|
||||
break;
|
||||
|
||||
status = mnt_kernelwatch_is_mount(p);
|
||||
p = mnt_kernelwatch_next_data(p, &rest);
|
||||
} while (p);
|
||||
|
||||
if (status == 1 && me->keep_data)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "%s", status == 1 ? " success" : " nothing"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* kernelwatch monitor operations
|
||||
*/
|
||||
static const struct monitor_opers kernelwatch_opers = {
|
||||
.op_get_fd = kernelwatch_monitor_get_fd,
|
||||
.op_close_fd = kernelwatch_monitor_close_fd,
|
||||
.op_event_read = kernelwatch_monitor_read
|
||||
};
|
||||
|
||||
static int __enable_kernelwatch(struct libmnt_monitor *mn, int enable)
|
||||
{
|
||||
struct monitor_entry *me;
|
||||
int rc = 0;
|
||||
|
||||
if (!mn)
|
||||
return -EINVAL;
|
||||
|
||||
me = monitor_get_entry(mn, MNT_MONITOR_TYPE_KERNELWATCH);
|
||||
if (me) {
|
||||
rc = monitor_modify_epoll(mn, me, enable);
|
||||
if (!enable)
|
||||
kernelwatch_monitor_close_fd(mn, me);
|
||||
return rc;
|
||||
}
|
||||
if (!enable)
|
||||
return 0;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "allocate new kernelwatch monitor"));
|
||||
|
||||
assert(BUFSIZ > sizeof(struct watch_notification) + 128);
|
||||
|
||||
me = monitor_new_entry(mn, BUFSIZ);
|
||||
if (!me)
|
||||
goto err;
|
||||
|
||||
me->type = MNT_MONITOR_TYPE_KERNELWATCH;
|
||||
me->opers = &kernelwatch_opers;
|
||||
me->events = EPOLLIN | EPOLLET;
|
||||
me->keep_data = 1;
|
||||
|
||||
/* it's kernelwatch_monitor_get_fd() where we setup 'fd' and add watchs */
|
||||
return monitor_modify_epoll(mn, me, TRUE);
|
||||
err:
|
||||
rc = -errno;
|
||||
free_monitor_entry(me);
|
||||
DBG(MONITOR, ul_debugobj(mn, "failed to allocate kernelwatch monitor [rc=%d]", rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* USE_LIBMOUNT_SUPPORT_WATCHQUEUE */
|
||||
|
||||
/**
|
||||
* mnt_monitor_enable_kernelwatch:
|
||||
* @mn: monitor
|
||||
* @enable: 0 or 1
|
||||
*
|
||||
* Enables or disables kernelwatch mounts and superblock. If the kernelwatch monitor does not
|
||||
* exist and enable=1 then allocates new resources necessary for the monitor.
|
||||
*
|
||||
* If the top-level monitor has been already created (by mnt_monitor_get_fd()
|
||||
* or mnt_monitor_wait()) then it's updated according to @enable.
|
||||
*
|
||||
* This monitor type is able to return "struct watch_notification" event data
|
||||
* (as read() from kernel and it's enabled by default. See
|
||||
* mnt_monitor_keep_data() and mnt_monitor_event_data() for more details.
|
||||
*
|
||||
* Return: 0 on success and <0 on error
|
||||
*/
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_WATCHQUEUE
|
||||
int mnt_monitor_enable_kernelwatch(struct libmnt_monitor *mn, int enable)
|
||||
{
|
||||
return __enable_kernelwatch(mn, enable);
|
||||
}
|
||||
#else
|
||||
int mnt_monitor_enable_kernelwatch(
|
||||
struct libmnt_monitor *mn __attribute__((__unused__)),
|
||||
int enable __attribute__((__unused__)))
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add/Remove monitor entry to/from monitor epoll.
|
||||
*/
|
||||
|
@ -552,7 +796,7 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn,
|
|||
assert(mn);
|
||||
assert(me);
|
||||
|
||||
me->enable = enable ? 1 : 0;
|
||||
me->enabled = enable ? 1 : 0;
|
||||
me->changed = 0;
|
||||
|
||||
if (mn->fd < 0)
|
||||
|
@ -565,7 +809,7 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn,
|
|||
if (fd < 0)
|
||||
goto err;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, " add fd=%d (for %s)", fd, me->path));
|
||||
DBG(MONITOR, ul_debugobj(mn, " add to epoll [%s, fd=%d]", me->path, fd));
|
||||
|
||||
ev.data.ptr = (void *) me;
|
||||
|
||||
|
@ -579,7 +823,7 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn,
|
|||
while (epoll_wait(mn->fd, events, 1, 0) > 0);
|
||||
}
|
||||
} else if (me->fd) {
|
||||
DBG(MONITOR, ul_debugobj(mn, " remove fd=%d (for %s)", me->fd, me->path));
|
||||
DBG(MONITOR, ul_debugobj(mn, " remove from epoll [%s, fd=%d]", me->path, me->fd));
|
||||
if (epoll_ctl(mn->fd, EPOLL_CTL_DEL, me->fd, NULL) < 0) {
|
||||
if (errno != ENOENT)
|
||||
goto err;
|
||||
|
@ -588,6 +832,7 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn,
|
|||
|
||||
return 0;
|
||||
err:
|
||||
DBG(MONITOR, ul_debugobj(mn, " modify epoll faild"));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -626,7 +871,7 @@ int mnt_monitor_close_fd(struct libmnt_monitor *mn)
|
|||
}
|
||||
|
||||
if (mn->fd >= 0) {
|
||||
DBG(MONITOR, ul_debugobj(mn, "closing top-level monitor fd"));
|
||||
DBG(MONITOR, ul_debugobj(mn, "closing top-level epoll"));
|
||||
close(mn->fd);
|
||||
}
|
||||
mn->fd = -1;
|
||||
|
@ -654,29 +899,29 @@ int mnt_monitor_get_fd(struct libmnt_monitor *mn)
|
|||
if (mn->fd >= 0)
|
||||
return mn->fd;
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "create top-level monitor fd"));
|
||||
DBG(MONITOR, ul_debugobj(mn, "creating top-level epoll"));
|
||||
mn->fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (mn->fd < 0)
|
||||
return -errno;
|
||||
|
||||
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "adding monitor entries to epoll (fd=%d)", mn->fd));
|
||||
DBG(MONITOR, ul_debugobj(mn, " adding entries to epoll [epoll fd=%d]", mn->fd));
|
||||
while (monitor_next_entry(mn, &itr, &me) == 0) {
|
||||
if (!me->enable)
|
||||
if (!me->enabled)
|
||||
continue;
|
||||
rc = monitor_modify_epoll(mn, me, TRUE);
|
||||
if (rc)
|
||||
goto err;
|
||||
}
|
||||
|
||||
DBG(MONITOR, ul_debugobj(mn, "successfully created monitor"));
|
||||
DBG(MONITOR, ul_debugobj(mn, " epoll created"));
|
||||
return mn->fd;
|
||||
err:
|
||||
rc = errno ? -errno : -EINVAL;
|
||||
close(mn->fd);
|
||||
mn->fd = -1;
|
||||
DBG(MONITOR, ul_debugobj(mn, "failed to create monitor [rc=%d]", rc));
|
||||
DBG(MONITOR, ul_debugobj(mn, "create epoll failed [rc=%d]", rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -718,8 +963,8 @@ int mnt_monitor_wait(struct libmnt_monitor *mn, int timeout)
|
|||
if (!me)
|
||||
return -EINVAL;
|
||||
|
||||
if (me->opers->op_event_verify == NULL ||
|
||||
me->opers->op_event_verify(mn, me) == 1) {
|
||||
if (me->opers->op_event_read == NULL ||
|
||||
me->opers->op_event_read(mn, me) == 1) {
|
||||
me->changed = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -742,6 +987,7 @@ static struct monitor_entry *get_changed(struct libmnt_monitor *mn)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mnt_monitor_next_change:
|
||||
* @mn: monitor
|
||||
|
@ -754,8 +1000,8 @@ static struct monitor_entry *get_changed(struct libmnt_monitor *mn)
|
|||
* Returns: 0 on success, 1 no change, <0 on error
|
||||
*/
|
||||
int mnt_monitor_next_change(struct libmnt_monitor *mn,
|
||||
const char **filename,
|
||||
int *type)
|
||||
const char **filename,
|
||||
int *type)
|
||||
{
|
||||
int rc;
|
||||
struct monitor_entry *me;
|
||||
|
@ -789,8 +1035,8 @@ int mnt_monitor_next_change(struct libmnt_monitor *mn,
|
|||
if (!me)
|
||||
return -EINVAL;
|
||||
|
||||
if (me->opers->op_event_verify != NULL &&
|
||||
me->opers->op_event_verify(mn, me) != 1)
|
||||
if (me->opers->op_event_read != NULL &&
|
||||
me->opers->op_event_read(mn, me) != 1)
|
||||
me = NULL;
|
||||
}
|
||||
|
||||
|
@ -825,6 +1071,71 @@ int mnt_monitor_event_cleanup(struct libmnt_monitor *mn)
|
|||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_monitor_event_data:
|
||||
* @mn: monitor
|
||||
* @type: wanted MNT_MONITOR_TYPE_* as returned by mnt_monitor_next_change()
|
||||
* @bufsz: returns size of the buffer
|
||||
*
|
||||
* The event data are not maintained by libmount and API/ABI does not guarantee
|
||||
* backward compatibility. Every monitor type returns different data or NULL
|
||||
* (MNT_MONITOR_TYPE_KERNEL does not provide any data).
|
||||
*
|
||||
* Returns: data read from kernel for the last event or NULL
|
||||
*/
|
||||
void *mnt_monitor_event_data(struct libmnt_monitor *mn, int type, ssize_t *bufsz)
|
||||
{
|
||||
struct libmnt_iter itr;
|
||||
struct monitor_entry *me;
|
||||
|
||||
if (!mn || mn->fd < 0)
|
||||
return NULL;;
|
||||
|
||||
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
|
||||
while (monitor_next_entry(mn, &itr, &me) == 0) {
|
||||
if (me->type == type) {
|
||||
if (bufsz)
|
||||
*bufsz = me->bufrsz; /* read() return */
|
||||
if (me->bufrsz > 0)
|
||||
return me->buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mnt_monitor_keep_data:
|
||||
* @mn: monitor
|
||||
* @type: MNT_MONITOR_TYPE_*
|
||||
* @enable: 1 or 0
|
||||
*
|
||||
* Forces monitor to keep event data in memory and do not overwrite it until
|
||||
* not requested by caller (usually by next mnt_monitor_next_change() or
|
||||
* mnt_monitor_event_cleanup()).
|
||||
*
|
||||
* Returns: 0 on success, <0 on error.
|
||||
*/
|
||||
int mnt_monitor_keep_data(struct libmnt_monitor *mn, int type, int enable)
|
||||
{
|
||||
struct libmnt_iter itr;
|
||||
struct monitor_entry *me;
|
||||
|
||||
if (!mn)
|
||||
return -EINVAL;
|
||||
|
||||
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
|
||||
while (monitor_next_entry(mn, &itr, &me) == 0) {
|
||||
if (me->type == type) {
|
||||
me->keep_data = enable;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef TEST_PROGRAM
|
||||
|
||||
static struct libmnt_monitor *create_test_monitor(int argc, char *argv[])
|
||||
|
@ -850,6 +1161,11 @@ static struct libmnt_monitor *create_test_monitor(int argc, char *argv[])
|
|||
warn("failed to initialize kernel monitor");
|
||||
goto err;
|
||||
}
|
||||
} else if (strcmp(argv[i], "kernelwatch") == 0) {
|
||||
if (mnt_monitor_enable_kernelwatch(mn, TRUE)) {
|
||||
warn("failed to initialize kernelwatch monitor");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == 1) {
|
||||
|
@ -963,12 +1279,96 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a monitor, wait for a change and use data
|
||||
*/
|
||||
static int test_data(struct libmnt_test *ts, int argc, char *argv[])
|
||||
{
|
||||
const char *filename;
|
||||
struct libmnt_monitor *mn = create_test_monitor(argc, argv);
|
||||
struct libmnt_fs *fs = NULL;
|
||||
|
||||
if (!mn)
|
||||
return -1;
|
||||
|
||||
mnt_monitor_keep_data(mn, MNT_MONITOR_TYPE_USERSPACE, 1);
|
||||
|
||||
printf("waiting for changes...\n");
|
||||
while (mnt_monitor_wait(mn, -1) > 0) {
|
||||
int type = 0;
|
||||
|
||||
printf("notification detected\n");
|
||||
|
||||
while (mnt_monitor_next_change(mn, &filename, &type) == 0) {
|
||||
char *data, *p;
|
||||
ssize_t sz;
|
||||
size_t chunksz = 0;
|
||||
|
||||
switch (type) {
|
||||
case MNT_MONITOR_TYPE_KERNEL: /* no data, just epoll */
|
||||
printf("epoll event on %s detected\n", filename);
|
||||
break;
|
||||
|
||||
case MNT_MONITOR_TYPE_KERNELWATCH: /* watch_notification */
|
||||
printf("watch-queue event detected\n");
|
||||
|
||||
data = mnt_monitor_event_data(mn, type, &sz);
|
||||
do {
|
||||
int id;
|
||||
|
||||
if (!mnt_kernelwatch_is_valid(data, sz) ||
|
||||
!mnt_kernelwatch_is_mount(data))
|
||||
break;
|
||||
id = mnt_kernelwatch_get_mount_id(data);
|
||||
|
||||
if (!fs)
|
||||
fs = mnt_new_fs();
|
||||
else
|
||||
mnt_reset_fs(fs);
|
||||
mnt_fs_set_id(fs, id);
|
||||
mnt_fs_enable_fsinfo(fs, 1);
|
||||
|
||||
printf(" mount id=%d target=%s\n", id,
|
||||
mnt_fs_get_target(fs));
|
||||
|
||||
data = mnt_kernelwatch_next_data(data, &sz);
|
||||
} while (data);
|
||||
break;
|
||||
|
||||
case MNT_MONITOR_TYPE_USERSPACE: /* inotify */
|
||||
printf("inotify event on %s detected\n", filename);
|
||||
data = mnt_monitor_event_data(mn, type, &sz);
|
||||
for (p = data; p && p < data + sz;
|
||||
p += sizeof(struct inotify_event) + chunksz) {
|
||||
struct inotify_event *e =
|
||||
(struct inotify_event *) p;
|
||||
|
||||
printf(" mask=0x%x, name=%s\n",
|
||||
e->mask, e->len ? e->name : "");
|
||||
|
||||
chunksz = e->len;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown event\n");
|
||||
break;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
mnt_unref_fs(fs);
|
||||
mnt_unref_monitor(mn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct libmnt_test tss[] = {
|
||||
{ "--epoll", test_epoll, "<userspace kernel ...> monitor in epoll" },
|
||||
{ "--epoll-clean", test_epoll_cleanup, "<userspace kernel ...> monitor in epoll and clean events" },
|
||||
{ "--wait", test_wait, "<userspace kernel ...> monitor wait function" },
|
||||
{ "--data", test_data, "<userspace kernel ...> notification kernel data" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
# include <linux/fsinfo.h> /* for struct fsinfo_params */
|
||||
#endif
|
||||
|
||||
#include "c.h"
|
||||
#include "list.h"
|
||||
#include "debug.h"
|
||||
|
@ -47,6 +51,7 @@
|
|||
#define MNT_DEBUG_BTRFS (1 << 12)
|
||||
#define MNT_DEBUG_LOOP (1 << 13)
|
||||
#define MNT_DEBUG_VERITY (1 << 14)
|
||||
#define MNT_DEBUG_WATCH (1 << 15)
|
||||
|
||||
#define MNT_DEBUG_ALL 0xFFFF
|
||||
|
||||
|
@ -144,6 +149,11 @@ extern int __mnt_table_is_fs_mounted( struct libmnt_table *tb,
|
|||
struct libmnt_fs *fstab_fs,
|
||||
const char *tgt_prefix);
|
||||
|
||||
extern struct libmnt_fs *mnt_table_find_triplet(struct libmnt_table *tb,
|
||||
const char *srcpath, const char *target,
|
||||
const char *root, int direction);
|
||||
|
||||
|
||||
/*
|
||||
* Generic iterator
|
||||
*/
|
||||
|
@ -198,6 +208,7 @@ struct libmnt_fs {
|
|||
char *optstr; /* fstab[4], merged options */
|
||||
char *vfs_optstr; /* mountinfo[6]: fs-independent (VFS) options */
|
||||
char *opt_fields; /* mountinfo[7]: optional fields */
|
||||
unsigned int propagation; /* parsed opt_fields */
|
||||
char *fs_optstr; /* mountinfo[11]: fs-dependent options */
|
||||
char *user_optstr; /* userspace mount options */
|
||||
char *attrs; /* mount attributes */
|
||||
|
@ -217,6 +228,9 @@ struct libmnt_fs {
|
|||
char *comment; /* fstab comment */
|
||||
|
||||
void *userdata; /* library independent data */
|
||||
|
||||
unsigned int fsinfo_enabled : 1;
|
||||
unsigned int fsinfo_done; /* FSINFO_ATTR_* mask */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -413,6 +427,26 @@ extern int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
|
|||
__attribute__((nonnull(1)));
|
||||
extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype)
|
||||
__attribute__((nonnull(1)));
|
||||
extern int __mnt_fs_set_propagation_from_string(struct libmnt_fs *fs, const char *str)
|
||||
__attribute__((nonnull(1)));
|
||||
extern int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf);
|
||||
extern int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs, int request);
|
||||
|
||||
/* fsinfo.c */
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
extern int mnt_get_target_id(const char *path, unsigned int *id, unsigned int flags);
|
||||
extern int mnt_fsinfo(const char *query,
|
||||
struct fsinfo_params *params,
|
||||
size_t params_size,
|
||||
char *buf,
|
||||
size_t *bufsz);
|
||||
extern int mnt_fsinfo_get_children(unsigned int id,
|
||||
struct fsinfo_mount_child **mounts,
|
||||
size_t *count);
|
||||
extern int mnt_fsinfo_get_mounts(unsigned int id,
|
||||
struct fsinfo_mount_child **mounts,
|
||||
size_t *count);
|
||||
#endif
|
||||
|
||||
/* context.c */
|
||||
extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o);
|
||||
|
|
|
@ -63,7 +63,7 @@ int is_mountinfo(struct libmnt_table *tb)
|
|||
return 0;
|
||||
|
||||
fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
|
||||
if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_root(fs))
|
||||
if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_id(fs))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -444,7 +444,6 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
|
|||
{
|
||||
if (!tb || !fs)
|
||||
return -EINVAL;
|
||||
|
||||
if (fs->tab)
|
||||
return -EBUSY;
|
||||
|
||||
|
@ -453,8 +452,12 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
|
|||
fs->tab = tb;
|
||||
tb->nents++;
|
||||
|
||||
DBG(TAB, ul_debugobj(tb, "add entry: %s %s",
|
||||
mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
|
||||
/* don't use mnt_fs_get_() in debug to avoid fsinfo() calls in on-emand
|
||||
* mode */
|
||||
if (!fs->target || !fs->source)
|
||||
DBG(TAB, ul_debugobj(tb, "add entry: id=%u parent=%u", fs->id, fs->parent));
|
||||
else
|
||||
DBG(TAB, ul_debugobj(tb, "add entry: %s %s", fs->source, fs->target));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -630,6 +633,8 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
|
|||
}
|
||||
}
|
||||
|
||||
DBG(TAB, ul_debugobj(tb, "expected root id=%d", root_id));
|
||||
|
||||
/* go to the root node by "parent_id -> id" relation */
|
||||
while (*root) {
|
||||
struct libmnt_fs *x = get_parent_fs(tb, *root);
|
||||
|
@ -663,8 +668,8 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
|
|||
if (!tb || !itr || !parent || !is_mountinfo(tb))
|
||||
return -EINVAL;
|
||||
|
||||
DBG(TAB, ul_debugobj(tb, "lookup next child of '%s'",
|
||||
mnt_fs_get_target(parent)));
|
||||
DBG(TAB, ul_debugobj(tb, "lookup next child of '%d'",
|
||||
mnt_fs_get_id(parent)));
|
||||
|
||||
parent_id = mnt_fs_get_id(parent);
|
||||
|
||||
|
@ -1360,6 +1365,39 @@ struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *sourc
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns fs whicg match srcpath, target and root. The function does not
|
||||
* canonicalize, do not covert tags, etc.
|
||||
*/
|
||||
struct libmnt_fs *mnt_table_find_triplet(struct libmnt_table *tb,
|
||||
const char *srcpath, const char *target,
|
||||
const char *root, int direction)
|
||||
{
|
||||
struct libmnt_fs *fs = NULL;
|
||||
struct libmnt_iter itr;
|
||||
|
||||
if (!tb || !target || !*target
|
||||
|| !srcpath || !*srcpath
|
||||
|| !root || !*root)
|
||||
return NULL;
|
||||
|
||||
if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
|
||||
return NULL;
|
||||
|
||||
mnt_reset_iter(&itr, direction);
|
||||
|
||||
while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
|
||||
const char *r = mnt_fs_get_root(fs);
|
||||
|
||||
if (r && strcmp(r, root) == 0
|
||||
&& mnt_fs_streq_target(fs, target)
|
||||
&& mnt_fs_streq_srcpath(fs, srcpath))
|
||||
return fs;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mnt_table_find_devno
|
||||
* @tb: /proc/self/mountinfo
|
||||
|
@ -1941,6 +1979,39 @@ done:
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
static int test_fsinfo(struct libmnt_test *ts, int argc, char *argv[])
|
||||
{
|
||||
struct libmnt_table *tb = NULL;
|
||||
struct libmnt_iter *itr = NULL;
|
||||
struct libmnt_fs *fs;
|
||||
int rc = 0;
|
||||
|
||||
tb = mnt_new_table();
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
|
||||
mnt_table_set_parser_errcb(tb, parser_errcb);
|
||||
|
||||
rc = mnt_table_parse_fsinfo(tb);
|
||||
if (rc) {
|
||||
fprintf(stderr, "fsinfo parsing failed\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
itr = mnt_new_iter(MNT_ITER_FORWARD);
|
||||
if (!itr)
|
||||
goto done;
|
||||
|
||||
while(mnt_table_next_fs(tb, itr, &fs) == 0)
|
||||
mnt_fs_print_debug(fs, stdout);
|
||||
done:
|
||||
mnt_free_iter(itr);
|
||||
mnt_unref_table(tb);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[])
|
||||
{
|
||||
struct libmnt_table *tb;
|
||||
|
@ -2196,6 +2267,9 @@ int main(int argc, char *argv[])
|
|||
{ "--find-mountpoint", test_find_mountpoint, "<path>" },
|
||||
{ "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
|
||||
{ "--is-mounted", test_is_mounted, "<fstab> check what from fstab is already mounted" },
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
{ "--fsinfo", test_fsinfo, "read mount table by fsinfo syscall"},
|
||||
#endif
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ struct libmnt_parser {
|
|||
char *buf; /* buffer (the current line content) */
|
||||
size_t bufsiz; /* size of the buffer */
|
||||
size_t line; /* current line */
|
||||
|
||||
pid_t tid; /* task ID */
|
||||
|
||||
unsigned int has_root_fs : 1; /* root fs aread parsed */
|
||||
};
|
||||
|
||||
static void parser_cleanup(struct libmnt_parser *pa)
|
||||
|
@ -245,8 +249,13 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, const char *s)
|
|||
DBG(TAB, ul_debug("mountinfo parse error: separator not found"));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (p > s + 1)
|
||||
if (p > s + 1) {
|
||||
fs->opt_fields = strndup(s + 1, p - s - 1);
|
||||
if (!fs->opt_fields)
|
||||
goto fail;
|
||||
|
||||
__mnt_fs_set_propagation_from_string(fs, fs->opt_fields);
|
||||
}
|
||||
|
||||
s = skip_separator(p + 3);
|
||||
|
||||
|
@ -660,49 +669,188 @@ done:
|
|||
}
|
||||
|
||||
static int kernel_fs_postparse(struct libmnt_table *tb,
|
||||
struct libmnt_fs *fs, pid_t *tid,
|
||||
const char *filename)
|
||||
struct libmnt_fs *fs,
|
||||
struct libmnt_parser *pa)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *src = mnt_fs_get_srcpath(fs);
|
||||
|
||||
/* This is a filesystem description from /proc, so we're in some process
|
||||
* namespace. Let's remember the process PID.
|
||||
*/
|
||||
if (filename && *tid == -1)
|
||||
*tid = path_to_tid(filename);
|
||||
if (pa->filename && pa->tid == 0)
|
||||
pa->tid = path_to_tid(pa->filename);
|
||||
|
||||
fs->tid = *tid;
|
||||
fs->tid = pa->tid;
|
||||
|
||||
/*
|
||||
* Convert obscure /dev/root to something more usable
|
||||
*/
|
||||
if (src && strcmp(src, "/dev/root") == 0) {
|
||||
char *real = NULL;
|
||||
if (!pa->has_root_fs) {
|
||||
const char *src = mnt_fs_get_srcpath(fs);
|
||||
|
||||
rc = mnt_guess_system_root(fs->devno, tb->cache, &real);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (src && strcmp(src, "/dev/root") == 0) {
|
||||
char *real = NULL;
|
||||
|
||||
if (rc == 0 && real) {
|
||||
DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real));
|
||||
rc = __mnt_fs_set_source_ptr(fs, real);
|
||||
rc = mnt_guess_system_root(mnt_fs_get_devno(fs), tb->cache, &real);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
} else if (rc == 1) {
|
||||
/* mnt_guess_system_root() returns 1 if not able to convert to
|
||||
* the real devname; ignore this problem */
|
||||
rc = 0;
|
||||
if (rc == 0 && real) {
|
||||
DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real));
|
||||
rc = __mnt_fs_set_source_ptr(fs, real);
|
||||
|
||||
} else if (rc == 1) {
|
||||
/* mnt_guess_system_root() returns 1 if not
|
||||
* able to convert to the real devname; ignore
|
||||
* this problem */
|
||||
rc = 0;
|
||||
}
|
||||
pa->has_root_fs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
static int parse_fsinfo_init_fs(
|
||||
struct libmnt_table *tb,
|
||||
struct libmnt_fs *fs,
|
||||
unsigned int id,
|
||||
struct libmnt_fs *parent,
|
||||
struct libmnt_parser *pa)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
fs->flags |= MNT_FS_KERNEL;
|
||||
fs->id = id;
|
||||
fs->parent = parent ? parent->id : 0;
|
||||
|
||||
mnt_fs_enable_fsinfo(fs, 1);
|
||||
|
||||
if (tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data))
|
||||
rc = 1; /* filtered out by callback... */
|
||||
if (!rc)
|
||||
rc = kernel_fs_postparse(tb, fs, pa);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int table_parse_fetch_mounts(struct libmnt_table *tb,
|
||||
unsigned int id, struct libmnt_fs *parent,
|
||||
struct libmnt_parser *pa)
|
||||
{
|
||||
struct libmnt_fs *fs = NULL;
|
||||
struct fsinfo_mount_child *mounts = NULL;
|
||||
size_t i, count;
|
||||
int rc = 0;
|
||||
|
||||
assert(id);
|
||||
|
||||
/* add root fs */
|
||||
if (!parent) {
|
||||
DBG(TAB, ul_debugobj(tb, "fsinfo: add root FS"));
|
||||
parent = mnt_new_fs();
|
||||
if (!parent) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = parse_fsinfo_init_fs(tb, parent, id, NULL, pa);
|
||||
if (!rc)
|
||||
rc = mnt_table_add_fs(tb, parent);
|
||||
if (rc)
|
||||
goto out;
|
||||
pa->has_root_fs = 1;
|
||||
}
|
||||
|
||||
/* mount list */
|
||||
rc = mnt_fsinfo_get_mounts(id, &mounts, &count);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
if (!count)
|
||||
goto out;
|
||||
|
||||
/* add childern to the table */
|
||||
for (i = 0; i < count; i++) {
|
||||
if (!fs) {
|
||||
fs = mnt_new_fs();
|
||||
if (!fs) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rc = parse_fsinfo_init_fs(tb, fs, mounts[i].mnt_id, parent, pa);
|
||||
if (!rc)
|
||||
rc = mnt_table_add_fs(tb, fs);
|
||||
if (rc) {
|
||||
if (rc > 0) {
|
||||
mnt_reset_fs(fs);
|
||||
assert(fs->refcount == 1);
|
||||
continue; /* recoverable error, reuse fs*/
|
||||
}
|
||||
|
||||
mnt_unref_fs(fs);
|
||||
goto out; /* fatal error */
|
||||
}
|
||||
|
||||
mnt_unref_fs(fs);
|
||||
fs = NULL;
|
||||
}
|
||||
out:
|
||||
free(mounts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __table_parse_fsinfo(struct libmnt_table *tb)
|
||||
{
|
||||
unsigned int id;
|
||||
int rc;
|
||||
struct libmnt_parser pa = { .filename = _PATH_PROC_MOUNTINFO };
|
||||
|
||||
DBG(TAB, ul_debugobj(tb, "fsinfo parse: start parsing [entries=%d, filter=%s]",
|
||||
mnt_table_get_nents(tb), tb->fltrcb ? "yes" : "not"));
|
||||
|
||||
rc = mnt_get_target_id("/", &id, AT_NO_AUTOMOUNT);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
rc = table_parse_fetch_mounts(tb, id, NULL, &pa);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
DBG(TAB, ul_debugobj(tb, "fsinfo parse : stop parsing (%d entries)",
|
||||
mnt_table_get_nents(tb)));
|
||||
return 0;
|
||||
err:
|
||||
DBG(TAB, ul_debugobj(tb, "fsinfo parse: error (rc=%d)", rc));
|
||||
return rc;
|
||||
}
|
||||
#endif /* USE_LIBMOUNT_SUPPORT_FSINFO */
|
||||
|
||||
/**
|
||||
* mnt_table_parse_fsinfo:
|
||||
* @tb: libmnt_table instance
|
||||
*
|
||||
* Read mount table by fsinfo() kernel interface. Note that all filesystems in the
|
||||
* table have enabled on-demand fsinfo().
|
||||
*
|
||||
* Returns: 0 on success, negative number in case of error.
|
||||
*/
|
||||
int mnt_table_parse_fsinfo(struct libmnt_table *tb)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
|
||||
assert(tb);
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
if (mnt_has_fsinfo())
|
||||
rc = __table_parse_fsinfo(tb);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
|
||||
{
|
||||
int rc = -1;
|
||||
int flags = 0;
|
||||
pid_t tid = -1;
|
||||
struct libmnt_fs *fs = NULL;
|
||||
struct libmnt_parser pa = { .line = 0 };
|
||||
|
||||
|
@ -740,7 +888,7 @@ static int __table_parse_stream(struct libmnt_table *tb, FILE *f, const char *fi
|
|||
fs->flags |= flags;
|
||||
|
||||
if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) {
|
||||
rc = kernel_fs_postparse(tb, fs, &tid, filename);
|
||||
rc = kernel_fs_postparse(tb, fs, &pa);
|
||||
if (rc)
|
||||
mnt_table_remove_fs(tb, fs);
|
||||
}
|
||||
|
@ -1169,11 +1317,10 @@ int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename)
|
|||
*
|
||||
* Returns modified filesystem (from @tb) or NULL.
|
||||
*/
|
||||
static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf)
|
||||
static struct libmnt_fs *mnt_table_merge_utab_fs(struct libmnt_table *tb, struct libmnt_fs *uf)
|
||||
{
|
||||
struct libmnt_fs *fs;
|
||||
struct libmnt_iter itr;
|
||||
const char *optstr, *src, *target, *root, *attrs;
|
||||
const char *src, *target, *root;
|
||||
|
||||
if (!tb || !uf)
|
||||
return NULL;
|
||||
|
@ -1182,34 +1329,15 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct
|
|||
|
||||
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))
|
||||
if (!src || !target || !root)
|
||||
return NULL;
|
||||
|
||||
mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
|
||||
|
||||
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
|
||||
const char *r = mnt_fs_get_root(fs);
|
||||
|
||||
if (fs->flags & MNT_FS_MERGED)
|
||||
continue;
|
||||
|
||||
if (r && strcmp(r, root) == 0
|
||||
&& mnt_fs_streq_target(fs, target)
|
||||
&& mnt_fs_streq_srcpath(fs, src))
|
||||
break;
|
||||
}
|
||||
|
||||
fs = mnt_table_find_triplet(tb, src, target, root, MNT_ITER_BACKWARD);
|
||||
if (fs) {
|
||||
DBG(TAB, ul_debugobj(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;
|
||||
|
||||
mnt_fs_merge_utab(fs, uf);
|
||||
DBG(TAB, ul_debugobj(tb, "found fs:"));
|
||||
DBG(TAB, mnt_fs_print_debug(fs, stderr));
|
||||
}
|
||||
|
@ -1305,11 +1433,10 @@ read_utab:
|
|||
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);
|
||||
while (mnt_table_next_fs(u_tb, &itr, &u_fs) == 0)
|
||||
mnt_table_merge_utab_fs(tb, u_fs);
|
||||
}
|
||||
|
||||
|
||||
if (priv_utab)
|
||||
mnt_unref_table(u_tb);
|
||||
return 0;
|
||||
|
|
|
@ -40,6 +40,9 @@ static const char *lib_features[] = {
|
|||
#ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
|
||||
"namespaces",
|
||||
#endif
|
||||
#ifdef USE_LIBMOUNT_SUPPORT_FSINFO
|
||||
"fsinfo",
|
||||
#endif
|
||||
#if !defined(NDEBUG)
|
||||
"assert", /* libc assert.h stuff */
|
||||
#endif
|
||||
|
|
|
@ -81,7 +81,8 @@ enum {
|
|||
enum {
|
||||
TABTYPE_FSTAB = 1,
|
||||
TABTYPE_MTAB,
|
||||
TABTYPE_KERNEL
|
||||
TABTYPE_KERNEL,
|
||||
TABTYPE_FSINFO
|
||||
};
|
||||
|
||||
/* column names */
|
||||
|
@ -844,6 +845,9 @@ static struct libmnt_table *parse_tabfiles(char **files,
|
|||
|
||||
rc = mnt_table_parse_file(tb, path);
|
||||
break;
|
||||
case TABTYPE_FSINFO:
|
||||
rc = mnt_table_parse_fsinfo(tb);
|
||||
break;
|
||||
}
|
||||
if (rc) {
|
||||
mnt_unref_table(tb);
|
||||
|
@ -891,7 +895,7 @@ static int tab_is_tree(struct libmnt_table *tb)
|
|||
|
||||
rc = (mnt_table_next_fs(tb, itr, &fs) == 0 &&
|
||||
mnt_fs_is_kernel(fs) &&
|
||||
mnt_fs_get_root(fs));
|
||||
mnt_fs_get_id(fs));
|
||||
|
||||
mnt_free_iter(itr);
|
||||
return rc;
|
||||
|
@ -1220,7 +1224,9 @@ static void __attribute__((__noreturn__)) usage(void)
|
|||
" (includes user space mount options)\n"), out);
|
||||
fputs(_(" -k, --kernel search in kernel table of mounted\n"
|
||||
" filesystems (default)\n"), out);
|
||||
fputs(_(" --fsinfo like -k, but use fsinfo kernel interface\n"), out);
|
||||
fputc('\n', out);
|
||||
|
||||
fputs(_(" -p, --poll[=<list>] monitor changes in table of mounted filesystems\n"), out);
|
||||
fputs(_(" -w, --timeout <num> upper limit in milliseconds that --poll will block\n"), out);
|
||||
fputc('\n', out);
|
||||
|
@ -1294,7 +1300,8 @@ int main(int argc, char *argv[])
|
|||
FINDMNT_OPT_TREE,
|
||||
FINDMNT_OPT_OUTPUT_ALL,
|
||||
FINDMNT_OPT_PSEUDO,
|
||||
FINDMNT_OPT_REAL
|
||||
FINDMNT_OPT_REAL,
|
||||
FINDMNT_OPT_FSINFO
|
||||
};
|
||||
|
||||
static const struct option longopts[] = {
|
||||
|
@ -1307,6 +1314,7 @@ int main(int argc, char *argv[])
|
|||
{ "evaluate", no_argument, NULL, 'e' },
|
||||
{ "first-only", no_argument, NULL, 'f' },
|
||||
{ "fstab", no_argument, NULL, 's' },
|
||||
{ "fsinfo", no_argument, NULL, FINDMNT_OPT_FSINFO },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "invert", no_argument, NULL, 'i' },
|
||||
{ "json", no_argument, NULL, 'J' },
|
||||
|
@ -1512,7 +1520,9 @@ int main(int argc, char *argv[])
|
|||
case FINDMNT_OPT_REAL:
|
||||
flags |= FL_REAL;
|
||||
break;
|
||||
|
||||
case FINDMNT_OPT_FSINFO:
|
||||
tabtype = TABTYPE_FSINFO;
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
case 'V':
|
||||
|
@ -1613,7 +1623,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
mnt_table_set_cache(tb, cache);
|
||||
|
||||
if (tabtype != TABTYPE_KERNEL)
|
||||
if (tabtype != TABTYPE_KERNEL && tabtype != TABTYPE_FSINFO)
|
||||
cache_set_targets(cache);
|
||||
}
|
||||
|
||||
|
@ -1699,7 +1709,7 @@ int main(int argc, char *argv[])
|
|||
rc = add_matching_lines(tb, table, direction);
|
||||
|
||||
if (rc != 0
|
||||
&& tabtype == TABTYPE_KERNEL
|
||||
&& (tabtype == TABTYPE_KERNEL || tabtype == TABTYPE_FSINFO)
|
||||
&& (flags & FL_NOSWAPMATCH)
|
||||
&& !(flags & FL_STRICTTARGET)
|
||||
&& get_match(COL_TARGET)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue