mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
Merge remote-tracking branch 'origin/master' into djwong-dev3
Merge upstream origin/master into bsbernd/fuse-service-container. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
This commit is contained in:
+1
-1
@@ -58,7 +58,7 @@ foreach ex : threaded_examples
|
||||
install: false)
|
||||
endforeach
|
||||
|
||||
if platform != 'dragonfly' and add_languages('cpp', required : false)
|
||||
if platform != 'dragonfly' and add_languages('cpp', native : false, required : false)
|
||||
executable('passthrough_hp', 'passthrough_hp.cc',
|
||||
dependencies: [ thread_dep, libfuse_dep ],
|
||||
install: false)
|
||||
|
||||
+10
-6
@@ -30,8 +30,8 @@
|
||||
#define FUSE_DAEMONIZE_SUCCESS 0
|
||||
#define FUSE_DAEMONIZE_FAILURE 1
|
||||
|
||||
/* Private/internal data */
|
||||
struct fuse_daemonize {
|
||||
/* Private/internal data */
|
||||
struct fuse_daemonize {
|
||||
unsigned int flags;
|
||||
int signal_pipe_wr; /* write end for signaling parent */
|
||||
int death_pipe_rd; /* read end, POLLHUP when parent dies */
|
||||
@@ -39,8 +39,8 @@
|
||||
int stop_pipe_wr; /* write end for stop signal */
|
||||
pthread_t watcher;
|
||||
bool watcher_started;
|
||||
_Atomic bool active;
|
||||
_Atomic bool daemonized;
|
||||
_Atomic bool active; /* daemonization in progress */
|
||||
_Atomic bool daemonized; /* daemonized successfully */
|
||||
_Atomic bool mounted; /* fuse_session_mount() completed */
|
||||
_Atomic bool got_init; /* got FUSE_INIT */
|
||||
};
|
||||
@@ -211,18 +211,22 @@ int fuse_daemonize_early_start(unsigned int flags)
|
||||
struct fuse_daemonize *dm = &daemonize;
|
||||
int err = 0;
|
||||
|
||||
if (dm->active)
|
||||
return 0; /* already called, no need for another fork */
|
||||
|
||||
dm->flags = flags;
|
||||
dm->signal_pipe_wr = -1;
|
||||
dm->death_pipe_rd = -1;
|
||||
dm->stop_pipe_rd = -1;
|
||||
dm->stop_pipe_wr = -1;
|
||||
dm->active = true;
|
||||
|
||||
if (!(flags & FUSE_DAEMONIZE_NO_CHDIR))
|
||||
(void)chdir("/");
|
||||
|
||||
if (!(flags & FUSE_DAEMONIZE_NO_BACKGROUND))
|
||||
if (!(flags & FUSE_DAEMONIZE_NO_BACKGROUND)) {
|
||||
dm->active = true;
|
||||
err = do_daemonize(dm);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
+38
-22
@@ -4470,14 +4470,25 @@ static void *session_sync_init_worker(void *data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable synchronous FUSE_INIT and start worker thread */
|
||||
static int session_start_sync_init(struct fuse_session *se, int fd)
|
||||
/*
|
||||
* Enable synchronous FUSE_INIT and start worker thread
|
||||
*
|
||||
* @param is_sync_init set to true on return if sync init is enabled
|
||||
* Returns 0 on success or kernel does not support it, -errno on unexpected failure
|
||||
*/
|
||||
static int session_start_sync_init(struct fuse_session *se, int fd,
|
||||
bool *is_sync_init)
|
||||
{
|
||||
int err, res;
|
||||
|
||||
if (!se->want_sync_init) {
|
||||
/* SYNC_INIT is required for io_uring(?) */
|
||||
if ((se->uring.enable && !fuse_daemonize_is_used()) || se->debug)
|
||||
*is_sync_init = false;
|
||||
|
||||
/*
|
||||
* Older fuse servers do not set want_sync_init or start the new
|
||||
* daemonize code, so they get async init.
|
||||
*/
|
||||
if (!fuse_daemonize_is_used() || !se->want_sync_init) {
|
||||
if (se->debug)
|
||||
fuse_log(FUSE_LOG_DEBUG,
|
||||
"fuse: sync init not enabled\n");
|
||||
return 0;
|
||||
@@ -4533,6 +4544,8 @@ static int session_start_sync_init(struct fuse_session *se, int fd)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*is_sync_init = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4590,8 +4603,9 @@ static int session_wait_sync_init_completion(struct fuse_session *se)
|
||||
*/
|
||||
static int new_api_fusermount(struct fuse_session *se,
|
||||
const char *mountpoint,
|
||||
const char *mnt_opts,
|
||||
int *sock_fd, pid_t *fusermount_pid)
|
||||
const char *mtab_opts,
|
||||
int *sock_fd, pid_t *fusermount_pid,
|
||||
bool *is_sync_init)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
@@ -4604,7 +4618,7 @@ static int new_api_fusermount(struct fuse_session *se,
|
||||
fuse_log(FUSE_LOG_ERR, "fuse: sync init completion failed\n");
|
||||
|
||||
/* Call fusermount3 with --sync-init */
|
||||
fd = mount_fusermount_obtain_fd(mountpoint, se->mo, mnt_opts, sock_fd,
|
||||
fd = mount_fusermount_obtain_fd(mountpoint, se->mo, mtab_opts, sock_fd,
|
||||
fusermount_pid);
|
||||
if (fd < 0) {
|
||||
fuse_log(FUSE_LOG_ERR,
|
||||
@@ -4614,7 +4628,7 @@ static int new_api_fusermount(struct fuse_session *se,
|
||||
|
||||
/* Start worker thread with correct fd from fusermount3 */
|
||||
se->fd = fd;
|
||||
err = session_start_sync_init(se, fd);
|
||||
err = session_start_sync_init(se, fd, is_sync_init);
|
||||
if (err) {
|
||||
fuse_log(FUSE_LOG_ERR,
|
||||
"fuse: failed to start sync init worker\n");
|
||||
@@ -4643,11 +4657,12 @@ static int fuse_session_mount_new_api(struct fuse_session *se,
|
||||
int sock_fd = -1;
|
||||
pid_t fusermount_pid = -1;
|
||||
int res, err;
|
||||
char *mnt_opts = NULL;
|
||||
char *mnt_opts_with_fd = NULL;
|
||||
char *mtab_opts = NULL;
|
||||
char *mtab_opts_with_fd = NULL;
|
||||
char fd_opt[32];
|
||||
bool is_sync_init = false;
|
||||
|
||||
res = fuse_kern_mount_get_base_mnt_opts(se->mo, &mnt_opts);
|
||||
res = fuse_kern_mount_get_base_mtab_opts(se->mo, &mtab_opts);
|
||||
err = -EIO;
|
||||
if (res == -1) {
|
||||
fuse_log(FUSE_LOG_ERR,
|
||||
@@ -4662,26 +4677,27 @@ static int fuse_session_mount_new_api(struct fuse_session *se,
|
||||
}
|
||||
|
||||
se->fd = fd;
|
||||
err = session_start_sync_init(se, fd);
|
||||
err = session_start_sync_init(se, fd, &is_sync_init);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
snprintf(fd_opt, sizeof(fd_opt), "fd=%i", fd);
|
||||
err = -ENOMEM;
|
||||
if (fuse_opt_add_opt(&mnt_opts_with_fd, mnt_opts) == -1 ||
|
||||
fuse_opt_add_opt(&mnt_opts_with_fd, fd_opt) == -1) {
|
||||
if (fuse_opt_add_opt(&mtab_opts_with_fd, mtab_opts) == -1 ||
|
||||
fuse_opt_add_opt(&mtab_opts_with_fd, fd_opt) == -1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Try to mount directly */
|
||||
err = fuse_kern_fsmount_mo(mountpoint, se->mo, mnt_opts_with_fd);
|
||||
err = fuse_kern_fsmount_mo(mountpoint, se->mo, mtab_opts_with_fd);
|
||||
|
||||
/* If mount failed with EPERM, fall back to fusermount3 with sync-init */
|
||||
if (err < 0 && errno == EPERM) {
|
||||
close(fd);
|
||||
se->fd = -1;
|
||||
fd = new_api_fusermount(se, mountpoint, mnt_opts,
|
||||
&sock_fd, &fusermount_pid);
|
||||
fd = new_api_fusermount(se, mountpoint, mtab_opts,
|
||||
&sock_fd, &fusermount_pid,
|
||||
&is_sync_init);
|
||||
if (fd < 0) {
|
||||
err = fd;
|
||||
goto err_with_sock;
|
||||
@@ -4708,13 +4724,14 @@ err:
|
||||
se->fd = -1;
|
||||
se->error = err;
|
||||
}
|
||||
se->is_sync_init = is_sync_init;
|
||||
|
||||
/* Wait for synchronous FUSE_INIT to complete */
|
||||
if (session_wait_sync_init_completion(se) < 0)
|
||||
fuse_log(FUSE_LOG_ERR, "fuse: sync init completion failed\n");
|
||||
|
||||
se->is_sync_init = true;
|
||||
free(mnt_opts);
|
||||
free(mnt_opts_with_fd);
|
||||
free(mtab_opts);
|
||||
free(mtab_opts_with_fd);
|
||||
return fd;
|
||||
}
|
||||
#else
|
||||
@@ -4788,7 +4805,6 @@ int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
|
||||
out:
|
||||
se->fd = fd;
|
||||
se->mountpoint = mountpoint;
|
||||
|
||||
fuse_daemonize_early_set_mounted();
|
||||
|
||||
return 0;
|
||||
|
||||
+19
-11
@@ -416,6 +416,7 @@ int fuse_main_real_versioned(int argc, char *argv[],
|
||||
struct fuse_cmdline_opts opts;
|
||||
int res;
|
||||
struct fuse_loop_config *loop_config = NULL;
|
||||
bool self_daemonize = false;
|
||||
|
||||
if (fuse_parse_cmdline(&args, &opts) != 0)
|
||||
return 1;
|
||||
@@ -445,6 +446,21 @@ int fuse_main_real_versioned(int argc, char *argv[],
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* The application might have already started daemonization itself */
|
||||
if (!fuse_daemonize_early_is_active()) {
|
||||
int daemonize_early_flags = 0;
|
||||
|
||||
if (opts.foreground)
|
||||
daemonize_early_flags |= FUSE_DAEMONIZE_NO_BACKGROUND;
|
||||
|
||||
res = fuse_daemonize_early_start(daemonize_early_flags);
|
||||
if (res != 0) {
|
||||
fuse_log(FUSE_LOG_ERR, "fuse: daemonize_early_start failed\n");
|
||||
goto out1;
|
||||
}
|
||||
self_daemonize = true;
|
||||
}
|
||||
|
||||
fuse = _fuse_new_31(&args, op, op_size, version, user_data);
|
||||
if (fuse == NULL) {
|
||||
res = 3;
|
||||
@@ -457,22 +473,14 @@ int fuse_main_real_versioned(int argc, char *argv[],
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/*
|
||||
* fuse_daemonize() already checks and fails then, but we need to
|
||||
* handle it gracefully here, as this is done libfuse internal
|
||||
* and caller didn't ask to daemonize with old API.
|
||||
*/
|
||||
if (!fuse_daemonize_early_is_active()) {
|
||||
if (fuse_daemonize(opts.foreground) != 0) {
|
||||
res = 5;
|
||||
goto out3;
|
||||
}
|
||||
}
|
||||
if (fuse_set_signal_handlers(se) != 0) {
|
||||
res = 6;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
if (self_daemonize)
|
||||
fuse_daemonize_early_success();
|
||||
|
||||
if (opts.singlethread)
|
||||
res = fuse_loop(fuse);
|
||||
else {
|
||||
|
||||
+39
-23
@@ -622,19 +622,20 @@ out_close:
|
||||
* Wrapper for fuse_kern_fsmount that accepts struct mount_opts
|
||||
* @mnt: mountpoint
|
||||
* @mo: mount options
|
||||
* @mnt_opts: mount options to pass to the kernel
|
||||
* @mtab_opts: options recorded in /etc/mtab via fuse_mnt_add_mount_helper();
|
||||
* see fuse_kern_fsmount() for details
|
||||
*
|
||||
* Returns: 0 on success, -1 on failure with errno set
|
||||
*/
|
||||
int fuse_kern_fsmount_mo(const char *mnt, const struct mount_opts *mo,
|
||||
const char *mnt_opts)
|
||||
const char *mtab_opts)
|
||||
{
|
||||
/* codeql[cpp/path-injection] verification is in the function */
|
||||
const char *devname = fuse_mnt_get_devname();
|
||||
|
||||
return fuse_kern_fsmount(mnt, mo->flags, mo->blkdev, mo->fsname,
|
||||
mo->subtype, devname, mo->kernel_opts,
|
||||
mnt_opts);
|
||||
mtab_opts);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -642,13 +643,16 @@ int fuse_kern_fsmount_mo(const char *mnt, const struct mount_opts *mo,
|
||||
* Complete the mount operation with an already-opened fd
|
||||
* @mnt: mountpoint
|
||||
* @mo: mount options
|
||||
* @mnt_opts: mount options to pass to the kernel
|
||||
* @mtab_opts: mtab/utab record string written via fuse_mnt_add_mount_helper().
|
||||
* Built by fuse_kern_mount_get_base_mtab_opts(); intentionally
|
||||
* overlaps with mo->kernel_opts so /etc/mtab reflects the options
|
||||
* the kernel saw.
|
||||
*
|
||||
* Returns: 0 on success, -1 on failure,
|
||||
* FUSE_MOUNT_FALLBACK_NEEDED if fusermount should be used
|
||||
*/
|
||||
int fuse_kern_do_mount(const char *mnt, struct mount_opts *mo,
|
||||
const char *mnt_opts)
|
||||
const char *mtab_opts)
|
||||
{
|
||||
char *source = NULL;
|
||||
char *type = NULL;
|
||||
@@ -708,7 +712,7 @@ int fuse_kern_do_mount(const char *mnt, struct mount_opts *mo,
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
res = fuse_mnt_add_mount_helper(mnt, source, type, mnt_opts);
|
||||
res = fuse_mnt_add_mount_helper(mnt, source, type, mtab_opts);
|
||||
if (res == -1)
|
||||
goto out_umount;
|
||||
|
||||
@@ -726,7 +730,7 @@ out_close:
|
||||
}
|
||||
|
||||
static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
const char *mnt_opts)
|
||||
const char *mtab_opts)
|
||||
{
|
||||
int fd;
|
||||
int res;
|
||||
@@ -735,7 +739,7 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
res = fuse_kern_do_mount(mnt, mo, mnt_opts);
|
||||
res = fuse_kern_do_mount(mnt, mo, mtab_opts);
|
||||
if (res) {
|
||||
close(fd);
|
||||
return res;
|
||||
@@ -744,16 +748,20 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int get_mnt_flag_opts(char **mnt_optsp, int flags)
|
||||
/*
|
||||
* Append the flag-mirror prefix (rw/nosuid/nodev/...) of the mtab record
|
||||
* to @mtab_optsp, derived from the MS_* bitmask in @flags.
|
||||
*/
|
||||
static int get_mtab_flag_opts(char **mtab_optsp, int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
|
||||
if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mtab_optsp, "rw") == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
|
||||
fuse_opt_add_opt(mtab_optsp, mount_flags[i].opt) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -792,13 +800,21 @@ void destroy_mount_opts(struct mount_opts *mo)
|
||||
free(mo);
|
||||
}
|
||||
|
||||
int fuse_kern_mount_get_base_mnt_opts(const struct mount_opts *mo, char **mnt_optsp)
|
||||
/*
|
||||
* Build the mtab/utab record string for this mount: flag-mirrors of MS_*
|
||||
* (rw/nosuid/nodev/...) + the kernel-bound -o options + the mtab-only
|
||||
* annotations. The result is what fuse_mnt_add_mount_helper() writes into
|
||||
* /etc/mtab (or /run/mount/utab). The overlap with @mo->kernel_opts is
|
||||
* intentional so the mtab line reflects what the kernel saw.
|
||||
*/
|
||||
int fuse_kern_mount_get_base_mtab_opts(const struct mount_opts *mo,
|
||||
char **mtab_optsp)
|
||||
{
|
||||
if (get_mnt_flag_opts(mnt_optsp, mo->flags) == -1)
|
||||
if (get_mtab_flag_opts(mtab_optsp, mo->flags) == -1)
|
||||
return -1;
|
||||
if (mo->kernel_opts && fuse_opt_add_opt(mnt_optsp, mo->kernel_opts) == -1)
|
||||
if (mo->kernel_opts && fuse_opt_add_opt(mtab_optsp, mo->kernel_opts) == -1)
|
||||
return -1;
|
||||
if (mo->mtab_opts && fuse_opt_add_opt(mnt_optsp, mo->mtab_opts) == -1)
|
||||
if (mo->mtab_opts && fuse_opt_add_opt(mtab_optsp, mo->mtab_opts) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -806,13 +822,13 @@ int fuse_kern_mount_get_base_mnt_opts(const struct mount_opts *mo, char **mnt_op
|
||||
int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
|
||||
{
|
||||
int res = -1;
|
||||
char *mnt_opts = NULL;
|
||||
char *mtab_opts = NULL;
|
||||
|
||||
res = -1;
|
||||
if (fuse_kern_mount_get_base_mnt_opts(mo, &mnt_opts) == -1)
|
||||
if (fuse_kern_mount_get_base_mtab_opts(mo, &mtab_opts) == -1)
|
||||
goto out;
|
||||
|
||||
res = fuse_mount_sys(mountpoint, mo, mnt_opts);
|
||||
res = fuse_mount_sys(mountpoint, mo, mtab_opts);
|
||||
if (res >= 0 && mo->auto_unmount) {
|
||||
if(0 > setup_auto_unmount(mountpoint, 0)) {
|
||||
// Something went wrong, let's umount like in fuse_mount_sys.
|
||||
@@ -821,14 +837,14 @@ int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
|
||||
}
|
||||
} else if (res == FUSE_MOUNT_FALLBACK_NEEDED) {
|
||||
if (mo->fusermount_opts &&
|
||||
fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
|
||||
fuse_opt_add_opt(&mtab_opts, mo->fusermount_opts) == -1)
|
||||
goto out;
|
||||
|
||||
if (mo->subtype) {
|
||||
char *tmp_opts = NULL;
|
||||
|
||||
res = -1;
|
||||
if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
|
||||
if (fuse_opt_add_opt(&tmp_opts, mtab_opts) == -1 ||
|
||||
fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
|
||||
free(tmp_opts);
|
||||
goto out;
|
||||
@@ -838,13 +854,13 @@ int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
|
||||
free(tmp_opts);
|
||||
if (res == -1)
|
||||
res = fuse_mount_fusermount(mountpoint, mo,
|
||||
mnt_opts, 0);
|
||||
mtab_opts, 0);
|
||||
} else {
|
||||
res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
|
||||
res = fuse_mount_fusermount(mountpoint, mo, mtab_opts, 0);
|
||||
}
|
||||
}
|
||||
out:
|
||||
free(mnt_opts);
|
||||
free(mtab_opts);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
+26
-16
@@ -39,6 +39,7 @@
|
||||
* Convert MS_* mount flags to MOUNT_ATTR_* mount attributes.
|
||||
* These flags are passed to fsmount(), not fsconfig().
|
||||
* Mount attributes control mount-point level behavior.
|
||||
* To called after set_ms_flags() which consumes the fsconfig flags.
|
||||
*
|
||||
* @attrs MOUNT_ATTR flags, built from MS_ flags
|
||||
* @return remaining flags
|
||||
@@ -108,8 +109,12 @@ static void log_fsconfig_kmsg(int fd)
|
||||
|
||||
/*
|
||||
* Apply VFS superblock (fsconfig) flags to the filesystem context.
|
||||
* Only handles flags that are filesystem parameters (ro, sync, dirsync).
|
||||
* Mount attributes (nosuid, nodev, etc.) are handled separately via fsmount().
|
||||
* Handles the fsconfig leg of every entry whose is_fsconfig is set
|
||||
* (ro, rw, sync, async, dirsync). Mount attributes (nosuid, nodev, etc.)
|
||||
* are handled separately via fsmount().
|
||||
*
|
||||
* Entries that have *both* legs (ro/rw) leave the MS_ bit in *ms_flags
|
||||
* so that ms_flags_to_mount_attrs() can also pick them up.
|
||||
*
|
||||
* @ms_flags flags to set, outvalue are the remaining flags
|
||||
* @return 0 on success, negative error code on failure
|
||||
@@ -120,8 +125,7 @@ static int set_ms_flags(int fsfd, unsigned long *ms_flags)
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL && flags != 0; i++) {
|
||||
/* Only process fsconfig flags (mount_attr == 0) with on==1 */
|
||||
if (mount_flags[i].mount_attr || !mount_flags[i].on)
|
||||
if (!mount_flags[i].is_fsconfig || !mount_flags[i].on)
|
||||
continue;
|
||||
|
||||
if (!(flags & mount_flags[i].flag))
|
||||
@@ -137,7 +141,14 @@ static int set_ms_flags(int fsfd, unsigned long *ms_flags)
|
||||
|
||||
return -save_errno;
|
||||
}
|
||||
flags &= ~mount_flags[i].flag;
|
||||
|
||||
/*
|
||||
* Only consume the bit if no fsmount mount-attr leg is
|
||||
* also pending for this option. Otherwise leave it for
|
||||
* ms_flags_to_mount_attrs() to apply via fsmount().
|
||||
*/
|
||||
if (!mount_flags[i].mount_attr)
|
||||
flags &= ~mount_flags[i].flag;
|
||||
}
|
||||
|
||||
*ms_flags = flags;
|
||||
@@ -303,12 +314,11 @@ static int apply_mount_opts(int fsfd, const char *opts)
|
||||
* not fsconfig().
|
||||
*
|
||||
* These string options (nosuid, nodev, etc.) are reconstructed
|
||||
* Skip mtab-only options - they're for /run/mount/utab, not kernel
|
||||
* from MS_* flags by get_mnt_flag_opts() in lib/mount.c and
|
||||
* get_mnt_opts() in util/fusermount.c. Both the library path
|
||||
* (via fuse_kern_mount_get_base_mnt_opts) and fusermount3 path
|
||||
* from MS_* flags by get_mtab_flag_opts() in lib/mount.c and
|
||||
* get_mtab_opts() in util/fusermount.c. Both the library path
|
||||
* (via fuse_kern_mount_get_base_mtab_opts) and fusermount3 path
|
||||
* rebuild these strings from the flags bitmask and pass them in
|
||||
* mnt_opts. They must be filtered here because they are mount
|
||||
* mtab_opts. They must be filtered here because they are mount
|
||||
* attributes (passed to fsmount via MOUNT_ATTR_*), not
|
||||
* filesystem parameters (which would be passed to fsconfig).
|
||||
*
|
||||
@@ -332,7 +342,7 @@ static int apply_mount_opts(int fsfd, const char *opts)
|
||||
int fuse_kern_fsmount(const char *mnt, unsigned long flags, int blkdev,
|
||||
const char *fsname, const char *subtype,
|
||||
const char *source_dev, const char *kernel_opts,
|
||||
const char *mnt_opts)
|
||||
const char *mtab_opts)
|
||||
{
|
||||
char *type = NULL;
|
||||
char *source = NULL;
|
||||
@@ -397,13 +407,13 @@ int fuse_kern_fsmount(const char *mnt, unsigned long flags, int blkdev,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Apply additional mount options */
|
||||
err = apply_mount_opts(fsfd, mnt_opts);
|
||||
/* Apply mtab options (overlap with kernel_opts is filtered and tolerated) */
|
||||
err = apply_mount_opts(fsfd, mtab_opts);
|
||||
if (err < 0) {
|
||||
log_fsconfig_kmsg(fsfd);
|
||||
fprintf(stderr,
|
||||
"fuse: failed to apply additional mount options '%s'\n",
|
||||
mnt_opts);
|
||||
"fuse: failed to apply mtab options '%s'\n",
|
||||
mtab_opts);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
@@ -448,7 +458,7 @@ int fuse_kern_fsmount(const char *mnt, unsigned long flags, int blkdev,
|
||||
goto out_close_mntfd;
|
||||
}
|
||||
|
||||
err = fuse_mnt_add_mount_helper(mnt, source, type, mnt_opts);
|
||||
err = fuse_mnt_add_mount_helper(mnt, source, type, mtab_opts);
|
||||
if (err == -1)
|
||||
goto out_umount;
|
||||
|
||||
|
||||
+10
-5
@@ -31,7 +31,8 @@ struct mount_opts {
|
||||
|
||||
int fuse_kern_mount_prepare(const char *mnt, struct mount_opts *mo);
|
||||
|
||||
int fuse_kern_mount_get_base_mnt_opts(const struct mount_opts *mo, char **mnt_optsp);
|
||||
int fuse_kern_mount_get_base_mtab_opts(const struct mount_opts *mo,
|
||||
char **mtab_optsp);
|
||||
|
||||
/**
|
||||
* Mount using the new Linux mount API (fsopen/fsconfig/fsmount/move_mount)
|
||||
@@ -41,18 +42,22 @@ int fuse_kern_mount_get_base_mnt_opts(const struct mount_opts *mo, char **mnt_op
|
||||
* @fsname: filesystem name (or NULL)
|
||||
* @subtype: filesystem subtype (or NULL)
|
||||
* @source_dev: device name for building source string
|
||||
* @kernel_opts: kernel mount options string
|
||||
* @mnt_opts: additional mount options to pass to the kernel
|
||||
* @kernel_opts: kernel mount options applied via fsconfig()
|
||||
* @mtab_opts: options recorded in /etc/mtab (or /run/mount/utab) via
|
||||
* fuse_mnt_add_mount_helper(). May overlap with @kernel_opts
|
||||
* because /etc/mtab is expected to display kernel-visible
|
||||
* options; the overlap is filtered before fsconfig where
|
||||
* needed and is idempotent at the kernel.
|
||||
*
|
||||
* Returns: 0 on success, -1 on failure with errno set
|
||||
*/
|
||||
int fuse_kern_fsmount(const char *mnt, unsigned long flags, int blkdev,
|
||||
const char *fsname, const char *subtype,
|
||||
const char *source_dev, const char *kernel_opts,
|
||||
const char *mnt_opts);
|
||||
const char *mtab_opts);
|
||||
|
||||
int fuse_kern_fsmount_mo(const char *mnt, const struct mount_opts *mo,
|
||||
const char *mnt_opts);
|
||||
const char *mtab_opts);
|
||||
int mount_fusermount_obtain_fd(const char *mountpoint,
|
||||
struct mount_opts *mo,
|
||||
const char *opts, int *sock_fd_out,
|
||||
|
||||
+28
-22
@@ -108,26 +108,32 @@
|
||||
#define MOUNT_ATTR_NOSYMFOLLOW 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* is_fsconfig and mount_attr are independent: ro/rw need both legs
|
||||
* (SB_RDONLY on the superblock via fsconfig SET_FLAG, plus
|
||||
* MOUNT_ATTR_RDONLY on the vfsmount via fsmount). Everything else is
|
||||
* exclusively one or the other.
|
||||
*/
|
||||
const struct mount_flags mount_flags[] = {
|
||||
/* opt flag on safe mount_attr */
|
||||
{"rw", MS_RDONLY, 0, 1, MOUNT_ATTR_RDONLY}, /* fsconfig */
|
||||
{"ro", MS_RDONLY, 1, 1, MOUNT_ATTR_RDONLY}, /* fsconfig */
|
||||
{"suid", MS_NOSUID, 0, 0, MOUNT_ATTR_NOSUID}, /* fsmount */
|
||||
{"nosuid", MS_NOSUID, 1, 1, MOUNT_ATTR_NOSUID}, /* fsmount */
|
||||
{"dev", MS_NODEV, 0, 1, MOUNT_ATTR_NODEV}, /* fsmount */
|
||||
{"nodev", MS_NODEV, 1, 1, MOUNT_ATTR_NODEV}, /* fsmount */
|
||||
{"exec", MS_NOEXEC, 0, 1, MOUNT_ATTR_NOEXEC}, /* fsmount */
|
||||
{"noexec", MS_NOEXEC, 1, 1, MOUNT_ATTR_NOEXEC}, /* fsmount */
|
||||
{"async", MS_SYNCHRONOUS, 0, 1, 0}, /* fsconfig */
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1, 0}, /* fsconfig */
|
||||
{"noatime", MS_NOATIME, 1, 1, MOUNT_ATTR_NOATIME}, /* fsmount */
|
||||
{"nodiratime", MS_NODIRATIME, 1, 1, MOUNT_ATTR_NODIRATIME}, /* fsmount */
|
||||
{"norelatime", MS_RELATIME, 0, 1, MOUNT_ATTR_RELATIME}, /* fsmount */
|
||||
{"nostrictatime", MS_STRICTATIME, 0, 1, MOUNT_ATTR_STRICTATIME},/* fsmount */
|
||||
{"symfollow", MS_NOSYMFOLLOW, 0, 1, MOUNT_ATTR_NOSYMFOLLOW},/* fsmount */
|
||||
{"nosymfollow", MS_NOSYMFOLLOW, 1, 1, MOUNT_ATTR_NOSYMFOLLOW},/* fsmount */
|
||||
{"dirsync", MS_DIRSYNC, 1, 1, 0}, /* fsconfig */
|
||||
{NULL, 0, 0, 0, 0}
|
||||
/* opt flag on safe fsconfig mount_attr */
|
||||
{"rw", MS_RDONLY, 0, 1, 1, MOUNT_ATTR_RDONLY},
|
||||
{"ro", MS_RDONLY, 1, 1, 1, MOUNT_ATTR_RDONLY},
|
||||
{"suid", MS_NOSUID, 0, 0, 0, MOUNT_ATTR_NOSUID},
|
||||
{"nosuid", MS_NOSUID, 1, 1, 0, MOUNT_ATTR_NOSUID},
|
||||
{"dev", MS_NODEV, 0, 1, 0, MOUNT_ATTR_NODEV},
|
||||
{"nodev", MS_NODEV, 1, 1, 0, MOUNT_ATTR_NODEV},
|
||||
{"exec", MS_NOEXEC, 0, 1, 0, MOUNT_ATTR_NOEXEC},
|
||||
{"noexec", MS_NOEXEC, 1, 1, 0, MOUNT_ATTR_NOEXEC},
|
||||
{"async", MS_SYNCHRONOUS, 0, 1, 1, 0},
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1, 1, 0},
|
||||
{"noatime", MS_NOATIME, 1, 1, 0, MOUNT_ATTR_NOATIME},
|
||||
{"nodiratime", MS_NODIRATIME, 1, 1, 0, MOUNT_ATTR_NODIRATIME},
|
||||
{"norelatime", MS_RELATIME, 0, 1, 0, MOUNT_ATTR_RELATIME},
|
||||
{"nostrictatime", MS_STRICTATIME, 0, 1, 0, MOUNT_ATTR_STRICTATIME},
|
||||
{"symfollow", MS_NOSYMFOLLOW, 0, 1, 0, MOUNT_ATTR_NOSYMFOLLOW},
|
||||
{"nosymfollow", MS_NOSYMFOLLOW, 1, 1, 0, MOUNT_ATTR_NOSYMFOLLOW},
|
||||
{"dirsync", MS_DIRSYNC, 1, 1, 1, 0},
|
||||
{NULL, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
#ifdef IGNORE_MTAB
|
||||
@@ -472,7 +478,7 @@ int fuse_mnt_parse_fuse_fd(const char *mountpoint)
|
||||
}
|
||||
|
||||
int fuse_mnt_add_mount_helper(const char *mnt, const char *source,
|
||||
const char *type, const char *mnt_opts)
|
||||
const char *type, const char *mtab_opts)
|
||||
{
|
||||
#ifndef IGNORE_MTAB
|
||||
if (geteuid() == 0) {
|
||||
@@ -483,7 +489,7 @@ int fuse_mnt_add_mount_helper(const char *mnt, const char *source,
|
||||
return -1;
|
||||
|
||||
res = fuse_mnt_add_mount("fuse", source, newmnt, type,
|
||||
mnt_opts);
|
||||
mtab_opts);
|
||||
free(newmnt);
|
||||
return res;
|
||||
}
|
||||
@@ -491,7 +497,7 @@ int fuse_mnt_add_mount_helper(const char *mnt, const char *source,
|
||||
(void)mnt;
|
||||
(void)source;
|
||||
(void)type;
|
||||
(void)mnt_opts;
|
||||
(void)mtab_opts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -18,7 +18,8 @@ struct mount_flags {
|
||||
unsigned long flag;
|
||||
int on;
|
||||
int safe; /* used by fusermount */
|
||||
unsigned long mount_attr; /* MOUNT_ATTR_* value for fsmount (0 = fsconfig flag) */
|
||||
int is_fsconfig; /* apply via fsconfig SET_FLAG (superblock-level) */
|
||||
unsigned long mount_attr; /* MOUNT_ATTR_* for fsmount (0 = none) */
|
||||
};
|
||||
extern const struct mount_flags mount_flags[];
|
||||
|
||||
@@ -34,7 +35,7 @@ int fuse_mnt_parse_fuse_fd(const char *mountpoint);
|
||||
/* Helper functions for mount operations */
|
||||
const char *fuse_mnt_get_devname(void);
|
||||
int fuse_mnt_add_mount_helper(const char *mnt, const char *source,
|
||||
const char *type, const char *mnt_opts);
|
||||
const char *type, const char *mtab_opts);
|
||||
|
||||
/* Build source and type strings for mounting */
|
||||
char *fuse_mnt_build_source(const char *fsname, const char *subtype,
|
||||
|
||||
+7
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
TEST_CMD="pytest -vv --tb=short --maxfail=1 --log-level=INFO --log-cli-level=INFO test/"
|
||||
TEST_CMD="meson test -C . --print-errorlogs"
|
||||
SAN="-Db_sanitize=address,undefined"
|
||||
|
||||
# not default
|
||||
@@ -136,8 +136,12 @@ sanitized_build()
|
||||
sudo chmod 4755 util/fuservicemount3
|
||||
fi
|
||||
|
||||
# Test as root and regular user
|
||||
sudo env PATH=$PATH ${TEST_CMD}
|
||||
# Test as root and regular user. Give the root run a distinct
|
||||
# meson log basename so its meson-logs/testlog.* files don't end
|
||||
# up owned by root and block the subsequent user run from writing
|
||||
# them.
|
||||
sudo env PATH=$PATH ${TEST_CMD} --logbase=testlog-root
|
||||
|
||||
# Cleanup temporary files (since they are now owned by root)
|
||||
sudo rm -rf test/.pytest_cache/ test/__pycache__
|
||||
|
||||
|
||||
+21
-5
@@ -35,19 +35,35 @@ td += executable('test_loop_config', 'test_loop_config.c',
|
||||
install: false)
|
||||
|
||||
test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.py',
|
||||
'util.py', 'test_ctests.py', 'test_custom_io.py' ]
|
||||
'util.py', 'test_ctests.py', 'test_custom_io.py',
|
||||
'test_mount_state.py' ]
|
||||
td += custom_target('test_scripts', input: test_scripts,
|
||||
output: test_scripts, build_by_default: true,
|
||||
command: ['cp', '-fPp',
|
||||
'@INPUT@', meson.current_build_dir() ])
|
||||
|
||||
# Provide something helpful when running 'ninja test'
|
||||
|
||||
if meson.is_subproject()
|
||||
test('libfuse is a subproject, skipping tests', executable('wrong_command',
|
||||
'wrong_command.c', install: false,
|
||||
c_args: [ '-DMESON_IS_SUBPROJECT' ]))
|
||||
else
|
||||
test('wrong_command', executable('wrong_command', 'wrong_command.c',
|
||||
install: false))
|
||||
# Run the pytest suite via 'meson test'. The same definition works
|
||||
# both as a regular user and under sudo: invoke 'meson test -C build'
|
||||
# for the unprivileged subset, or 'sudo -E meson test -C build' to
|
||||
# also exercise the tests that require root (they pick the uid up at
|
||||
# runtime via os.geteuid()).
|
||||
pytest = find_program('pytest', required: false)
|
||||
if pytest.found()
|
||||
test('pytest', pytest,
|
||||
args: ['-vv', '--tb=short', '--maxfail=1',
|
||||
'--log-level=INFO', '--log-cli-level=INFO',
|
||||
meson.current_build_dir()],
|
||||
depends: td,
|
||||
workdir: meson.project_build_root(),
|
||||
timeout: 600,
|
||||
is_parallel: false)
|
||||
else
|
||||
test('pytest not found', executable('wrong_command',
|
||||
'wrong_command.c', install: false))
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
Tests that observable mount state (as exposed by /proc/self/mountinfo)
|
||||
matches the options requested at mount time.
|
||||
|
||||
Existing tests check filesystem behavior (read/write/xattr/...) but
|
||||
never inspect the post-mount metadata recorded by the kernel. That
|
||||
metadata is populated differently by the legacy mount(2) path and the
|
||||
new fsopen/fsconfig/fsmount path, so an option dropped on one path can
|
||||
go undetected — the subtype regression in 5e9e16d6 is one example.
|
||||
These tests assert what /proc/self/mountinfo reports for each mount,
|
||||
so a parity bug between the two paths fails loudly.
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import pytest
|
||||
from contextlib import contextmanager
|
||||
from os.path import join as pjoin
|
||||
from util import (wait_for_mount, umount, cleanup, base_cmdline, basename,
|
||||
fuse_test_marker, parse_mountinfo)
|
||||
|
||||
pytestmark = fuse_test_marker()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def hello_mount(tmpdir, output_checker, name, options=()):
|
||||
mnt_dir = str(tmpdir)
|
||||
cmdline = base_cmdline + [pjoin(basename, 'example', name),
|
||||
'-f', mnt_dir]
|
||||
if name == 'hello_ll':
|
||||
cmdline.append('-s')
|
||||
if options:
|
||||
cmdline += ['-o', ','.join(options)]
|
||||
mp = subprocess.Popen(cmdline, stdout=output_checker.fd,
|
||||
stderr=output_checker.fd)
|
||||
try:
|
||||
wait_for_mount(mp, mnt_dir)
|
||||
yield mnt_dir
|
||||
except:
|
||||
cleanup(mp, mnt_dir)
|
||||
raise
|
||||
else:
|
||||
umount(mp, mnt_dir)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
def test_mountinfo_baseline(tmpdir, output_checker, name):
|
||||
# libfuse's add_default_subtype() (lib/helper.c) defaults the
|
||||
# subtype to basename(argv[0]) when the caller didn't pass
|
||||
# -o fsname=/-o subtype=, so the bare-mount fstype is
|
||||
# 'fuse.<example-name>', not 'fuse'. The override case is what
|
||||
# test_mountinfo_subtype below verifies; here we just assert the
|
||||
# fuse-ness and the standard kernel-side identity options.
|
||||
with hello_mount(tmpdir, output_checker, name) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
assert info is not None, 'mountpoint not found in /proc/self/mountinfo'
|
||||
assert info['fstype'] in ('fuse', 'fuse.' + name), \
|
||||
'unexpected fstype %r (expected fuse or fuse.%s)' % \
|
||||
(info['fstype'], name)
|
||||
assert any(o.startswith('user_id=') for o in info['super_options'])
|
||||
assert any(o.startswith('group_id=') for o in info['super_options'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
def test_mountinfo_subtype(tmpdir, output_checker, name):
|
||||
# Regression guard for 5e9e16d6: the new mount API needs an
|
||||
# explicit fsconfig(SET_STRING,"subtype",...). Without it the
|
||||
# kernel records fstype=='fuse' (or whatever basename default
|
||||
# leaks through) instead of the user-requested 'fuse.<subtype>'.
|
||||
# An explicit -o subtype= must override the basename default.
|
||||
with hello_mount(tmpdir, output_checker, name,
|
||||
('subtype=mysub',)) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
assert info is not None
|
||||
assert info['fstype'] == 'fuse.mysub', \
|
||||
'explicit subtype not propagated: fstype=%r' % info['fstype']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
def test_mountinfo_fsname(tmpdir, output_checker, name):
|
||||
with hello_mount(tmpdir, output_checker, name,
|
||||
('fsname=myfsname',)) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
assert info is not None
|
||||
assert info['source'] == 'myfsname', \
|
||||
'fsname not propagated: source=%r' % info['source']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
def test_mountinfo_subtype_fsname(tmpdir, output_checker, name):
|
||||
with hello_mount(tmpdir, output_checker, name,
|
||||
('subtype=mysub', 'fsname=myfsname')) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
assert info is not None
|
||||
assert info['fstype'] == 'fuse.mysub'
|
||||
# 'mysub#myfsname' is the ENODEV-fallback form when the kernel
|
||||
# rejects fuse.<subtype>; accept either so the test isn't fragile.
|
||||
assert info['source'] in ('myfsname', 'mysub#myfsname'), \
|
||||
'unexpected source: %r' % info['source']
|
||||
|
||||
|
||||
# (label, options, must-be-in mount_options, must-NOT-be-in mount_options)
|
||||
#
|
||||
# Library defaults are MS_NOSUID|MS_NODEV (lib/mount.c,
|
||||
# util/fusermount.c), so a no-options mount is expected to land
|
||||
# with both attrs set. The negation forms (suid/dev) clear the default
|
||||
# flags via lib/mount.c:set_mount_flag(), which on the new mount API
|
||||
# path means MOUNT_ATTR_NOSUID/MOUNT_ATTR_NODEV are not set in the
|
||||
# fsmount() call. Asserting their absence catches a routing bug where
|
||||
# the negation wasn't honored.
|
||||
ATTR_CASES = [
|
||||
('default', (), ('rw', 'nosuid', 'nodev'), ('ro',)),
|
||||
('ro', ('ro',), ('ro',), ('rw',)),
|
||||
('nosuid', ('nosuid',), ('nosuid',), ()),
|
||||
('nodev', ('nodev',), ('nodev',), ()),
|
||||
]
|
||||
|
||||
# suid/dev are root-only: the kernel rejects MS_NOSUID/MS_NODEV being
|
||||
# cleared for unprivileged mounts, and fusermount3 hard-codes them on
|
||||
# anyway (util/fusermount.c:988).
|
||||
ATTR_CASES_ROOT = [
|
||||
('suid', ('suid',), (), ('nosuid',)),
|
||||
('dev', ('dev',), (), ('nodev',)),
|
||||
]
|
||||
|
||||
|
||||
def _check_attrs(info, must_have, must_not_have):
|
||||
assert info is not None
|
||||
for opt in must_have:
|
||||
assert opt in info['mount_options'], \
|
||||
'%r missing from mount_options=%r' % (opt, info['mount_options'])
|
||||
for opt in must_not_have:
|
||||
assert opt not in info['mount_options'], \
|
||||
'unexpected %r in mount_options=%r' % (opt, info['mount_options'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
@pytest.mark.parametrize('label,opts,must_have,must_not_have', ATTR_CASES,
|
||||
ids=[c[0] for c in ATTR_CASES])
|
||||
def test_mountinfo_attrs(tmpdir, output_checker, name,
|
||||
label, opts, must_have, must_not_have):
|
||||
with hello_mount(tmpdir, output_checker, name, opts) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
_check_attrs(info, must_have, must_not_have)
|
||||
# ro/rw also surface in super_options; if we asked for ro the
|
||||
# superblock must agree. Catches a path that sets MOUNT_ATTR_RDONLY
|
||||
# but forgets the FSCONFIG-side MS_RDONLY (or vice versa).
|
||||
if 'ro' in must_have:
|
||||
assert 'ro' in info['super_options'], \
|
||||
'ro on mount but rw on superblock: super_options=%r' % \
|
||||
info['super_options']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ('hello', 'hello_ll'))
|
||||
@pytest.mark.parametrize('label,opts,must_have,must_not_have',
|
||||
ATTR_CASES_ROOT,
|
||||
ids=[c[0] for c in ATTR_CASES_ROOT])
|
||||
def test_mountinfo_attrs_root(tmpdir, output_checker, name,
|
||||
label, opts, must_have, must_not_have):
|
||||
if os.getuid() != 0:
|
||||
pytest.skip('clearing nosuid/nodev requires root')
|
||||
with hello_mount(tmpdir, output_checker, name, opts) as mnt:
|
||||
info = parse_mountinfo(mnt)
|
||||
_check_attrs(info, must_have, must_not_have)
|
||||
@@ -125,6 +125,51 @@ def umount(mount_process, mnt_dir):
|
||||
pytest.fail('mount process did not terminate')
|
||||
|
||||
|
||||
def parse_mountinfo(mnt_dir):
|
||||
'''Return the /proc/self/mountinfo entry for *mnt_dir*, or None.
|
||||
|
||||
Parses the line for the mountpoint and returns a dict with keys:
|
||||
'mountpoint' - the mount point path (str)
|
||||
'fstype' - filesystem type as the kernel sees it,
|
||||
e.g. 'fuse' or 'fuse.<subtype>' (str)
|
||||
'source' - mount source field, e.g. 'hello' or
|
||||
'<subtype>#<fsname>' fallback form (str)
|
||||
'mount_options' - per-mount options/attrs (set of str), e.g.
|
||||
{'rw','nosuid','nodev','noatime','relatime'}
|
||||
'super_options' - superblock options from the filesystem (set of
|
||||
str), e.g. {'rw','user_id=1000','group_id=1000',
|
||||
'default_permissions','allow_other','max_read=...'}
|
||||
|
||||
These fields are exactly what /proc/self/mountinfo exposes (see
|
||||
`man 5 proc.5_mountinfo`); they capture the post-mount state that
|
||||
differs between the legacy mount(2) path and the new fsopen/fsconfig/
|
||||
fsmount path. Asserting on this dict catches bugs where one path
|
||||
drops a parameter the other passes (e.g. `subtype` showing up in
|
||||
`fstype`, `user=` ending up only in /run/mount/utab, ...).
|
||||
'''
|
||||
target = os.path.realpath(mnt_dir)
|
||||
with open('/proc/self/mountinfo') as fh:
|
||||
for line in fh:
|
||||
parts = line.rstrip('\n').split(' ')
|
||||
try:
|
||||
sep = parts.index('-')
|
||||
except ValueError:
|
||||
continue
|
||||
if len(parts) < sep + 4 or sep < 6:
|
||||
continue
|
||||
mountpoint = parts[4].replace('\\040', ' ')
|
||||
if mountpoint != target:
|
||||
continue
|
||||
return {
|
||||
'mountpoint': mountpoint,
|
||||
'fstype': parts[sep + 1],
|
||||
'source': parts[sep + 2].replace('\\040', ' '),
|
||||
'mount_options': set(parts[5].split(',')),
|
||||
'super_options': set(parts[sep + 3].split(',')),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def safe_sleep(secs):
|
||||
'''Like time.sleep(), but sleep for at least *secs*
|
||||
|
||||
|
||||
+56
-45
@@ -575,34 +575,41 @@ static int add_option(char **optsp, const char *opt, unsigned expand)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_mnt_opts(int flags, const char *opts, char **mnt_optsp)
|
||||
/*
|
||||
* Build the mtab/utab record string for this mount: flag-mirrors of MS_*
|
||||
* (rw/nosuid/nodev/...) + the kernel-bound -o options + "user=<n>" when
|
||||
* mounted by a non-root user. The result is what add_mount() writes into
|
||||
* /etc/mtab (or /run/mount/utab). The overlap with @opts is intentional so
|
||||
* the mtab line reflects what the kernel saw.
|
||||
*/
|
||||
static int get_mtab_opts(int flags, const char *opts, char **mtab_optsp)
|
||||
{
|
||||
int i;
|
||||
int l;
|
||||
|
||||
if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
|
||||
if (!(flags & MS_RDONLY) && add_option(mtab_optsp, "rw", 0) == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
|
||||
add_option(mtab_optsp, mount_flags[i].opt, 0) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_option(mnt_optsp, opts, 0) == -1)
|
||||
if (add_option(mtab_optsp, opts, 0) == -1)
|
||||
return -1;
|
||||
/* remove comma from end of opts*/
|
||||
l = strlen(*mnt_optsp);
|
||||
if ((*mnt_optsp)[l-1] == ',')
|
||||
(*mnt_optsp)[l-1] = '\0';
|
||||
l = strlen(*mtab_optsp);
|
||||
if ((*mtab_optsp)[l-1] == ',')
|
||||
(*mtab_optsp)[l-1] = '\0';
|
||||
if (getuid() != 0) {
|
||||
const char *user = get_user_name();
|
||||
if (user == NULL)
|
||||
return -1;
|
||||
|
||||
if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
|
||||
if (add_option(mtab_optsp, "user=", strlen(user)) == -1)
|
||||
return -1;
|
||||
strcat(*mnt_optsp, user);
|
||||
strcat(*mtab_optsp, user);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -674,7 +681,11 @@ struct mount_params {
|
||||
/* Generated mount parameters */
|
||||
char *source; /* Mount source string */
|
||||
char *type; /* Filesystem type string */
|
||||
char *mnt_opts; /* Mount table options */
|
||||
|
||||
/* mtab/utab record content: MS_* flag mirrors + kernel-bound -o opts
|
||||
* + user=<n> (non-root)
|
||||
*/
|
||||
char *mtab_opts;
|
||||
|
||||
/* Pointer for optbuf manipulation */
|
||||
char *optbuf_end; /* Points to end of optbuf for sprintf */
|
||||
@@ -687,7 +698,7 @@ static void free_mount_params(struct mount_params *mp)
|
||||
free(mp->subtype);
|
||||
free(mp->source);
|
||||
free(mp->type);
|
||||
free(mp->mnt_opts);
|
||||
free(mp->mtab_opts);
|
||||
memset(mp, 0, sizeof(*mp));
|
||||
}
|
||||
|
||||
@@ -801,7 +812,7 @@ static int prepare_mount(const char *opts, struct mount_params *mp)
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
res = get_mnt_opts(mp->flags, mp->optbuf, &mp->mnt_opts);
|
||||
res = get_mtab_opts(mp->flags, mp->optbuf, &mp->mtab_opts);
|
||||
if (res == -1)
|
||||
goto err;
|
||||
|
||||
@@ -868,7 +879,7 @@ static int perform_mount(const char *mnt, struct mount_params *mp)
|
||||
|
||||
static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
|
||||
int fd, const char *opts, const char *dev, char **sourcep,
|
||||
char **mnt_optsp)
|
||||
char **mtab_optsp)
|
||||
{
|
||||
struct mount_params mp = { .fd = fd }; /* implicit zero of other params */
|
||||
int res;
|
||||
@@ -888,7 +899,7 @@ static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
|
||||
|
||||
*sourcep = mp.source;
|
||||
*typep = mp.type;
|
||||
*mnt_optsp = mp.mnt_opts;
|
||||
*mtab_optsp = mp.mtab_opts;
|
||||
|
||||
/* Free only the intermediate allocations, not the returned ones */
|
||||
free(mp.fsname);
|
||||
@@ -1005,9 +1016,9 @@ struct mount_context {
|
||||
const char *dev;
|
||||
struct stat stbuf;
|
||||
char *source;
|
||||
char *mnt_opts;
|
||||
char *mtab_opts; /* mtab/utab record string for add_mount() */
|
||||
char *x_opts;
|
||||
char *kern_mnt_opts; /* mnt_opts with removed x_opts */
|
||||
char *kern_mnt_opts; /* user-provided -o opts with x-* removed */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1081,7 +1092,7 @@ static int mount_fuse_finish_fsmount(const char *mnt,
|
||||
.rootmode = ctx->stbuf.st_mode & S_IFMT,
|
||||
.dev = ctx->dev,
|
||||
};
|
||||
char *final_mnt_opts = NULL;
|
||||
char *final_mtab_opts = NULL;
|
||||
|
||||
res = prepare_mount(ctx->kern_mnt_opts, &mp);
|
||||
if (res == -1)
|
||||
@@ -1091,27 +1102,27 @@ static int mount_fuse_finish_fsmount(const char *mnt,
|
||||
* Merge x-options if running as root, root is allowed to update
|
||||
* /etc/mtab or /run/mount/utab
|
||||
*/
|
||||
final_mnt_opts = mp.mnt_opts;
|
||||
final_mtab_opts = mp.mtab_opts;
|
||||
if (geteuid() == 0 && ctx->x_opts && strlen(ctx->x_opts) > 0) {
|
||||
char *x_mnt_opts = NULL;
|
||||
char *x_mtab_opts = NULL;
|
||||
int ret;
|
||||
|
||||
if (strlen(mp.mnt_opts) > 0)
|
||||
ret = asprintf(&x_mnt_opts, "%s,%s",
|
||||
mp.mnt_opts, ctx->x_opts);
|
||||
if (strlen(mp.mtab_opts) > 0)
|
||||
ret = asprintf(&x_mtab_opts, "%s,%s",
|
||||
mp.mtab_opts, ctx->x_opts);
|
||||
else
|
||||
ret = asprintf(&x_mnt_opts, "%s", ctx->x_opts);
|
||||
ret = asprintf(&x_mtab_opts, "%s", ctx->x_opts);
|
||||
|
||||
if (ret < 0)
|
||||
goto fail_free_params;
|
||||
|
||||
final_mnt_opts = x_mnt_opts;
|
||||
final_mtab_opts = x_mtab_opts;
|
||||
}
|
||||
|
||||
/* Use new mount API */
|
||||
res = fuse_kern_fsmount(mnt, mp.flags, mp.blkdev,
|
||||
mp.fsname, mp.subtype, ctx->dev,
|
||||
mp.optbuf, final_mnt_opts);
|
||||
mp.optbuf, final_mtab_opts);
|
||||
if (res == -1)
|
||||
goto fail_free_merged;
|
||||
|
||||
@@ -1124,7 +1135,7 @@ static int mount_fuse_finish_fsmount(const char *mnt,
|
||||
|
||||
/* Store results in context */
|
||||
ctx->source = mp.source;
|
||||
ctx->mnt_opts = final_mnt_opts;
|
||||
ctx->mtab_opts = final_mtab_opts;
|
||||
*type = mp.type;
|
||||
|
||||
res = 0;
|
||||
@@ -1133,15 +1144,15 @@ static int mount_fuse_finish_fsmount(const char *mnt,
|
||||
free(mp.fsname);
|
||||
free(mp.subtype);
|
||||
free(mp.optbuf);
|
||||
if (final_mnt_opts != mp.mnt_opts)
|
||||
free(mp.mnt_opts);
|
||||
if (final_mtab_opts != mp.mtab_opts)
|
||||
free(mp.mtab_opts);
|
||||
|
||||
out:
|
||||
return res;
|
||||
|
||||
fail_free_merged:
|
||||
if (final_mnt_opts != mp.mnt_opts)
|
||||
free(final_mnt_opts);
|
||||
if (final_mtab_opts != mp.mtab_opts)
|
||||
free(final_mtab_opts);
|
||||
fail_free_params:
|
||||
free_mount_params(&mp);
|
||||
fail:
|
||||
@@ -1158,7 +1169,7 @@ static int mount_fuse(const char *mnt, const char *opts, const char **type)
|
||||
const char *dev = fuse_mnt_get_devname();
|
||||
struct stat stbuf;
|
||||
char *source = NULL;
|
||||
char *mnt_opts = NULL;
|
||||
char *mtab_opts = NULL;
|
||||
const char *real_mnt = mnt;
|
||||
int mountpoint_fd = -1;
|
||||
char *do_mount_opts = NULL;
|
||||
@@ -1183,7 +1194,7 @@ static int mount_fuse(const char *mnt, const char *opts, const char **type)
|
||||
restore_privs();
|
||||
if (res != -1)
|
||||
res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
|
||||
fd, do_mount_opts, dev, &source, &mnt_opts);
|
||||
fd, do_mount_opts, dev, &source, &mtab_opts);
|
||||
|
||||
if (mountpoint_fd != -1)
|
||||
close(mountpoint_fd);
|
||||
@@ -1203,24 +1214,24 @@ static int mount_fuse(const char *mnt, const char *opts, const char **type)
|
||||
* Add back the options starting with "x-" to opts from
|
||||
* do_mount. +2 for ',' and '\0'
|
||||
*/
|
||||
size_t mnt_opts_len = strlen(mnt_opts);
|
||||
size_t x_mnt_opts_len = mnt_opts_len+
|
||||
size_t mtab_opts_len = strlen(mtab_opts);
|
||||
size_t x_mtab_opts_len = mtab_opts_len +
|
||||
strlen(x_prefixed_opts) + 2;
|
||||
char *x_mnt_opts = calloc(1, x_mnt_opts_len);
|
||||
char *x_mtab_opts = calloc(1, x_mtab_opts_len);
|
||||
|
||||
if (mnt_opts_len) {
|
||||
strcpy(x_mnt_opts, mnt_opts);
|
||||
strncat(x_mnt_opts, ",", 2);
|
||||
if (mtab_opts_len) {
|
||||
strcpy(x_mtab_opts, mtab_opts);
|
||||
strncat(x_mtab_opts, ",", 2);
|
||||
}
|
||||
|
||||
strncat(x_mnt_opts, x_prefixed_opts,
|
||||
x_mnt_opts_len - mnt_opts_len - 2);
|
||||
strncat(x_mtab_opts, x_prefixed_opts,
|
||||
x_mtab_opts_len - mtab_opts_len - 2);
|
||||
|
||||
free(mnt_opts);
|
||||
mnt_opts = x_mnt_opts;
|
||||
free(mtab_opts);
|
||||
mtab_opts = x_mtab_opts;
|
||||
}
|
||||
|
||||
res = add_mount(source, mnt, *type, mnt_opts);
|
||||
res = add_mount(source, mnt, *type, mtab_opts);
|
||||
if (res == -1) {
|
||||
/* Can't clean up mount in a non-racy way */
|
||||
goto fail_close_fd;
|
||||
@@ -1229,7 +1240,7 @@ static int mount_fuse(const char *mnt, const char *opts, const char **type)
|
||||
|
||||
out_free:
|
||||
free(source);
|
||||
free(mnt_opts);
|
||||
free(mtab_opts);
|
||||
free(x_prefixed_opts);
|
||||
free(do_mount_opts);
|
||||
|
||||
@@ -1288,7 +1299,7 @@ static int mount_fuse_sync_init(const char *mnt, const char *opts,
|
||||
out:
|
||||
close(fd);
|
||||
free(ctx.source);
|
||||
free(ctx.mnt_opts);
|
||||
free(ctx.mtab_opts);
|
||||
free(ctx.x_opts);
|
||||
free(ctx.kern_mnt_opts);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user