From 8aa73754e0f514fa7796c3f9ffc362fd3c134fe2 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 2 Apr 2020 11:28:05 +0200 Subject: [PATCH 01/44] 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 --- libmount/src/fs.c | 46 ++++++++++++++++++++-------------------- libmount/src/mountP.h | 3 +++ libmount/src/tab_parse.c | 7 +++++- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index bcc827348..a614a48bd 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -557,39 +557,39 @@ 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) - return -EINVAL; + if (fs && flags) + *flags = fs->propagation; - *flags = 0; - - if (!fs->opt_fields) - return 0; - - /* - * The optional fields format is incompatible with mount options - * ... we have to parse the field here. - */ - *flags |= strstr(fs->opt_fields, "shared:") ? MS_SHARED : MS_PRIVATE; - - if (strstr(fs->opt_fields, "master:")) - *flags |= MS_SLAVE; - if (strstr(fs->opt_fields, "unbindable")) - *flags |= MS_UNBINDABLE; - - return 0; + return fs ? 0 : -EINVAL; } /** diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 9e7ad2b71..98f987489 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -198,6 +198,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 */ @@ -413,6 +414,8 @@ 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))); /* context.c */ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index ffcf24579..12a179ab9 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -245,8 +245,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); From 2bf3ef76665b5779de98771f209e12faca01d4ab Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 2 Apr 2020 12:40:52 +0200 Subject: [PATCH 02/44] libmount: move utab merge code to mnt_fs_merge_utab() Signed-off-by: Karel Zak --- libmount/src/fs.c | 27 +++++++++++++++++++++++++++ libmount/src/mountP.h | 1 + libmount/src/tab_parse.c | 24 +++++++++--------------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index a614a48bd..22c9ca71c 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1622,3 +1622,30 @@ 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; +} diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 98f987489..007b22420 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -416,6 +416,7 @@ 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); /* context.c */ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index 12a179ab9..2f5c6759a 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -1174,11 +1174,11 @@ 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; @@ -1187,21 +1187,20 @@ 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); + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *r; if (fs->flags & MNT_FS_MERGED) continue; + r = mnt_fs_get_root(fs); if (r && strcmp(r, root) == 0 && mnt_fs_streq_target(fs, target) && mnt_fs_streq_srcpath(fs, src)) @@ -1210,11 +1209,7 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct 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)); } @@ -1310,11 +1305,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; From 01aede7316ee8d4ef458633a4f87dd335589bc1d Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 2 Apr 2020 14:08:09 +0200 Subject: [PATCH 03/44] libmount: use separate function to search in utab Signed-off-by: Karel Zak --- libmount/src/mountP.h | 5 +++++ libmount/src/tab.c | 33 +++++++++++++++++++++++++++++++++ libmount/src/tab_parse.c | 17 +---------------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 007b22420..f79ae1e65 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -144,6 +144,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 */ diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 1633d4231..cdce06160 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -1360,6 +1360,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 diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index 2f5c6759a..2bca790ec 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -1177,7 +1177,6 @@ int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename) 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 *src, *target, *root; if (!tb || !uf) @@ -1192,21 +1191,7 @@ static struct libmnt_fs *mnt_table_merge_utab_fs(struct libmnt_table *tb, struct 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; - - if (fs->flags & MNT_FS_MERGED) - continue; - - r = mnt_fs_get_root(fs); - 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_merge_utab(fs, uf); From 3ee27dc5b3bd65af8c8db532fe0dbfc438815f71 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 3 Apr 2020 12:41:54 +0200 Subject: [PATCH 04/44] 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 --- libmount/src/context_umount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index 733e9194d..787c3a2a1 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.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; From f6e42e342d53e659319045bb9268bb70e6c9c5d3 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 3 Apr 2020 12:44:19 +0200 Subject: [PATCH 05/44] 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 --- libmount/src/context_umount.c | 43 ++++++++++ libmount/src/fs.c | 149 ++++++++++++++++++++++++++++++++++ libmount/src/mountP.h | 1 + 3 files changed, 193 insertions(+) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index 787c3a2a1..b39573564 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -334,6 +334,44 @@ 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; + int rc; + + DBG(CXT, ul_debugobj(cxt, " lookup by fsinfo")); + + assert(cxt); + fs = cxt->fs; + + assert(fs); + assert(mnt_fs_get_target(fs)); + + /* read data from kernel */ + rc = mnt_fs_fetch_fsinfo(fs); + if (rc) + return rc; + + /* 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 +396,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) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 22c9ca71c..4c6a98746 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1649,3 +1649,152 @@ int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf) fs->flags |= MNT_FS_MERGED; return 0; } + + +#include +#include + +#define __NR_fsinfo 441 +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); +} + +static int fsinfo_id2target(unsigned int id, char *buf, size_t bufsz) +{ + /* open by ID to target */ + char idstr[sizeof(stringify_value(UINT_MAX))]; + ssize_t res; + + /* ask for moutpoint name */ + struct fsinfo_params params = { + .request = FSINFO_ATTR_MOUNT_POINT_FULL, + .flags = FSINFO_FLAGS_QUERY_MOUNT + }; + + snprintf(idstr, sizeof(idstr), "%u", id); + res = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, bufsz); + if (res < 0) + return -errno; + if ((size_t) res >= sizeof(buf)) + return -ENAMETOOLONG; + buf[res] = '\0'; + + return 0; +} + + +static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, + char *buf, size_t bufsz, size_t len) +{ + int rc = 0; + + switch (request) { + case FSINFO_ATTR_SOURCE: + buf[len] = '\0'; + rc = mnt_fs_set_source(fs, buf); + break; + case FSINFO_ATTR_CONFIGURATION: + buf[len] = '\0'; + rc = strdup_to_struct_member(fs, fs_optstr, buf); + break; + case FSINFO_ATTR_MOUNT_PATH: + buf[len] = '\0'; + rc = strdup_to_struct_member(fs, root, buf); + break; + case FSINFO_ATTR_MOUNT_POINT_FULL: + buf[len] = '\0'; + rc = strdup_to_struct_member(fs, target, buf); + 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; + /* mount ID */ + fs->id = x->mnt_id; + /* VFS options (convert it to strings) */ + free(fs->vfs_optstr); + fs->vfs_optstr = NULL; + rc = mnt_optstr_apply_flags(&fs->vfs_optstr, x->attr, + 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; +} + +int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) +{ + int fd, i, rc = 0; + char buf[BUFSIZ]; + int requests[] = { + FSINFO_ATTR_SOURCE, + FSINFO_ATTR_MOUNT_POINT_FULL, + FSINFO_ATTR_CONFIGURATION, + FSINFO_ATTR_MOUNT_PATH, + FSINFO_ATTR_IDS, + FSINFO_ATTR_MOUNT_INFO, + FSINFO_ATTR_MOUNT_TOPOLOGY + }; + + if (!fs->target && fs->id > 0) { + rc = fsinfo_id2target(fs->id, buf, sizeof(buf)); + if (rc) + return rc; + rc = mnt_fs_set_target(fs, buf); + if (rc) + return rc; + } + if (!fs->target) + return -EINVAL; + + fd = open(fs->target, O_PATH); + if (fd < 0) + return fd; + + for (i = 0; i < ARRAY_SIZE(requests); i++) { + ssize_t res; + struct fsinfo_params params = { + .request = requests[i], + .flags = FSINFO_FLAGS_QUERY_FD + }; + + res = fsinfo(fd, NULL, ¶ms, sizeof(params), buf, sizeof(buf)); + if (res < 0) + rc = res; + if ((size_t) rc >= sizeof(buf)) + rc = -ENAMETOOLONG; + if (rc == 0) + rc = fsinfo_buf2fs(fs, requests[i], buf, sizeof(buf), res); + if (rc) + break; /* error */ + } + + if (rc == 0) + fs->flags |= MNT_FS_KERNEL; + close(fd); + return rc; +} + + diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index f79ae1e65..5e7d052d1 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -422,6 +422,7 @@ extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) 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); /* context.c */ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); From 5fe931f872deec123e4ec292cd8fe99c7560784e Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 06/44] 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 /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 --- libmount/src/fs.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 4c6a98746..339fdd209 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "mountP.h" #include "strutils.h" @@ -1650,11 +1652,10 @@ int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf) return 0; } +#ifndef __NR_fsinfo +#define __NR_fsinfo -1 +#endif -#include -#include - -#define __NR_fsinfo 441 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) From 27c6c71003fdf2e6aacc39a970177ee4425ca1f2 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 07/44] 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 --- libmount/src/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 339fdd209..de445874a 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1747,7 +1747,8 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) { - int fd, i, rc = 0; + unsigned int i; + int fd, rc = 0; char buf[BUFSIZ]; int requests[] = { FSINFO_ATTR_SOURCE, From de1aa60bdb6ef0c52fd4ca58c3af69101ca2e6f2 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 08/44] libmount: fix incorrect error check in mnt_fs_fetch_fsinfo() Umm ... looks wrong and causes false -ENAMETOOLONG error returns. Signed-off-by: Ian Kent --- libmount/src/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index de445874a..a9e88ada3 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1785,7 +1785,7 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) res = fsinfo(fd, NULL, ¶ms, sizeof(params), buf, sizeof(buf)); if (res < 0) rc = res; - if ((size_t) rc >= sizeof(buf)) + if ((size_t) res >= sizeof(buf)) rc = -ENAMETOOLONG; if (rc == 0) rc = fsinfo_buf2fs(fs, requests[i], buf, sizeof(buf), res); From 3708880a4869dd3899b0cbdc26109d1f25988571 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 09/44] 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 --- libmount/src/fs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index a9e88ada3..68f5b9168 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "mountP.h" #include "strutils.h" @@ -1728,6 +1729,8 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, /* VFS options (convert it to strings) */ free(fs->vfs_optstr); fs->vfs_optstr = NULL; + if ((x->attr & (MOUNT_ATTR_NOATIME|MOUNT_ATTR_STRICTATIME)) == 0) + x->attr |= MS_RELATIME; rc = mnt_optstr_apply_flags(&fs->vfs_optstr, x->attr, mnt_get_builtin_optmap(MNT_LINUX_MAP)); break; From e6a848597c60a9b169685334f00ba71d3dc85a9a Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 10/44] 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 --- libmount/src/fs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 68f5b9168..9a50bbc12 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1689,9 +1689,8 @@ static int fsinfo_id2target(unsigned int id, char *buf, size_t bufsz) return 0; } - -static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, - char *buf, size_t bufsz, size_t len) +static int fsinfo_buf2fs(struct libmnt_fs *fs, + int request, char *buf, size_t len) { int rc = 0; @@ -1791,7 +1790,7 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) if ((size_t) res >= sizeof(buf)) rc = -ENAMETOOLONG; if (rc == 0) - rc = fsinfo_buf2fs(fs, requests[i], buf, sizeof(buf), res); + rc = fsinfo_buf2fs(fs, requests[i], buf, res); if (rc) break; /* error */ } From a41fc98ad0294e7a24a3d8720817a26b2115b395 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 20 Apr 2020 16:19:36 +0200 Subject: [PATCH 11/44] 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 --- libmount/src/fs.c | 68 +++++++++++++++++++++---------------------- libmount/src/mountP.h | 1 + 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 9a50bbc12..54cd74cee 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1666,27 +1666,24 @@ static ssize_t fsinfo(int dfd, const char *filename, result_buffer, result_buf_size); } -static int fsinfo_id2target(unsigned int id, char *buf, size_t bufsz) +int mnt_fs_lookup_id(const char *path, unsigned int *id, unsigned int flags) { - /* open by ID to target */ - char idstr[sizeof(stringify_value(UINT_MAX))]; - ssize_t res; - - /* ask for moutpoint name */ + struct fsinfo_mount_info info; struct fsinfo_params params = { - .request = FSINFO_ATTR_MOUNT_POINT_FULL, - .flags = FSINFO_FLAGS_QUERY_MOUNT + .flags = FSINFO_FLAGS_QUERY_PATH, + .request = FSINFO_ATTR_MOUNT_INFO, + .at_flags = flags, }; + int rc; - snprintf(idstr, sizeof(idstr), "%u", id); - res = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, bufsz); - if (res < 0) - return -errno; - if ((size_t) res >= sizeof(buf)) - return -ENAMETOOLONG; - buf[res] = '\0'; - - return 0; + errno = 0; + rc = fsinfo(AT_FDCWD, path, + ¶ms, sizeof(params), &info, sizeof(info)); + if (rc == -1) + rc = -errno; + else + *id = info.mnt_id; + return rc; } static int fsinfo_buf2fs(struct libmnt_fs *fs, @@ -1750,7 +1747,7 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) { unsigned int i; - int fd, rc = 0; + int rc = 0; char buf[BUFSIZ]; int requests[] = { FSINFO_ATTR_SOURCE, @@ -1762,29 +1759,33 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) FSINFO_ATTR_MOUNT_TOPOLOGY }; - if (!fs->target && fs->id > 0) { - rc = fsinfo_id2target(fs->id, buf, sizeof(buf)); - if (rc) - return rc; - rc = mnt_fs_set_target(fs, buf); - if (rc) - return rc; - } - if (!fs->target) + if (!fs->target && !fs->id) return -EINVAL; - fd = open(fs->target, O_PATH); - if (fd < 0) - return fd; + if (fs->target && !fs->id) { + unsigned int id; + + rc = mnt_fs_lookup_id(fs->target, &id, AT_NO_AUTOMOUNT); + if (rc < 0) + return rc; + fs->id = id; + } for (i = 0; i < ARRAY_SIZE(requests); i++) { - ssize_t res; + char idstr[sizeof(stringify_value(UINT_MAX))]; struct fsinfo_params params = { .request = requests[i], - .flags = FSINFO_FLAGS_QUERY_FD + .flags = FSINFO_FLAGS_QUERY_MOUNT }; + ssize_t res; - res = fsinfo(fd, NULL, ¶ms, sizeof(params), buf, sizeof(buf)); + /* fs->target already set? */ + if (fs->target && + params.request == FSINFO_ATTR_MOUNT_POINT_FULL) + continue; + + snprintf(idstr, sizeof(idstr), "%u", fs->id); + res = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, sizeof(buf)); if (res < 0) rc = res; if ((size_t) res >= sizeof(buf)) @@ -1797,7 +1798,6 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) if (rc == 0) fs->flags |= MNT_FS_KERNEL; - close(fd); return rc; } diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 5e7d052d1..89a82c192 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -422,6 +422,7 @@ extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) 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); +int mnt_fs_lookup_id(const char *path, unsigned int *id, unsigned int flags); extern int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs); /* context.c */ From 3fd63029064ae1b4aa6a3e0d9243e537998c89a7 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 20 Apr 2020 17:35:19 +0200 Subject: [PATCH 12/44] libmount: move fsinfo calls to separate file Signed-off-by: Karel Zak --- configure.ac | 1 + libmount/src/Makemodule.am | 1 + libmount/src/fs.c | 49 ++--------------------- libmount/src/fsinfo.c | 80 ++++++++++++++++++++++++++++++++++++++ libmount/src/mountP.h | 11 +++++- 5 files changed, 96 insertions(+), 46 deletions(-) create mode 100644 libmount/src/fsinfo.c diff --git a/configure.ac b/configure.ac index 7f1219ad8..d9d03ffd3 100644 --- a/configure.ac +++ b/configure.ac @@ -479,6 +479,7 @@ AC_CHECK_FUNCS([ \ explicit_bzero \ fmemopen \ fsync \ + fsinfo \ utimensat \ getdomainname \ getdtablesize \ diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index a2b218d5a..d54b3e50a 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -11,6 +11,7 @@ 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/lock.c \ diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 54cd74cee..b6bdeb393 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include "mountP.h" @@ -1653,39 +1651,6 @@ int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf) return 0; } -#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); -} - -int mnt_fs_lookup_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; - - errno = 0; - rc = fsinfo(AT_FDCWD, path, - ¶ms, sizeof(params), &info, sizeof(info)); - if (rc == -1) - rc = -errno; - else - *id = info.mnt_id; - return rc; -} - static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, char *buf, size_t len) { @@ -1765,33 +1730,27 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) if (fs->target && !fs->id) { unsigned int id; - rc = mnt_fs_lookup_id(fs->target, &id, AT_NO_AUTOMOUNT); + rc = mnt_get_target_id(fs->target, &id, AT_NO_AUTOMOUNT); if (rc < 0) return rc; fs->id = id; } for (i = 0; i < ARRAY_SIZE(requests); i++) { - char idstr[sizeof(stringify_value(UINT_MAX))]; struct fsinfo_params params = { .request = requests[i], .flags = FSINFO_FLAGS_QUERY_MOUNT }; - ssize_t res; + size_t sz = sizeof(buf); /* fs->target already set? */ if (fs->target && params.request == FSINFO_ATTR_MOUNT_POINT_FULL) continue; - snprintf(idstr, sizeof(idstr), "%u", fs->id); - res = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, sizeof(buf)); - if (res < 0) - rc = res; - if ((size_t) res >= sizeof(buf)) - rc = -ENAMETOOLONG; + rc = mnt_get_id_fsinfo(fs->id, ¶ms, sizeof(params), buf, &sz); if (rc == 0) - rc = fsinfo_buf2fs(fs, requests[i], buf, res); + rc = fsinfo_buf2fs(fs, requests[i], buf, sz); if (rc) break; /* error */ } diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c new file mode 100644 index 000000000..0e4faaa79 --- /dev/null +++ b/libmount/src/fsinfo.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2020 Karel Zak + * + * 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" + +#include +#include + +/* libc fallback */ +#ifndef HAVE_FSINFO +# 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 */ + +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; + + errno = 0; + rc = fsinfo(AT_FDCWD, path, + ¶ms, sizeof(params), &info, sizeof(info)); + if (rc == -1) + 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_get_id_fsinfo( unsigned int id, + struct fsinfo_params *params, + size_t params_size, + char *buf, + size_t *bufsz) +{ + char idstr[sizeof(stringify_value(UINT_MAX))]; + ssize_t res; + int rc = 0; + + assert(buf); + assert(bufsz); + assert(params); + + snprintf(idstr, sizeof(idstr), "%u", id); + + res = fsinfo(AT_FDCWD, idstr, params, params_size, buf, *bufsz); + if (res < 0) + rc = res; + if ((size_t) res >= *bufsz) + rc = -ENAMETOOLONG; + if (rc == 0) + *bufsz = res; + return rc; +} diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 89a82c192..31ba9d7d4 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -24,6 +24,8 @@ #include #include +#include /* for struct fsinfo_params */ + #include "c.h" #include "list.h" #include "debug.h" @@ -422,9 +424,16 @@ extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) 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); -int mnt_fs_lookup_id(const char *path, unsigned int *id, unsigned int flags); extern int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs); +/* fsinfo.c */ +extern int mnt_get_target_id(const char *path, unsigned int *id, unsigned int flags); +extern int mnt_get_id_fsinfo(unsigned int id, + struct fsinfo_params *params, + size_t params_size, + char *buf, + size_t *bufsz); + /* context.c */ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); extern int mnt_context_mtab_writable(struct libmnt_context *cxt); From bb881ebf61e875145389f61c140fb2d5c57856ed Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 20 Apr 2020 18:06:02 +0200 Subject: [PATCH 13/44] 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 --- configure.ac | 7 +++++++ libmount/src/fs.c | 10 ++++++++-- libmount/src/fsinfo.c | 6 ++++-- libmount/src/mountP.h | 6 +++++- libmount/src/version.c | 3 +++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index d9d03ffd3..6632644a2 100644 --- a/configure.ac +++ b/configure.ac @@ -246,6 +246,7 @@ AC_CHECK_HEADERS([ \ linux/falloc.h \ linux/watchdog.h \ linux/fd.h \ + linux/fsinfo.h \ linux/raw.h \ linux/tiocl.h \ linux/version.h \ @@ -389,6 +390,7 @@ 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_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 @@ -1132,6 +1134,11 @@ 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]) + ]) ]) AC_SUBST([LIBMOUNT_VERSION]) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index b6bdeb393..90c64a91d 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1651,6 +1651,13 @@ int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf) return 0; } +#ifndef USE_LIBMOUNT_SUPPORT_FSINFO +int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs __attribute__((__unused__))) +{ + return -ENOSYS; +} +#else /* USE_LIBMOUNT_SUPPORT_FSINFO */ + static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, char *buf, size_t len) { @@ -1759,5 +1766,4 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) fs->flags |= MNT_FS_KERNEL; return rc; } - - +#endif /* USE_LIBMOUNT_SUPPORT_FSINFO */ diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 0e4faaa79..be329e56d 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -11,11 +11,11 @@ */ #include "mountP.h" -#include -#include +#ifdef USE_LIBMOUNT_SUPPORT_FSINFO /* libc fallback */ #ifndef HAVE_FSINFO +# include # ifndef __NR_fsinfo # define __NR_fsinfo -1 # endif @@ -78,3 +78,5 @@ int mnt_get_id_fsinfo( unsigned int id, *bufsz = res; return rc; } + +#endif /* USE_LIBMOUNT_SUPPORT_FSINFO */ diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 31ba9d7d4..d2b7af89e 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -24,7 +24,9 @@ #include #include -#include /* for struct fsinfo_params */ +#ifdef USE_LIBMOUNT_SUPPORT_FSINFO +# include /* for struct fsinfo_params */ +#endif #include "c.h" #include "list.h" @@ -427,12 +429,14 @@ extern int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf); extern int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs); /* 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_get_id_fsinfo(unsigned int id, struct fsinfo_params *params, size_t params_size, char *buf, size_t *bufsz); +#endif /* context.c */ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); diff --git a/libmount/src/version.c b/libmount/src/version.c index b69b09a17..387e41b43 100644 --- a/libmount/src/version.c +++ b/libmount/src/version.c @@ -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 From 7ecabf3a9cbcdb8842676fee2cd841342f50b3ee Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 23 Apr 2020 11:26:22 +0200 Subject: [PATCH 14/44] libmount: implement on-demand fsinfo() call for libmnt_fs Use mnt_fs_enable_fsinfo() to enable fsinfo() calls. Signed-off-by: Karel Zak --- libmount/src/context_umount.c | 5 +- libmount/src/fs.c | 255 ++++++++++++++++++++++++++++------ libmount/src/fsinfo.c | 16 +-- libmount/src/libmount.h.in | 3 + libmount/src/libmount.sym | 6 + libmount/src/mountP.h | 15 +- 6 files changed, 238 insertions(+), 62 deletions(-) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index b39573564..cf43cfa87 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -349,9 +349,10 @@ static int lookup_umount_fs_by_fsinfo(struct libmnt_context *cxt) assert(mnt_fs_get_target(fs)); /* read data from kernel */ - rc = mnt_fs_fetch_fsinfo(fs); + rc = mnt_fs_enable_fsinfo(fs, 1); if (rc) - return rc; + /* return not-found if fsinfo() not avalable */ + return rc == -ENOSYS ? 1 : rc; /* merge utab to fs */ if (has_utab_entry(cxt, mnt_fs_get_target(fs))) { diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 90c64a91d..fa5927055 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -24,6 +24,53 @@ #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; + 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 } +}; + +/* converts libmnt_fs struct member offset to FSINFO_ATTR_ request ID */ +static 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; \ +}) + +#else /* !USE_LIBMOUNT_SUPPORT_FSINFO */ +#define mnt_fs_fetch_member_fsinfo(_fs, _member) __extension__ ({ \ + (0); \ +}) +#endif + /** * mnt_new_fs: * @@ -348,6 +395,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; } @@ -360,6 +409,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; } @@ -536,7 +587,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; } /** @@ -587,10 +642,13 @@ int __mnt_fs_set_propagation_from_string(struct libmnt_fs *fs, const char *str) */ int mnt_fs_get_propagation(struct libmnt_fs *fs, unsigned long *flags) { + if (!fs) + return -EINVAL; + if (!fs->propagation && mnt_fs_has_fsinfo(fs)) + mnt_fs_fetch_member_fsinfo(fs, propagation); if (fs && flags) *flags = fs->propagation; - - return fs ? 0 : -EINVAL; + return 0; } /** @@ -645,7 +703,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 */ @@ -770,7 +832,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 && @@ -938,7 +1000,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; } /** @@ -949,7 +1015,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; } /** @@ -1091,7 +1161,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; } /** @@ -1198,7 +1272,11 @@ 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; } /** @@ -1209,7 +1287,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; } /** @@ -1220,7 +1302,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; } /** @@ -1250,9 +1336,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); @@ -1353,7 +1439,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 */ @@ -1470,7 +1556,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); } /** @@ -1651,13 +1737,56 @@ int mnt_fs_merge_utab(struct libmnt_fs *fs, struct libmnt_fs *uf) 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; +} + #ifndef USE_LIBMOUNT_SUPPORT_FSINFO -int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs __attribute__((__unused__))) +int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs __attribute__((__unused__)), + int request __attribute__((__unused__))) +{ + return -ENOSYS; +} + +/** + * mnt_fs_enable_fsinfo: + * @fs: libmnt_fs instance + * @enable: 1 or 1 + * + * Enable fetch FS information from kernel by fsinfo(). FS target or ID has to + * be set in the @fs. If not syscall not avalable than returns -ENOSYS. + */ +int mnt_fs_enable_fsinfo(struct libmnt_fs *fs __attribute__((__unused__)), + int enable __attribute__((__unused__))) { return -ENOSYS; } #else /* USE_LIBMOUNT_SUPPORT_FSINFO */ +/** + * mnt_fs_enable_fsinfo: + * @fs: libmnt_fs instance + * @enable: 1 or 1 + * + * Enable fetch FS information from kernel by fsinfo(). FS target or ID has to + * be set in the @fs. f not syscall not avalable than returns -ENOSYS. + */ +int mnt_fs_enable_fsinfo(struct libmnt_fs *fs, int enable) +{ + assert(fs); + fs->fsinfo_enabled = enable ? 1 : 0; + + /* TODO: return -ENOSYS if fsinfo() is not avalable */ + return 0; +} + static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, char *buf, size_t len) { @@ -1716,50 +1845,84 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, return rc; } -int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs) + +/* 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) { - unsigned int i; - int rc = 0; char buf[BUFSIZ]; - int requests[] = { - FSINFO_ATTR_SOURCE, - FSINFO_ATTR_MOUNT_POINT_FULL, - FSINFO_ATTR_CONFIGURATION, - FSINFO_ATTR_MOUNT_PATH, - FSINFO_ATTR_IDS, - FSINFO_ATTR_MOUNT_INFO, - FSINFO_ATTR_MOUNT_TOPOLOGY + 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 == 0) + rc = fsinfo_buf2fs(fs, request, buf, sz); + + fs->fsinfo_done |= 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; - if (fs->target && !fs->id) { - unsigned int id; + /* don't call the same request more than once */ + if (fs->fsinfo_done & request) + return 0; - rc = mnt_get_target_id(fs->target, &id, AT_NO_AUTOMOUNT); + /* 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; - fs->id = id; + if (!fs->id) + return -EINVAL; + if (req == request) + return rc; + } - for (i = 0; i < ARRAY_SIZE(requests); i++) { - struct fsinfo_params params = { - .request = requests[i], - .flags = FSINFO_FLAGS_QUERY_MOUNT - }; - size_t sz = sizeof(buf); + snprintf(idstr, sizeof(idstr), "%u", fs->id); - /* fs->target already set? */ - if (fs->target && - params.request == FSINFO_ATTR_MOUNT_POINT_FULL) - continue; + if (request > -1) + /* one request */ + rc = __fs_fetch_fsinfo(fs, idstr, request, + FSINFO_FLAGS_QUERY_MOUNT, 0); + else { + /* fetch all */ + size_t i; - rc = mnt_get_id_fsinfo(fs->id, ¶ms, sizeof(params), buf, &sz); - if (rc == 0) - rc = fsinfo_buf2fs(fs, requests[i], buf, sz); - if (rc) - break; /* error */ + for (i = 0; i < ARRAY_SIZE(fsinfo_map); i++) { + int req = fsinfo_map[i].request; + + if (fs->fsinfo_done & req) + continue; + rc = __fs_fetch_fsinfo(fs, idstr, req, + FSINFO_FLAGS_QUERY_MOUNT, 0); + if (rc < 0) + break; + } } if (rc == 0) diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index be329e56d..4596098eb 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -53,13 +53,12 @@ int mnt_get_target_id(const char *path, unsigned int *id, unsigned int flags) * Call fsinfo(), fill @buf with the result, on success update @bufsz * to the real result size. */ -int mnt_get_id_fsinfo( unsigned int id, - struct fsinfo_params *params, - size_t params_size, - char *buf, - size_t *bufsz) +int mnt_fsinfo(const char *query, + struct fsinfo_params *params, + size_t params_size, + char *buf, + size_t *bufsz) { - char idstr[sizeof(stringify_value(UINT_MAX))]; ssize_t res; int rc = 0; @@ -67,9 +66,10 @@ int mnt_get_id_fsinfo( unsigned int id, assert(bufsz); assert(params); - snprintf(idstr, sizeof(idstr), "%u", id); + DBG(UTILS, ul_debug(" fsinfo(2) [query=%s, request=%u, flags=%u, at_flags=%u]", + query, params->request, params->flags, params->at_flags)); - res = fsinfo(AT_FDCWD, idstr, params, params_size, buf, *bufsz); + res = fsinfo(AT_FDCWD, query, params, params_size, buf, *bufsz); if (res < 0) rc = res; if ((size_t) res >= *bufsz) diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 321c0540b..111c6339e 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -518,6 +518,9 @@ 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); + /* tab-parse.c */ extern struct libmnt_table *mnt_new_table_from_file(const char *filename) __ul_attribute__((warn_unused_result)); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 792d11753..d7cf559d8 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -356,3 +356,9 @@ 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; +} MOUNT_2_35; + diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index d2b7af89e..05c198590 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -227,6 +227,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 */ }; /* @@ -426,16 +429,16 @@ extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) 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); +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_get_id_fsinfo(unsigned int id, - struct fsinfo_params *params, - size_t params_size, - char *buf, - size_t *bufsz); +extern int mnt_fsinfo(const char *query, + struct fsinfo_params *params, + size_t params_size, + char *buf, + size_t *bufsz); #endif /* context.c */ From 446c84741549ff093306c80f568eabb6aba480d5 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 23 Apr 2020 15:37:13 +0200 Subject: [PATCH 15/44] 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 --- libmount/src/context_umount.c | 16 +++++++---- libmount/src/fs.c | 52 ++++++++++++++--------------------- libmount/src/fsinfo.c | 46 +++++++++++++++++++++++++++---- libmount/src/libmount.h.in | 3 ++ libmount/src/libmount.sym | 1 + 5 files changed, 75 insertions(+), 43 deletions(-) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index cf43cfa87..cf724a964 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -338,7 +338,6 @@ static int lookup_umount_fs_by_mountinfo(struct libmnt_context *cxt, const char static int lookup_umount_fs_by_fsinfo(struct libmnt_context *cxt) { struct libmnt_fs *fs; - int rc; DBG(CXT, ul_debugobj(cxt, " lookup by fsinfo")); @@ -348,11 +347,16 @@ static int lookup_umount_fs_by_fsinfo(struct libmnt_context *cxt) assert(fs); assert(mnt_fs_get_target(fs)); - /* read data from kernel */ - rc = mnt_fs_enable_fsinfo(fs, 1); - if (rc) - /* return not-found if fsinfo() not avalable */ - return rc == -ENOSYS ? 1 : rc; + /* + * 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))) { diff --git a/libmount/src/fs.c b/libmount/src/fs.c index fa5927055..4597d5cb1 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1748,6 +1748,23 @@ 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__))) @@ -1755,38 +1772,8 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs __attribute__((__unused__)), return -ENOSYS; } -/** - * mnt_fs_enable_fsinfo: - * @fs: libmnt_fs instance - * @enable: 1 or 1 - * - * Enable fetch FS information from kernel by fsinfo(). FS target or ID has to - * be set in the @fs. If not syscall not avalable than returns -ENOSYS. - */ -int mnt_fs_enable_fsinfo(struct libmnt_fs *fs __attribute__((__unused__)), - int enable __attribute__((__unused__))) -{ - return -ENOSYS; -} #else /* USE_LIBMOUNT_SUPPORT_FSINFO */ -/** - * mnt_fs_enable_fsinfo: - * @fs: libmnt_fs instance - * @enable: 1 or 1 - * - * Enable fetch FS information from kernel by fsinfo(). FS target or ID has to - * be set in the @fs. f not syscall not avalable than returns -ENOSYS. - */ -int mnt_fs_enable_fsinfo(struct libmnt_fs *fs, int enable) -{ - assert(fs); - fs->fsinfo_enabled = enable ? 1 : 0; - - /* TODO: return -ENOSYS if fsinfo() is not avalable */ - return 0; -} - static int fsinfo_buf2fs(struct libmnt_fs *fs, int request, char *buf, size_t len) { @@ -1856,7 +1843,6 @@ static int __fs_fetch_fsinfo(struct libmnt_fs *fs, char buf[BUFSIZ]; size_t sz = sizeof(buf); int rc; - struct fsinfo_params params = { .request = request, .flags = flags, @@ -1864,7 +1850,9 @@ static int __fs_fetch_fsinfo(struct libmnt_fs *fs, }; rc = mnt_fsinfo(query, ¶ms, sizeof(params), buf, &sz); - if (rc == 0) + if (rc == -ENOSYS) + mnt_fs_enable_fsinfo(fs, 0); + else if (rc == 0) rc = fsinfo_buf2fs(fs, request, buf, sz); fs->fsinfo_done |= request; diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 4596098eb..074aba7e3 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -12,7 +12,6 @@ #include "mountP.h" #ifdef USE_LIBMOUNT_SUPPORT_FSINFO - /* libc fallback */ #ifndef HAVE_FSINFO # include @@ -29,6 +28,8 @@ static ssize_t fsinfo(int dfd, const char *filename, } #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; @@ -66,17 +67,52 @@ int mnt_fsinfo(const char *query, assert(bufsz); assert(params); - DBG(UTILS, ul_debug(" fsinfo(2) [query=%s, request=%u, flags=%u, at_flags=%u]", + DBG(UTILS, ul_debug("fsinfo(2) [query=%s, request=%u, flags=%u, at_flags=%u]", query, params->request, params->flags, params->at_flags)); res = fsinfo(AT_FDCWD, query, params, params_size, buf, *bufsz); if (res < 0) - rc = res; - if ((size_t) res >= *bufsz) + rc = -errno; + else if ((size_t) res >= *bufsz) rc = -ENAMETOOLONG; - if (rc == 0) + else if (rc == 0) *bufsz = res; + if (have_fsinfo == -1) + have_fsinfo = rc == -ENOSYS ? 0 : 1; + + if (rc) + DBG(UTILS, ul_debug("fsinfo(2) [rc=%d %m]", rc)); return rc; } + #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); + + mnt_fsinfo("/", ¶ms, sizeof(params), (char *)&info, &sz); + } + return have_fsinfo; +#else + return 0; +#endif +} + diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 111c6339e..68394cc7a 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -521,6 +521,9 @@ 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)); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index d7cf559d8..7ef3bb8eb 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -360,5 +360,6 @@ MOUNT_2_35 { MOUNT_2_36 { mnt_fs_enable_fsinfo; mnt_fs_has_fsinfo; + mnt_has_fsinfo; } MOUNT_2_35; From 0f728eee37a835a3d3e6df2361b0365ea2e278d8 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 23 Apr 2020 16:15:44 +0200 Subject: [PATCH 16/44] libmount: fix fs->fsinfo_done use Signed-off-by: Karel Zak --- libmount/src/fs.c | 34 +++++++++++++++++++++++++++++----- libmount/src/fsinfo.c | 2 +- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 4597d5cb1..aaf3d3919 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -30,7 +30,7 @@ */ struct fs_member_fsinfo { size_t member; - int request; + unsigned int request; }; static const struct fs_member_fsinfo fsinfo_map[] = @@ -47,8 +47,32 @@ static const struct fs_member_fsinfo fsinfo_map[] = { 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 int fsinfo_off2req(size_t member_offset) +static unsigned int fsinfo_off2req(size_t member_offset) { size_t i; @@ -1855,7 +1879,7 @@ static int __fs_fetch_fsinfo(struct libmnt_fs *fs, else if (rc == 0) rc = fsinfo_buf2fs(fs, request, buf, sz); - fs->fsinfo_done |= request; + fs->fsinfo_done |= fsinfo_req2mask(request); return rc; } @@ -1872,7 +1896,7 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs, int request) return -EINVAL; /* don't call the same request more than once */ - if (fs->fsinfo_done & request) + if (fs->fsinfo_done & fsinfo_req2mask(request)) return 0; /* ask for ID if missing */ @@ -1904,7 +1928,7 @@ int mnt_fs_fetch_fsinfo(struct libmnt_fs *fs, int request) for (i = 0; i < ARRAY_SIZE(fsinfo_map); i++) { int req = fsinfo_map[i].request; - if (fs->fsinfo_done & req) + if (fs->fsinfo_done & fsinfo_req2mask(req)) continue; rc = __fs_fetch_fsinfo(fs, idstr, req, FSINFO_FLAGS_QUERY_MOUNT, 0); diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 074aba7e3..6d7c61ea3 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -67,7 +67,7 @@ int mnt_fsinfo(const char *query, assert(bufsz); assert(params); - DBG(UTILS, ul_debug("fsinfo(2) [query=%s, request=%u, flags=%u, at_flags=%u]", + 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)); res = fsinfo(AT_FDCWD, query, params, params_size, buf, *bufsz); From a31dcde3f9c8ead5a8416b29649c2e4e48b85a1f Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 24 Apr 2020 12:19:50 +0200 Subject: [PATCH 17/44] 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 Signed-off-by: Karel Zak --- libmount/src/fsinfo.c | 70 +++++++++++++++++++-- libmount/src/libmount.h.in | 1 + libmount/src/libmount.sym | 1 + libmount/src/mountP.h | 3 + libmount/src/tab.c | 36 +++++++++++ libmount/src/tab_parse.c | 122 ++++++++++++++++++++++++++++++++++++- 6 files changed, 227 insertions(+), 6 deletions(-) diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 6d7c61ea3..001cb8db2 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -38,12 +38,11 @@ int mnt_get_target_id(const char *path, unsigned int *id, unsigned int flags) .request = FSINFO_ATTR_MOUNT_INFO, .at_flags = flags, }; - int rc; + int rc = 0; errno = 0; - rc = fsinfo(AT_FDCWD, path, - ¶ms, sizeof(params), &info, sizeof(info)); - if (rc == -1) + if (fsinfo(AT_FDCWD, path, + ¶ms, sizeof(params), &info, sizeof(info)) < 0) rc = -errno; else *id = info.mnt_id; @@ -69,7 +68,7 @@ int mnt_fsinfo(const char *query, 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 = -errno; @@ -85,6 +84,67 @@ int mnt_fsinfo(const char *query, 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; + + 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 */ +int mnt_fsinfo_get_children(unsigned int id, + struct fsinfo_mount_child **mounts, + size_t *count) +{ + size_t size = 0; + + assert(id); + assert(mounts); + assert(count); + + *mounts = fsinfo_alloc_attrs(id, FSINFO_ATTR_MOUNT_CHILDREN, 0, &size); + if (!*mounts) + return -errno; + + *count = size / sizeof((*mounts)[0]) - 1; + return 0; +} #endif /* USE_LIBMOUNT_SUPPORT_FSINFO */ diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 68394cc7a..790f40891 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -531,6 +531,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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 7ef3bb8eb..58388fdb9 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -361,5 +361,6 @@ MOUNT_2_36 { mnt_fs_enable_fsinfo; mnt_fs_has_fsinfo; mnt_has_fsinfo; + mnt_table_parse_fsinfo; } MOUNT_2_35; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 05c198590..099888ab0 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -439,6 +439,9 @@ extern int mnt_fsinfo(const char *query, 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); #endif /* context.c */ diff --git a/libmount/src/tab.c b/libmount/src/tab.c index cdce06160..4244ab7bb 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -1974,6 +1974,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; @@ -2229,6 +2262,9 @@ int main(int argc, char *argv[]) { "--find-mountpoint", test_find_mountpoint, "" }, { "--copy-fs", test_copy_fs, " copy root FS from the file" }, { "--is-mounted", test_is_mounted, " check what from fstab is already mounted" }, +#ifdef USE_LIBMOUNT_SUPPORT_FSINFO + { "--fsinfo", test_fsinfo, "read mount table by fsinfo syscall"}, +#endif { NULL } }; diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index 2bca790ec..cbf53dd2b 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -685,7 +685,7 @@ static int kernel_fs_postparse(struct libmnt_table *tb, if (src && strcmp(src, "/dev/root") == 0) { char *real = NULL; - rc = mnt_guess_system_root(fs->devno, tb->cache, &real); + rc = mnt_guess_system_root(mnt_fs_get_devno(fs), tb->cache, &real); if (rc < 0) return rc; @@ -703,6 +703,126 @@ static int kernel_fs_postparse(struct libmnt_table *tb, return rc; } +#ifdef USE_LIBMOUNT_SUPPORT_FSINFO +static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id) +{ + struct libmnt_fs *fs = NULL; + struct fsinfo_mount_child *mounts = NULL; + pid_t tid = -1; + size_t i, count; + int rc = 0; + + rc = mnt_fsinfo_get_children(id, &mounts, &count); + if (rc != 0) + goto out; + if (!count) + goto out; + + for (i = 0; i < count; i++) { + if (!fs) { + fs = mnt_new_fs(); + if (!fs) { + rc = -ENOMEM; + goto out; + } + } + fs->id = mounts[i].mnt_id; + + mnt_fs_enable_fsinfo(fs, 1); + + if (!rc && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data)) + rc = 1; /* filtered out by callback... */ + if (!rc) + rc = mnt_table_add_fs(tb, fs); + if (!rc) { + /* Using fsinfo() is equivalent to parsing + * mountinfo. + */ + rc = kernel_fs_postparse(tb, fs, &tid, + _PATH_PROC_MOUNTINFO); + if (rc) { + mnt_table_remove_fs(tb, fs); + goto done; + } + + /* merge VFS and FS options to one string */ + fs->optstr = mnt_fs_strdup_options(fs); + if (!fs->optstr) { + mnt_table_remove_fs(tb, fs); + rc = -ENOMEM; + goto done; + } + + rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs)); + if (rc) + mnt_table_remove_fs(tb, fs); + } +done: + 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; + + DBG(TAB, ul_debugobj(tb, "fsinfo: 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_chldren(tb, id); + if (rc < 0) + goto err; + + DBG(TAB, ul_debugobj(tb, "fsinfo: stop parsing (%d entries)", + mnt_table_get_nents(tb))); + return 0; +err: + DBG(TAB, ul_debugobj(tb, "fsinfo: 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; From aa312e00ae8403f83e38f02343d832f9c4f72379 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 12:59:46 +0200 Subject: [PATCH 18/44] findmnt: add --fsinfo Signed-off-by: Karel Zak --- misc-utils/findmnt.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c index 53f647fc1..a670d7dbf 100644 --- a/misc-utils/findmnt.c +++ b/misc-utils/findmnt.c @@ -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); @@ -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[=] monitor changes in table of mounted filesystems\n"), out); fputs(_(" -w, --timeout 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)) { From 464ee2c8fefcd9c6964914caa3f9d35435ebff60 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 13:00:23 +0200 Subject: [PATCH 19/44] libmount: read tid only once Signed-off-by: Karel Zak --- libmount/src/tab_parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index cbf53dd2b..e67e2b8bc 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -704,11 +704,11 @@ static int kernel_fs_postparse(struct libmnt_table *tb, } #ifdef USE_LIBMOUNT_SUPPORT_FSINFO -static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id) +static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id, struct libmnt_fs *parent) { struct libmnt_fs *fs = NULL; struct fsinfo_mount_child *mounts = NULL; - pid_t tid = -1; + pid_t tid = parent ? parent->tid : -1; size_t i, count; int rc = 0; @@ -753,7 +753,7 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id) goto done; } - rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs)); + rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs), fs); if (rc) mnt_table_remove_fs(tb, fs); } @@ -789,7 +789,7 @@ static int __table_parse_fsinfo(struct libmnt_table *tb) if (rc < 0) goto err; - rc = table_parse_fetch_chldren(tb, id); + rc = table_parse_fetch_chldren(tb, id, NULL); if (rc < 0) goto err; From 1cdddcfa32bfeaf6d335cb7593a113afe9caa06d Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 14:47:38 +0200 Subject: [PATCH 20/44] findmnt: fix cache read for fsinfo() Signed-off-by: Karel Zak --- misc-utils/findmnt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c index a670d7dbf..65eb4a4c2 100644 --- a/misc-utils/findmnt.c +++ b/misc-utils/findmnt.c @@ -1623,7 +1623,7 @@ int main(int argc, char *argv[]) } mnt_table_set_cache(tb, cache); - if (tabtype != TABTYPE_KERNEL || tabtype != TABTYPE_FSINFO) + if (tabtype != TABTYPE_KERNEL && tabtype != TABTYPE_FSINFO) cache_set_targets(cache); } From 1728c6238d0f4baed5d398cc474e23d2c17ad059 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 15:00:26 +0200 Subject: [PATCH 21/44] libmount: create optstr on-demand --- libmount/src/fs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index aaf3d3919..2bcd0d915 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -875,7 +875,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; } /** From b8ed57bdf382b5a3a4041e5ace0d259ed1eeb209 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 15:01:05 +0200 Subject: [PATCH 22/44] libmount: don't trigger fsinfo() by debug message Signed-off-by: Karel Zak --- libmount/src/tab.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 4244ab7bb..d66650729 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -453,8 +453,9 @@ 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 */ + DBG(TAB, ul_debugobj(tb, "add entry: %s %s", fs->source, fs->target)); return 0; } From 4b5ec23c5a5f5c23a7419267b11b10dd2ebb56e1 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 15:01:39 +0200 Subject: [PATCH 23/44] libmount: optimize kernel_fs_postparse use Signed-off-by: Karel Zak --- libmount/src/tab_parse.c | 75 ++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index e67e2b8bc..ae63a2314 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -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) @@ -665,38 +669,43 @@ 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(mnt_fs_get_devno(fs), 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; } } @@ -704,11 +713,12 @@ static int kernel_fs_postparse(struct libmnt_table *tb, } #ifdef USE_LIBMOUNT_SUPPORT_FSINFO -static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id, struct libmnt_fs *parent) +static int table_parse_fetch_chldren(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; - pid_t tid = parent ? parent->tid : -1; size_t i, count; int rc = 0; @@ -738,22 +748,21 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id, s /* Using fsinfo() is equivalent to parsing * mountinfo. */ - rc = kernel_fs_postparse(tb, fs, &tid, - _PATH_PROC_MOUNTINFO); + rc = kernel_fs_postparse(tb, fs, pa); if (rc) { mnt_table_remove_fs(tb, fs); goto done; } - /* merge VFS and FS options to one string */ - fs->optstr = mnt_fs_strdup_options(fs); - if (!fs->optstr) { - mnt_table_remove_fs(tb, fs); - rc = -ENOMEM; - goto done; - } + /* save one fsinfo() call */ + fs->parent = parent ? parent->id : 0; - rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs), fs); + /* we can be sure the current fs is root */ + if (parent == NULL && i == 0) + pa->has_root_fs = 1; + + /* recursively read children */ + rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs), fs, pa); if (rc) mnt_table_remove_fs(tb, fs); } @@ -781,6 +790,7 @@ 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: start parsing [entries=%d, filter=%s]", mnt_table_get_nents(tb), tb->fltrcb ? "yes" : "not")); @@ -789,7 +799,7 @@ static int __table_parse_fsinfo(struct libmnt_table *tb) if (rc < 0) goto err; - rc = table_parse_fetch_chldren(tb, id, NULL); + rc = table_parse_fetch_chldren(tb, id, NULL, &pa); if (rc < 0) goto err; @@ -827,7 +837,6 @@ static int __table_parse_stream(struct libmnt_table *tb, FILE *f, const char *fi { int rc = -1; int flags = 0; - pid_t tid = -1; struct libmnt_fs *fs = NULL; struct libmnt_parser pa = { .line = 0 }; @@ -865,7 +874,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); } From abd584bee048ac58a9cae59e55e23e53c66f9a1c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 16:11:44 +0200 Subject: [PATCH 24/44] findmnt: use ID to detect tree-ability Signed-off-by: Karel Zak --- misc-utils/findmnt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c index 65eb4a4c2..6eb0bbf8a 100644 --- a/misc-utils/findmnt.c +++ b/misc-utils/findmnt.c @@ -895,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; From 25f56a94553a8910234123925cc96db21b3f7a13 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 16:12:29 +0200 Subject: [PATCH 25/44] libmount: be more verbose about fsinfo rc Signed-off-by: Karel Zak --- libmount/src/fsinfo.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 001cb8db2..4c3c01db1 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -40,6 +40,7 @@ int mnt_get_target_id(const char *path, unsigned int *id, unsigned int 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) @@ -70,17 +71,19 @@ int mnt_fsinfo(const char *query, query, params->request, params->flags, params->at_flags)); errno = 0; res = fsinfo(AT_FDCWD, query, params, params_size, buf, *bufsz); - if (res < 0) - rc = -errno; - else if ((size_t) res >= *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) + } else if (rc == 0) *bufsz = res; if (have_fsinfo == -1) have_fsinfo = rc == -ENOSYS ? 0 : 1; if (rc) - DBG(UTILS, ul_debug("fsinfo(2) [rc=%d %m]", rc)); + DBG(UTILS, ul_debug("fsinfo(2) [res=%ld]", res)); return rc; } @@ -111,6 +114,7 @@ static void *fsinfo_alloc_attrs(unsigned int id, } buf = tmp; + errno = 0; ret = fsinfo(AT_FDCWD, idstr, ¶ms, sizeof(params), buf, bufsz); if (ret < 0) { free(buf); @@ -138,6 +142,7 @@ int mnt_fsinfo_get_children(unsigned int id, assert(mounts); assert(count); + DBG(UTILS, ul_debug("fsinfo: reading children for id=%u", id)); *mounts = fsinfo_alloc_attrs(id, FSINFO_ATTR_MOUNT_CHILDREN, 0, &size); if (!*mounts) return -errno; From 783d41e43db56850445544b43ef66ffaf3447f79 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 16:13:20 +0200 Subject: [PATCH 26/44] 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 --- libmount/src/tab.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libmount/src/tab.c b/libmount/src/tab.c index d66650729..c7a5f509a 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -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; @@ -455,7 +454,10 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) /* don't use mnt_fs_get_() in debug to avoid fsinfo() calls in on-emand * mode */ - DBG(TAB, ul_debugobj(tb, "add entry: %s %s", fs->source, fs->target)); + 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; } @@ -631,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); @@ -664,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); From d15875e29cdc051dfb010d88ecb6358342eca848 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 16:15:09 +0200 Subject: [PATCH 27/44] libmount: mnt_table_parse_fsinfo() refactoring - add root FS - reuse FS initialization code Signed-off-by: Karel Zak --- libmount/src/fs.c | 2 +- libmount/src/tab_parse.c | 74 +++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 2bcd0d915..c58568769 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1792,7 +1792,7 @@ 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")); + /*DBG(FS, ul_debugobj(fs, "fsinfo %s", enable ? "ENABLED" : "DISABLED"));*/ return 0; } diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index ae63a2314..62d791d42 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -712,6 +712,29 @@ static int kernel_fs_postparse(struct libmnt_table *tb, return rc; } +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; +} + + #ifdef USE_LIBMOUNT_SUPPORT_FSINFO static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id, struct libmnt_fs *parent, @@ -722,12 +745,32 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, 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; + } + + /* children list */ rc = mnt_fsinfo_get_children(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(); @@ -736,37 +779,14 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, goto out; } } - fs->id = mounts[i].mnt_id; - - mnt_fs_enable_fsinfo(fs, 1); - - if (!rc && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data)) - rc = 1; /* filtered out by callback... */ + rc = parse_fsinfo_init_fs(tb, fs, mounts[i].mnt_id, parent, pa); if (!rc) rc = mnt_table_add_fs(tb, fs); if (!rc) { - /* Using fsinfo() is equivalent to parsing - * mountinfo. - */ - rc = kernel_fs_postparse(tb, fs, pa); - if (rc) { - mnt_table_remove_fs(tb, fs); - goto done; - } - - /* save one fsinfo() call */ - fs->parent = parent ? parent->id : 0; - - /* we can be sure the current fs is root */ - if (parent == NULL && i == 0) - pa->has_root_fs = 1; - - /* recursively read children */ rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs), fs, pa); if (rc) mnt_table_remove_fs(tb, fs); } -done: if (rc) { if (rc > 0) { mnt_reset_fs(fs); @@ -792,7 +812,7 @@ static int __table_parse_fsinfo(struct libmnt_table *tb) int rc; struct libmnt_parser pa = { .filename = _PATH_PROC_MOUNTINFO }; - DBG(TAB, ul_debugobj(tb, "fsinfo: start parsing [entries=%d, filter=%s]", + 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); @@ -803,11 +823,11 @@ static int __table_parse_fsinfo(struct libmnt_table *tb) if (rc < 0) goto err; - DBG(TAB, ul_debugobj(tb, "fsinfo: stop parsing (%d entries)", + 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: error (rc=%d)", rc)); + DBG(TAB, ul_debugobj(tb, "fsinfo parse: error (rc=%d)", rc)); return rc; } #endif /* USE_LIBMOUNT_SUPPORT_FSINFO */ From c0cffb7c0f81cfd1199d3ae0440a95774a45e628 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 24 Apr 2020 16:26:50 +0200 Subject: [PATCH 28/44] libmount: don't use eplty strings from fsinfo Signed-off-by: Karel Zak --- libmount/src/fs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index c58568769..c3af01e9e 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1813,19 +1813,19 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, switch (request) { case FSINFO_ATTR_SOURCE: buf[len] = '\0'; - rc = mnt_fs_set_source(fs, buf); + 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); + 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); + 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); + rc = strdup_to_struct_member(fs, target, *buf ? buf : NULL); break; case FSINFO_ATTR_IDS: { From b372c1b637296ec75b7588775cb842f6a8274d54 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 10 Jun 2020 16:09:48 +0200 Subject: [PATCH 29/44] libmount: fix compiler warning [-Wunused-function] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libmount/src/tab_parse.c:715:12: warning: ‘parse_fsinfo_init_fs’ defined but not used [-Wunused-function] Signed-off-by: Karel Zak --- libmount/src/tab_parse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index 62d791d42..8f23f5dd2 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -712,6 +712,7 @@ static int kernel_fs_postparse(struct libmnt_table *tb, return rc; } +#ifdef USE_LIBMOUNT_SUPPORT_FSINFO static int parse_fsinfo_init_fs( struct libmnt_table *tb, struct libmnt_fs *fs, @@ -734,8 +735,6 @@ static int parse_fsinfo_init_fs( return rc; } - -#ifdef USE_LIBMOUNT_SUPPORT_FSINFO static int table_parse_fetch_chldren(struct libmnt_table *tb, unsigned int id, struct libmnt_fs *parent, struct libmnt_parser *pa) From 8386d586b4cefe1d77960248ee7dee23d5df5cfe Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 10 Jun 2020 16:15:16 +0200 Subject: [PATCH 30/44] 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 --- libmount/src/monitor.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index 1f6eb1a57..41f3b919d 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -45,18 +45,26 @@ 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; + changed : 1; /* change detected */ - struct list_head ents; + struct list_head ents; /* libmnt_monitor->ents list item */ }; struct libmnt_monitor { @@ -117,6 +125,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 +155,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,6 +168,15 @@ 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; + } + } return me; } @@ -309,7 +327,6 @@ err: static int userspace_event_verify(struct libmnt_monitor *mn, struct monitor_entry *me) { - char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; int status = 0; if (!me || me->fd < 0) @@ -319,15 +336,14 @@ static int userspace_event_verify(struct libmnt_monitor *mn, /* 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) + 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; @@ -410,7 +426,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; @@ -511,7 +527,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; From cb201c9c8b422b9986454e48b9354cde486c2708 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 17 Jun 2020 18:00:53 +0200 Subject: [PATCH 31/44] libmount: (monitor) cleanup debug messages Signed-off-by: Karel Zak --- libmount/src/monitor.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index 41f3b919d..be953d1cd 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -178,6 +178,7 @@ static struct monitor_entry *monitor_new_entry(struct libmnt_monitor *mn, size_t } } + DBG(MONITOR, ul_debugobj(me, "alloc entry")); return me; } @@ -255,7 +256,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; @@ -276,7 +277,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; @@ -291,7 +292,7 @@ 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; @@ -302,7 +303,6 @@ static int userspace_monitor_get_fd(struct libmnt_monitor *mn, 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) @@ -311,13 +311,15 @@ 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; } @@ -332,13 +334,14 @@ static int userspace_event_verify(struct libmnt_monitor *mn, 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 { char *p; const struct inotify_event *e; + DBG(MONITOR, ul_debugobj(me, " reading inotify events")); me->bufrsz = read(me->fd, me->buf, me->bufsz); if (me->bufrsz < 0) break; @@ -349,7 +352,8 @@ static int userspace_event_verify(struct libmnt_monitor *mn, 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; @@ -358,7 +362,7 @@ static int userspace_event_verify(struct libmnt_monitor *mn, userspace_add_watch(me, &status, &fd); if (fd != e->wd) { - DBG(MONITOR, ul_debugobj(mn, " removing watch [fd=%d]", e->wd)); + DBG(MONITOR, ul_debugobj(me, " removing watch [fd=%d]", e->wd)); inotify_rm_watch(me->fd, e->wd); } } @@ -581,7 +585,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; @@ -595,7 +599,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; @@ -642,7 +646,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; @@ -670,14 +674,14 @@ 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) continue; @@ -686,13 +690,13 @@ int mnt_monitor_get_fd(struct libmnt_monitor *mn) 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; } From 98fd10f63f71090ea41024373a40a962d3e9c318 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 17 Jun 2020 18:08:56 +0200 Subject: [PATCH 32/44] 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 --- libmount/src/monitor.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index be953d1cd..dc90e175e 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -268,6 +268,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) @@ -330,6 +333,7 @@ static int userspace_event_verify(struct libmnt_monitor *mn, struct monitor_entry *me) { int status = 0; + int last_removed = -1; if (!me || me->fd < 0) return 0; @@ -361,9 +365,18 @@ static int userspace_event_verify(struct libmnt_monitor *mn, /* event on lock file */ userspace_add_watch(me, &status, &fd); - if (fd != 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; } } } From e1f76818340bdd8067bb3df29088331b0e4b7c82 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 17 Jun 2020 18:15:21 +0200 Subject: [PATCH 33/44] libmount: (monitor) fix typo Signed-off-by: Karel Zak --- libmount/src/monitor.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index dc90e175e..5c366e400 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -61,7 +61,7 @@ struct monitor_entry { const struct monitor_opers *opers; - unsigned int enable : 1, + unsigned int enabled : 1, changed : 1; /* change detected */ struct list_head ents; /* libmnt_monitor->ents list item */ @@ -300,7 +300,7 @@ static int userspace_monitor_get_fd(struct libmnt_monitor *mn __attribute__((__u { 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 */ @@ -483,7 +483,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 */ @@ -585,7 +585,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) @@ -696,7 +696,7 @@ int mnt_monitor_get_fd(struct libmnt_monitor *mn) 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) From 386d42fa5de9c49c8d7868d0773fecd0b99ff84e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 19 Jun 2020 09:19:07 +0200 Subject: [PATCH 34/44] 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 --- libmount/src/libmount.h.in | 3 + libmount/src/monitor.c | 154 +++++++++++++++++++++++++++++++++---- 2 files changed, 143 insertions(+), 14 deletions(-) diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 790f40891..557740b01 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -681,6 +681,9 @@ 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); + /* context.c */ diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index 5c366e400..515cc8af7 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -62,6 +62,7 @@ struct monitor_entry { const struct monitor_opers *opers; unsigned int enabled : 1, + keep_data : 1, /* return read() buffer to caller */ changed : 1; /* change detected */ struct list_head ents; /* libmnt_monitor->ents list item */ @@ -77,7 +78,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, @@ -329,7 +330,7 @@ err: /* * 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) { int status = 0; @@ -347,7 +348,7 @@ static int userspace_event_verify(struct libmnt_monitor *mn, DBG(MONITOR, ul_debugobj(me, " reading inotify events")); me->bufrsz = read(me->fd, me->buf, me->bufsz); - if (me->bufrsz < 0) + if (me->bufrsz <= 0) break; for (p = me->buf; p < me->buf + me->bufrsz; @@ -380,6 +381,9 @@ static int userspace_event_verify(struct libmnt_monitor *mn, } } } + + if (status == 1 && me->keep_data) + break; } while (1); DBG(MONITOR, ul_debugobj(mn, "%s", status == 1 ? " success" : " nothing")); @@ -390,9 +394,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 }; /** @@ -401,7 +405,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() @@ -409,11 +413,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) @@ -751,8 +759,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; } @@ -775,6 +783,7 @@ static struct monitor_entry *get_changed(struct libmnt_monitor *mn) return NULL; } + /** * mnt_monitor_next_change: * @mn: monitor @@ -787,8 +796,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; @@ -822,8 +831,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; } @@ -858,6 +867,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[]) @@ -996,12 +1070,64 @@ 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); + + 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; + + printf(" %s: change detected\n", filename); + + switch (type) { + + case MNT_MONITOR_TYPE_KERNEL: /* no data, jusr epoll */ + break; + + case MNT_MONITOR_TYPE_USERSPACE: /* inotify */ + 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(" inotify event mask=0x%x, name=%s\n", + e->mask, e->len ? e->name : ""); + + chunksz = e->len; + } + break; + } + } + } + mnt_unref_monitor(mn); + return 0; +} + int main(int argc, char *argv[]) { struct libmnt_test tss[] = { { "--epoll", test_epoll, " monitor in epoll" }, { "--epoll-clean", test_epoll_cleanup, " monitor in epoll and clean events" }, { "--wait", test_wait, " monitor wait function" }, + { "--data", test_data, " notification kernel data" }, { NULL } }; From dbf6243597b0323139bcb04a848bb435ea8db33d Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 19 Jun 2020 14:08:56 +0200 Subject: [PATCH 35/44] 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 --- configure.ac | 8 ++ libmount/src/libmount.h.in | 4 +- libmount/src/monitor.c | 267 ++++++++++++++++++++++++++++++++++++- 3 files changed, 275 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 6632644a2..10f0edaf5 100644 --- a/configure.ac +++ b/configure.ac @@ -253,6 +253,7 @@ AC_CHECK_HEADERS([ \ linux/securebits.h \ linux/net_namespace.h \ linux/capability.h \ + linux/watch_queue.h \ locale.h \ mntent.h \ net/if.h \ @@ -391,6 +392,7 @@ 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 @@ -524,6 +526,7 @@ AC_CHECK_FUNCS([ \ vwarnx \ warn \ warnx \ + watch_mount \ ]) AC_FUNC_FSEEKO @@ -1139,6 +1142,11 @@ AS_IF([test "x$build_libmount" = xyes], [ ],[ 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]) diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 557740b01..d3f2bf956 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -662,7 +662,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); @@ -670,6 +671,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); diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index 515cc8af7..f5422a8aa 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -35,13 +35,20 @@ * */ -#include "fileutils.h" #include "mountP.h" #include "pathnames.h" +#include "fileutils.h" #include #include +#ifdef USE_LIBMOUNT_SUPPORT_WATCHQUEUE +#define _LINUX_FCNTL_H /* WORKAROUND to build against non-distro headers */ +# include +# include +# include +#endif + struct monitor_opers; @@ -584,6 +591,238 @@ err: return rc; } +/* + * kernel mount-watch monitor + */ +#ifdef USE_LIBMOUNT_SUPPORT_WATCHQUEUE + +#ifndef HAVE_WATCH_MOUNT +# include +# 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 */ +#define MNT_KERNELWATCH_MSG_MINSZ sizeof(struct watch_notification) + +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; + size_t len = 0, rest = 0; + + 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 + || me->bufrsz < (ssize_t) MNT_KERNELWATCH_MSG_MINSZ) { + DBG(MONITOR, ul_debugobj(me, " no data [rc=%zd]", me->bufrsz)); + break; + } + rest = me->bufrsz; + + for (p = me->buf; p < me->buf + me->bufrsz; p += len) { + const struct watch_notification *n = + (const struct watch_notification *) p; + + len = n->info & WATCH_INFO_LENGTH; + if (len < MNT_KERNELWATCH_MSG_MINSZ || len > rest) { + DBG(MONITOR, ul_debugobj(me, " invalid in-header lenght")); + break; + } + rest -= len; + + DBG(MONITOR, ul_debugobj(me, " watch event 0x%p " + "[len=%zu id=%d, info=%08x]", + n, len, n->info & WATCH_INFO_ID, n->info)); + + switch (n->type) { + case WATCH_TYPE_META: + switch (n->subtype) { + case WATCH_META_REMOVAL_NOTIFICATION: + DBG(MONITOR, ul_debugobj(me, " meta: watchpoint removal")); + break; + case WATCH_META_LOSS_NOTIFICATION: + DBG(MONITOR, ul_debugobj(me, " meta: data loss")); + break; + default: + DBG(MONITOR, ul_debugobj(me, " meta: another subtype")); + break; + } + break; + case WATCH_TYPE_MOUNT_NOTIFY: + DBG(MONITOR, ul_debugobj(me, " mount notify")); + status = 1; + break; + case WATCH_TYPE_SB_NOTIFY: + DBG(MONITOR, ul_debugobj(me, " superblock notify")); + status = 1; + break; + default: + DBG(MONITOR, ul_debugobj(me, " another notify type")); + break; + } + } + + 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. */ @@ -629,6 +868,7 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn, return 0; err: + DBG(MONITOR, ul_debugobj(mn, " modify epoll faild")); return -errno; } @@ -957,6 +1197,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) { @@ -1094,11 +1339,27 @@ static int test_data(struct libmnt_test *ts, int argc, char *argv[]) ssize_t sz; size_t chunksz = 0; - printf(" %s: change detected\n", filename); + printf(" %s: change detected [type=%d]\n", filename, type); switch (type) { - case MNT_MONITOR_TYPE_KERNEL: /* no data, jusr epoll */ + case MNT_MONITOR_TYPE_KERNEL: /* no data, just epoll */ + break; + + case MNT_MONITOR_TYPE_KERNELWATCH: /* watch_notification */ + data = mnt_monitor_event_data(mn, type, &sz); + + /* TODO: add functions to read mount-ID from data + * and hide all "struct watch_notification" there */ + + for (p = data; p && p < data + sz; + p += sizeof(struct watch_notification) + chunksz) { + struct watch_notification *n = + (struct watch_notification *) p; + + printf(" watch event %08x\n", n->info); + chunksz = n->info & WATCH_INFO_LENGTH; + } break; case MNT_MONITOR_TYPE_USERSPACE: /* inotify */ From f3d523724b2f40e8a23dd06a2498e36453eb72c0 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 22 Jun 2020 15:45:19 +0200 Subject: [PATCH 36/44] 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 --- libmount/src/Makemodule.am | 1 + libmount/src/init.c | 1 + libmount/src/kernelwatch.c | 167 +++++++++++++++++++++++++++++++++++++ libmount/src/libmount.h.in | 7 ++ libmount/src/libmount.sym | 5 ++ libmount/src/monitor.c | 87 +++++++------------ libmount/src/mountP.h | 1 + 7 files changed, 210 insertions(+), 59 deletions(-) create mode 100644 libmount/src/kernelwatch.c diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index d54b3e50a..c8a783bf0 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -14,6 +14,7 @@ libmount_la_SOURCES = \ 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 \ diff --git a/libmount/src/init.c b/libmount/src/init.c index 2410fcc3a..bba22421a 100644 --- a/libmount/src/init.c +++ b/libmount/src/init.c @@ -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 } }; diff --git a/libmount/src/kernelwatch.c b/libmount/src/kernelwatch.c new file mode 100644 index 000000000..6c6bf4cad --- /dev/null +++ b/libmount/src/kernelwatch.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2020 Karel Zak + * + * 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 + +#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 + * + * See also mnt_fs_set_id(). + * + * 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->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 */ diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index d3f2bf956..994078da4 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -686,6 +686,13 @@ 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_operation(void *data); +extern void *mnt_kernelwatch_next_data(void *data, ssize_t *datasz); + /* context.c */ diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 58388fdb9..e8777441e 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -362,5 +362,10 @@ MOUNT_2_36 { mnt_fs_has_fsinfo; mnt_has_fsinfo; mnt_table_parse_fsinfo; + mnt_kernelwatch_is_mount; + mnt_kernelwatch_is_valid; + mnt_kernelwatch_get_mount_id; + mnt_kernelwatch_get_operation; + mnt_kernelwatch_next_data; } MOUNT_2_35; diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index f5422a8aa..5e11de271 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -613,7 +613,6 @@ enum { }; #define MNT_KERNELWATCH_QUEUE_SIZE 256 /* number of messages 1..512 */ -#define MNT_KERNELWATCH_MSG_MINSZ sizeof(struct watch_notification) static int kernelwatch_monitor_close_fd(struct libmnt_monitor *mn __attribute__((__unused__)), struct monitor_entry *me) @@ -677,61 +676,26 @@ static int kernelwatch_monitor_read(struct libmnt_monitor *mn, do { char *p; - size_t len = 0, rest = 0; + 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 - || me->bufrsz < (ssize_t) MNT_KERNELWATCH_MSG_MINSZ) { + || me->bufrsz > (ssize_t) me->bufsz) { DBG(MONITOR, ul_debugobj(me, " no data [rc=%zd]", me->bufrsz)); break; } rest = me->bufrsz; + p = me->buf; - for (p = me->buf; p < me->buf + me->bufrsz; p += len) { - const struct watch_notification *n = - (const struct watch_notification *) p; + do { + if (!mnt_kernelwatch_is_valid(p, rest)) + break; - len = n->info & WATCH_INFO_LENGTH; - if (len < MNT_KERNELWATCH_MSG_MINSZ || len > rest) { - DBG(MONITOR, ul_debugobj(me, " invalid in-header lenght")); - break; - } - rest -= len; - - DBG(MONITOR, ul_debugobj(me, " watch event 0x%p " - "[len=%zu id=%d, info=%08x]", - n, len, n->info & WATCH_INFO_ID, n->info)); - - switch (n->type) { - case WATCH_TYPE_META: - switch (n->subtype) { - case WATCH_META_REMOVAL_NOTIFICATION: - DBG(MONITOR, ul_debugobj(me, " meta: watchpoint removal")); - break; - case WATCH_META_LOSS_NOTIFICATION: - DBG(MONITOR, ul_debugobj(me, " meta: data loss")); - break; - default: - DBG(MONITOR, ul_debugobj(me, " meta: another subtype")); - break; - } - break; - case WATCH_TYPE_MOUNT_NOTIFY: - DBG(MONITOR, ul_debugobj(me, " mount notify")); - status = 1; - break; - case WATCH_TYPE_SB_NOTIFY: - DBG(MONITOR, ul_debugobj(me, " superblock notify")); - status = 1; - break; - default: - DBG(MONITOR, ul_debugobj(me, " another notify type")); - break; - } - } + status = mnt_kernelwatch_is_mount(p); + p = mnt_kernelwatch_next_data(p, &rest); + } while (p); if (status == 1 && me->keep_data) break; @@ -1339,43 +1303,48 @@ static int test_data(struct libmnt_test *ts, int argc, char *argv[]) ssize_t sz; size_t chunksz = 0; - printf(" %s: change detected [type=%d]\n", filename, type); - 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; - /* TODO: add functions to read mount-ID from data - * and hide all "struct watch_notification" there */ + if (!mnt_kernelwatch_is_valid(data, sz) || + !mnt_kernelwatch_is_mount(data)) + break; + id = mnt_kernelwatch_get_mount_id(data); + printf(" mount ID=%d\n", id); - for (p = data; p && p < data + sz; - p += sizeof(struct watch_notification) + chunksz) { - struct watch_notification *n = - (struct watch_notification *) p; - - printf(" watch event %08x\n", n->info); - chunksz = n->info & WATCH_INFO_LENGTH; - } + 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(" inotify event mask=0x%x, name=%s\n", + 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_monitor(mn); diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 099888ab0..b3f1edd43 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -51,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 From 5a9a9ab7adb26266d8b0b314214f06f188a1683b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 22 Jun 2020 15:56:13 +0200 Subject: [PATCH 37/44] libmount: add mnt_fs_set_id() This function is necessary to force fsinfo stuff follow specific FS (id). Signed-off-by: Karel Zak --- libmount/docs/libmount-sections.txt | 1 + libmount/src/fs.c | 15 +++++++++++++++ libmount/src/libmount.h.in | 1 + libmount/src/libmount.sym | 1 + 4 files changed, 18 insertions(+) diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index da96b75b3..c5694f11a 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -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 diff --git a/libmount/src/fs.c b/libmount/src/fs.c index c3af01e9e..89d4994df 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -1310,6 +1310,21 @@ int mnt_fs_get_id(struct libmnt_fs *fs) 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; +} + /** * mnt_fs_get_parent_id: * @fs: /proc/self/mountinfo entry diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 994078da4..cecacc41f 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index e8777441e..a78bdafe6 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -367,5 +367,6 @@ MOUNT_2_36 { mnt_kernelwatch_get_mount_id; mnt_kernelwatch_get_operation; mnt_kernelwatch_next_data; + mnt_fs_set_id; } MOUNT_2_35; From 522d134700e164322a3cac9e013abd194000a56c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 22 Jun 2020 15:57:40 +0200 Subject: [PATCH 38/44] libmount: (monitor) print FS target in test Signed-off-by: Karel Zak --- libmount/src/monitor.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index 5e11de271..cc1b6ccd8 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -1286,6 +1286,7 @@ 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; @@ -1319,7 +1320,16 @@ static int test_data(struct libmnt_test *ts, int argc, char *argv[]) !mnt_kernelwatch_is_mount(data)) break; id = mnt_kernelwatch_get_mount_id(data); - printf(" mount ID=%d\n", id); + + 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); @@ -1347,6 +1357,7 @@ static int test_data(struct libmnt_test *ts, int argc, char *argv[]) fflush(stdout); } } + mnt_unref_fs(fs); mnt_unref_monitor(mn); return 0; } From 7baaf3c25137b5e3d39729120cc6a351df5d032f Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 22 Jun 2020 17:01:53 +0200 Subject: [PATCH 39/44] 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 --- libmount/src/kernelwatch.c | 22 +++++++++++++++++++++- libmount/src/libmount.h.in | 1 + libmount/src/libmount.sym | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/libmount/src/kernelwatch.c b/libmount/src/kernelwatch.c index 6c6bf4cad..cb7a30d1d 100644 --- a/libmount/src/kernelwatch.c +++ b/libmount/src/kernelwatch.c @@ -89,13 +89,33 @@ int mnt_kernelwatch_is_mount(void *data) * mnt_kernelwatch_get_mount_id * @data: event data * - * See also mnt_fs_set_id(). + * 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; diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index cecacc41f..bc5ab44e7 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -691,6 +691,7 @@ extern void *mnt_monitor_event_data(struct libmnt_monitor *mn, int type, ssize_t 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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index a78bdafe6..fa0010c3d 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -362,9 +362,12 @@ MOUNT_2_36 { 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; From bf715d42affb0fa072665bd8e3b5a2785216996b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 22 Jun 2020 17:03:58 +0200 Subject: [PATCH 40/44] libmount: add watch-queue sample Signed-off-by: Karel Zak --- libmount/Makemodule.am | 1 + libmount/samples/Makemodule.am | 11 ++++++ libmount/samples/watchqueue.c | 69 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 libmount/samples/Makemodule.am create mode 100644 libmount/samples/watchqueue.c diff --git a/libmount/Makemodule.am b/libmount/Makemodule.am index 8546c2f9b..d48797511 100644 --- a/libmount/Makemodule.am +++ b/libmount/Makemodule.am @@ -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 diff --git a/libmount/samples/Makemodule.am b/libmount/samples/Makemodule.am new file mode 100644 index 000000000..4b943fe5f --- /dev/null +++ b/libmount/samples/Makemodule.am @@ -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) diff --git a/libmount/samples/watchqueue.c b/libmount/samples/watchqueue.c new file mode 100644 index 000000000..d2d13fb52 --- /dev/null +++ b/libmount/samples/watchqueue.c @@ -0,0 +1,69 @@ + +#include +#include +#include +#include + +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"); +} From c791058243cf6d14b79242406f44bf673632f72a Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Thu, 25 Jun 2020 15:12:52 +0800 Subject: [PATCH 41/44] 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 --- libmount/src/fsinfo.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 4c3c01db1..8995b062d 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -172,8 +172,13 @@ int mnt_has_fsinfo(void) .request = FSINFO_ATTR_MOUNT_INFO, }; size_t sz = sizeof(info); + long rc; - mnt_fsinfo("/", ¶ms, sizeof(params), (char *)&info, &sz); + rc = mnt_fsinfo("/", ¶ms, sizeof(params), (char *)&info, &sz); + if (rc != 1) + have_fsinfo = 1; + else + have_fsinfo = 0; } return have_fsinfo; #else From e523affd6f8f2896fe6b0307a7cc5c5c7306b7f7 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Thu, 25 Jun 2020 15:12:57 +0800 Subject: [PATCH 42/44] 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 --- libmount/src/fs.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index 89d4994df..3321f5ea2 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -89,6 +89,44 @@ static unsigned int fsinfo_off2req(size_t member_offset) __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); \ @@ -1854,14 +1892,14 @@ static int fsinfo_buf2fs(struct libmnt_fs *fs, 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; - if ((x->attr & (MOUNT_ATTR_NOATIME|MOUNT_ATTR_STRICTATIME)) == 0) - x->attr |= MS_RELATIME; - rc = mnt_optstr_apply_flags(&fs->vfs_optstr, x->attr, + 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; } From 7003b502b6f1d0066a906f0ea51684d9084651ca Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Thu, 25 Jun 2020 15:13:07 +0800 Subject: [PATCH 43/44] 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 --- libmount/src/fsinfo.c | 40 ++++++++++++++++++++++++++++++++-------- libmount/src/mountP.h | 3 +++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/libmount/src/fsinfo.c b/libmount/src/fsinfo.c index 8995b062d..a552511ac 100644 --- a/libmount/src/fsinfo.c +++ b/libmount/src/fsinfo.c @@ -131,26 +131,50 @@ static void *fsinfo_alloc_attrs(unsigned int id, 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) { - size_t size = 0; - assert(id); assert(mounts); assert(count); DBG(UTILS, ul_debug("fsinfo: reading children for id=%u", id)); - *mounts = fsinfo_alloc_attrs(id, FSINFO_ATTR_MOUNT_CHILDREN, 0, &size); - if (!*mounts) - return -errno; - - *count = size / sizeof((*mounts)[0]) - 1; - return 0; + 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 */ /* diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index b3f1edd43..9799bbb7e 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -443,6 +443,9 @@ extern int mnt_fsinfo(const char *query, 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 */ From 117a4baffdd9e0465b7ed799dc587b2106f21ea3 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Thu, 25 Jun 2020 15:13:12 +0800 Subject: [PATCH 44/44] libmount: get all mount ids at once --- libmount/src/tab_parse.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index 8f23f5dd2..193fc7ce7 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -735,9 +735,9 @@ static int parse_fsinfo_init_fs( return rc; } -static int table_parse_fetch_chldren(struct libmnt_table *tb, - unsigned int id, struct libmnt_fs *parent, - struct libmnt_parser *pa) +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; @@ -762,8 +762,8 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, pa->has_root_fs = 1; } - /* children list */ - rc = mnt_fsinfo_get_children(id, &mounts, &count); + /* mount list */ + rc = mnt_fsinfo_get_mounts(id, &mounts, &count); if (rc != 0) goto out; if (!count) @@ -781,11 +781,6 @@ static int table_parse_fetch_chldren(struct libmnt_table *tb, rc = parse_fsinfo_init_fs(tb, fs, mounts[i].mnt_id, parent, pa); if (!rc) rc = mnt_table_add_fs(tb, fs); - if (!rc) { - rc = table_parse_fetch_chldren(tb, mnt_fs_get_id(fs), fs, pa); - if (rc) - mnt_table_remove_fs(tb, fs); - } if (rc) { if (rc > 0) { mnt_reset_fs(fs); @@ -818,7 +813,7 @@ static int __table_parse_fsinfo(struct libmnt_table *tb) if (rc < 0) goto err; - rc = table_parse_fetch_chldren(tb, id, NULL, &pa); + rc = table_parse_fetch_mounts(tb, id, NULL, &pa); if (rc < 0) goto err;