Compare commits

...
Sign in to create a new pull request.

44 commits

Author SHA1 Message Date
Ian Kent
117a4baffd libmount: get all mount ids at once 2020-07-21 15:19:04 +02:00
Ian Kent
7003b502b6 libmount: add function mnt_fsinfo_get_mounts()
Add a function that gets all mounts at once using FSINFO_ATTR_MOUNT_ALL.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-07-21 15:19:04 +02:00
Ian Kent
e523affd6f libmount: fix vfs_options handling
The vfs_options handling isn't mapping the options properly for
libmount and the previous change I did for MS_RELATIME simply
doesn't work either.

So add an options mapping table to deal withi this.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-07-21 15:19:04 +02:00
Ian Kent
c791058243 libmount: fix mnt_has_fsinfo()
When USE_LIBMOUNT_SUPPORT_FSINFO is set function mnt_has_fsinfo() doesn't
set have_fsinfo to other than -1 so the fsinfo() system call is made
on every invocation and -1 is always returned.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-07-21 15:19:04 +02:00
Karel Zak
bf715d42af libmount: add watch-queue sample
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-22 17:03:58 +02:00
Karel Zak
7baaf3c251 libmount: (kernelwatch) fix and extend ID use
We need to differentiate between ID (of the node) which triggered
the event and child which is affected by the change.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-22 17:01:53 +02:00
Karel Zak
522d134700 libmount: (monitor) print FS target in test
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-22 15:57:40 +02:00
Karel Zak
5a9a9ab7ad libmount: add mnt_fs_set_id()
This function is necessary to force fsinfo stuff follow specific FS (id).

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-22 15:56:13 +02:00
Karel Zak
f3d523724b libmount: add kernelwatch API
This is very simple interface to hide linux/watch_queue.h details when
we use data from notification read(). The interface is independent on
libmnt_monitor.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-22 15:45:19 +02:00
Karel Zak
dbf6243597 libmount: (monitor) add "kernelwatch" monitor type
This monitor uses watch_mount() kernel API to get details about
modified VFS nodes. The patch implements only very basic
functionality. In the next commits we need  to support watch_sb() and
connect all with libmount fsinfo stuff to hid all details.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-19 14:08:56 +02:00
Karel Zak
386d42fa5d libmount: (monitor) add functions to return event data
* mnt_monitor_keep_data() to enable this feture
* mnt_monitor_event_data() to return data after a change

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-19 09:19:07 +02:00
Karel Zak
e1f7681834 libmount: (monitor) fix typo
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-17 18:15:21 +02:00
Karel Zak
98fd10f63f libmount: (monitor) don't repeat inotify_rm_watch() call
The directory /run/mount watch generates more vents, call
inotify_rm_watch() only first time for the directory watch.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-17 18:08:56 +02:00
Karel Zak
cb201c9c8b libmount: (monitor) cleanup debug messages
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-17 18:00:53 +02:00
Karel Zak
8386d586b4 libmount: move notification buffer to monitor entry
Don't keep the buffer as local variable for read() operation, but keep
it in memory for later use. We will use it for mount-watch-queue
later.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-10 16:15:16 +02:00
Karel Zak
b372c1b637 libmount: fix compiler warning [-Wunused-function]
libmount/src/tab_parse.c:715:12: warning: ‘parse_fsinfo_init_fs’ defined but not used [-Wunused-function]

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-06-10 16:09:48 +02:00
Karel Zak
c0cffb7c0f libmount: don't use eplty strings from fsinfo
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 16:26:50 +02:00
Karel Zak
d15875e29c libmount: mnt_table_parse_fsinfo() refactoring
- add root FS
- reuse FS initialization code

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 16:15:09 +02:00
Karel Zak
783d41e43d libmount: cleanup dbg messages and tree functions
- don't use mnt_fs_get_root() (mnt_fs_get_id() is cheaper)
- don't use in DBG() stuff which triggers fsinfo()

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 16:13:20 +02:00
Karel Zak
25f56a9455 libmount: be more verbose about fsinfo rc
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 16:12:29 +02:00
Karel Zak
abd584bee0 findmnt: use ID to detect tree-ability
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 16:11:44 +02:00
Karel Zak
4b5ec23c5a libmount: optimize kernel_fs_postparse use
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 15:01:39 +02:00
Karel Zak
b8ed57bdf3 libmount: don't trigger fsinfo() by debug message
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 15:01:05 +02:00
Karel Zak
1728c6238d libmount: create optstr on-demand 2020-04-24 15:00:26 +02:00
Karel Zak
1cdddcfa32 findmnt: fix cache read for fsinfo()
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 14:58:48 +02:00
Karel Zak
464ee2c8fe libmount: read tid only once
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 13:00:23 +02:00
Karel Zak
aa312e00ae findmnt: add --fsinfo
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 12:59:46 +02:00
Ian Kent
a31dcde3f9 libmount: add mnt_table_parse_fsinfo()
The new public API function to read complete mount table by fsinfo().
For tests use:

	$ make test_mount_tab
	$ ./test_mount_tab --fsinfo

[kzak@redhat.com: - add commit message, cleanup function names, etc.]

Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-24 12:19:50 +02:00
Karel Zak
0f728eee37 libmount: fix fs->fsinfo_done use
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-23 16:15:44 +02:00
Karel Zak
446c847415 libmount: add mnt_has_fsinfo()
* add mnt_has_fsinfo()
* cleanup stuff around fs->fsinfo_enabled
* probe for fsinfo() in umount in effective way

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-23 15:37:13 +02:00
Karel Zak
7ecabf3a9c libmount: implement on-demand fsinfo() call for libmnt_fs
Use mnt_fs_enable_fsinfo() to enable fsinfo() calls.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-23 11:26:22 +02:00
Karel Zak
bb881ebf61 libmount: make fsinfo optional
* add USE_LIBMOUNT_SUPPORT_FSINFO to build-system with dependence on linux/fsinfo.h
* use #ifdefs to enable fsinfo code only if possible
* add "fsinfo" to mount --version output

* for non-distro kernel headers use:

   CFLAGS="-I /usr/src/$(uname -r)/usr/include" ./configure

 and see ./configure script output and config.h for more details.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-20 18:06:02 +02:00
Karel Zak
3fd6302906 libmount: move fsinfo calls to separate file
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-20 17:37:59 +02:00
Ian Kent
a41fc98ad0 libmount: dont use fd in mnt_fs_fetch_fsinfo()
Using open(2) will trigger autofs direct and offset mounts so it
shouldn't be used in mnt_fs_fetch_fsinfo().

The mount id is a better choice here.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Ian Kent
e6a848597c libmount: fix fsinfo_buf2fs() compile warning
The fsinfo_buf2fs() bufsz parameter isn't used and generates a compile
warning.

But the following len parameter has already been checked to enusre it's
smaller that the buf size so remove the unused parameter.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Ian Kent
3708880a48 libmount: fix merge of options in fsinfo_buf2fs()
The mount attribute MOUNT_ATTR_RELATIME is defined as 0 in the fsinfo()
header files.

The vfs option relatime is included in the vfs options proc string only
if noatime and strictatime are not set so this option gets ommitted when
using fsinfo().

This maybe should be fixed in mnt_optstr_apply_flags() but it's a special
case that may cause side effects for non fsinfo() use. Or perhaps fsinfo()
should set the flag to its MS_* counterpart similar to the other MOUNT_ATTR_*
flags, not sure?

In any case I needed this set so added this hack.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Ian Kent
de1aa60bdb libmount: fix incorrect error check in mnt_fs_fetch_fsinfo()
Umm ... looks wrong and causes false -ENAMETOOLONG error returns.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Ian Kent
27c6c71003 libmount-fix-type-mismatch-in-mnt_fs_fetch_fsinfo
Using ARRAY_SIZE() causes a sign comparision warning, change the
counter to an unsigned int.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Ian Kent
5fe931f872 libmount: update fsinfo includes
After a make in the kernel source tree all that's needed to build against
a kernel with fsinfo() is:
CFLAGS="-I <kernel source base>/usr/include"

Also, using the above include path will define __NR_fsinfo so checking
if it's defined and seting it to -1 is enough to handle kernels without
fsinfo().

And move the include directives to the top of fs.c.

Signed-off-by: Ian Kent <raven@themaw.net>
2020-04-20 16:19:36 +02:00
Karel Zak
f6e42e342d libmount: (experimental) add fsinfo() support for umount
This patch allows to replace mountinfo parsing by fsinfo() call, it
means we do not have to parse all irrelevant content in mountinfo, but
get information about desired mountpoint.

Use (for example):

 CFLAGS=-I/usr/src/kernels/5.6.0-0.rc6.git0.1.fsinfo.fc33.x86_64/include/uapi/ \
        -I/usr/src/kernels/5.6.0-0.rc6.git0.1.fsinfo.fc33.x86_64/include \
        ./configure

to point to your experimental kernel headers.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-03 12:44:19 +02:00
Karel Zak
3ee27dc5b3 libmount: initialize utab path only once
The function mnt_context_utab_writable() initializes paths in the
context and then we can use it on many places.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-03 12:42:38 +02:00
Karel Zak
01aede7316 libmount: use separate function to search in utab
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-02 14:08:09 +02:00
Karel Zak
2bf3ef7666 libmount: move utab merge code to mnt_fs_merge_utab()
Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-02 12:40:52 +02:00
Karel Zak
8aa73754e0 libmount: keep propagation as flag in libmnt_fs
Let's generate the flag when parse mountinfo rather than later in
mnt_fs_get_propagation(). It's more efficient (we parse the optimal
mountinfo field only once) and it allows to set propagation from
another source in future.

Signed-off-by: Karel Zak <kzak@redhat.com>
2020-04-02 11:28:05 +02:00
19 changed files with 1759 additions and 145 deletions

View file

@ -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])

View file

@ -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

View file

@ -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

View 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)

View 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");
}

View file

@ -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 \

View file

@ -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)

View file

@ -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, &params, 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
View 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,
&params, 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, &params, 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("/", &params, sizeof(params), (char *)&info, &sz);
if (rc != 1)
have_fsinfo = 1;
else
have_fsinfo = 0;
}
return have_fsinfo;
#else
return 0;
#endif
}

View file

@ -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
View 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 */

View file

@ -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 */

View file

@ -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;

View file

@ -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 }
};

View file

@ -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);

View file

@ -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 }
};

View file

@ -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;

View file

@ -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

View file

@ -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)) {