mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
da15a2f6ed
add_mount(), exec_umount() and remove_mount() returned waitpid()'s pid on success rather than 0, contradicting the documented '0 or -1' contract (harmless today only because every caller tests == -1). They also tested the raw wait status, and read it uninitialized when waitpid() itself failed. Read the status only on the success path and decode it with WIFEXITED()/WEXITSTATUS(), matching how check_is_mount() in fusermount.c already does it, so these helpers return 0 or -1 as advertised. Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
629 lines
15 KiB
C
629 lines
15 KiB
C
/*
|
|
FUSE: Filesystem in Userspace
|
|
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
|
|
Architecture-independent mounting code.
|
|
|
|
This program can be distributed under the terms of the GNU LGPLv2.
|
|
See the file LGPL2.txt.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "fuse_config.h"
|
|
#include "mount_util.h"
|
|
#ifdef __linux__
|
|
#include "mount_i_linux.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <paths.h>
|
|
#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
|
|
#include <mntent.h>
|
|
#else
|
|
#define IGNORE_MTAB
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "fuse_mount_compat.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <spawn.h>
|
|
|
|
/* Environment variable for FUSE kernel device */
|
|
#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
|
|
#define FUSE_DEF_KERN_DEVICE "/dev/fuse"
|
|
|
|
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
|
|
#include <sys/mount.h>
|
|
#ifdef __NetBSD__
|
|
#include <perfuse.h>
|
|
#endif
|
|
|
|
#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
|
|
|
|
#define MS_RDONLY MNT_RDONLY
|
|
#define MS_NOSUID MNT_NOSUID
|
|
#ifdef MNT_NODEV
|
|
#define MS_NODEV MNT_NODEV
|
|
#else
|
|
#define MS_NODEV 0
|
|
#endif
|
|
#define MS_NOEXEC MNT_NOEXEC
|
|
#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
|
|
#define MS_NOATIME MNT_NOATIME
|
|
#define MS_NOSYMFOLLOW 0
|
|
#define MS_DIRSYNC 0
|
|
|
|
/* BSD doesn't have these, define as 0 */
|
|
#ifndef MS_NODIRATIME
|
|
#define MS_NODIRATIME 0
|
|
#endif
|
|
#ifndef MS_RELATIME
|
|
#define MS_RELATIME 0
|
|
#endif
|
|
#ifndef MS_STRICTATIME
|
|
#define MS_STRICTATIME 0
|
|
#endif
|
|
|
|
#endif /* BSD */
|
|
|
|
/*
|
|
* MOUNT_ATTR_* constants from linux/mount.h for the new mount API.
|
|
* If not available from system headers (older kernels), define to 0
|
|
* so the mount_attr field will indicate these are fsconfig flags.
|
|
* The new mount API (fsmount) won't be available on such systems anyway.
|
|
*/
|
|
#ifndef MOUNT_ATTR_RDONLY
|
|
#define MOUNT_ATTR_RDONLY 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NOSUID
|
|
#define MOUNT_ATTR_NOSUID 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NODEV
|
|
#define MOUNT_ATTR_NODEV 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NOEXEC
|
|
#define MOUNT_ATTR_NOEXEC 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NOATIME
|
|
#define MOUNT_ATTR_NOATIME 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_RELATIME
|
|
#define MOUNT_ATTR_RELATIME 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_STRICTATIME
|
|
#define MOUNT_ATTR_STRICTATIME 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NODIRATIME
|
|
#define MOUNT_ATTR_NODIRATIME 0
|
|
#endif
|
|
#ifndef MOUNT_ATTR_NOSYMFOLLOW
|
|
#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 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
|
|
#define mtab_needs_update(mnt) 0
|
|
#else
|
|
static int mtab_needs_update(const char *mnt)
|
|
{
|
|
int res;
|
|
struct stat stbuf;
|
|
|
|
/* If mtab is within new mount, don't touch it */
|
|
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
|
_PATH_MOUNTED[strlen(mnt)] == '/')
|
|
return 0;
|
|
|
|
/*
|
|
* Skip mtab update if /etc/mtab:
|
|
*
|
|
* - doesn't exist,
|
|
* - is on a read-only filesystem.
|
|
*/
|
|
res = lstat(_PATH_MOUNTED, &stbuf);
|
|
if (res == -1) {
|
|
if (errno == ENOENT)
|
|
return 0;
|
|
} else {
|
|
uid_t ruid;
|
|
int err;
|
|
|
|
ruid = getuid();
|
|
if (ruid != 0)
|
|
setreuid(0, -1);
|
|
|
|
res = access(_PATH_MOUNTED, W_OK);
|
|
err = (res == -1) ? errno : 0;
|
|
if (ruid != 0)
|
|
setreuid(ruid, -1);
|
|
|
|
if (err == EROFS)
|
|
return 0;
|
|
|
|
res = access("/run/mount/utab", F_OK);
|
|
if (res == -1)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif /* IGNORE_MTAB */
|
|
|
|
/*
|
|
* @brief Spawn @cmd with a clean environment and the given signal mask.
|
|
*
|
|
* @param[out] pid Child pid on success.
|
|
* @param[in] cmd Absolute path of the executable.
|
|
* @param[in] argv NULL-terminated argument vector.
|
|
* @param[in] mask Signal mask to install in the child.
|
|
* @return 0 on success, positive errno on failure.
|
|
*/
|
|
static int fuse_mount_spawn(pid_t *pid, const char *cmd, char *const argv[],
|
|
sigset_t *mask)
|
|
{
|
|
posix_spawnattr_t attr;
|
|
char *const envp[] = { NULL };
|
|
uid_t ruid = getuid();
|
|
uid_t euid = geteuid();
|
|
int res;
|
|
|
|
res = posix_spawnattr_init(&attr);
|
|
if (res) {
|
|
fprintf(stderr, "Failed to init posix_spawn attr for cmd %s: %s\n",
|
|
cmd, strerror(res));
|
|
return res;
|
|
}
|
|
|
|
res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK);
|
|
if (res) {
|
|
fprintf(stderr, "Failed to set posix_spawn flags for cmd %s: %s\n",
|
|
cmd, strerror(res));
|
|
goto out_destroy;
|
|
}
|
|
|
|
res = posix_spawnattr_setsigmask(&attr, mask);
|
|
if (res) {
|
|
fprintf(stderr, "Failed to set posix_spawn sigmask for cmd %s: %s\n",
|
|
cmd, strerror(res));
|
|
goto out_destroy;
|
|
}
|
|
|
|
/*
|
|
* Reached via setuid-root fusermount (ruid != euid, euid 0) or via
|
|
* lib/mount.c in a privileged daemon after a direct mount (ruid ==
|
|
* euid). /bin/mount keys "restricted" mode off the real uid, so raise
|
|
* it to euid for the fusermount case; ruid == euid needs no change.
|
|
* Done in this single-threaded parent as posix_spawn cannot do it.
|
|
*/
|
|
if (ruid != euid && setreuid(euid, -1) == -1) {
|
|
res = errno;
|
|
fprintf(stderr, "Failed to set real uid for cmd %s: %s\n",
|
|
cmd, strerror(res));
|
|
goto out_destroy;
|
|
}
|
|
|
|
res = posix_spawn(pid, cmd, NULL, &attr, argv, envp);
|
|
|
|
/*
|
|
* A failed restore would leave this process with the raised real uid;
|
|
* refuse to continue rather than run on privileged.
|
|
*/
|
|
if (ruid != euid && setreuid(ruid, -1) == -1) {
|
|
fprintf(stderr, "Failed to restore real uid for cmd %s: %s\n",
|
|
cmd, strerror(errno));
|
|
abort();
|
|
}
|
|
|
|
out_destroy:
|
|
posix_spawnattr_destroy(&attr);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* @return caller expects 0 or -1
|
|
*/
|
|
static int add_mount(const char *progname, const char *fsname,
|
|
const char *mnt, const char *type, const char *opts)
|
|
{
|
|
int res;
|
|
int status;
|
|
pid_t pid;
|
|
sigset_t blockmask;
|
|
sigset_t oldmask;
|
|
const char *cmd = "/bin/mount";
|
|
|
|
sigemptyset(&blockmask);
|
|
sigaddset(&blockmask, SIGCHLD);
|
|
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
|
if (res == -1) {
|
|
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
char const * const argv[] = {
|
|
cmd, "--no-canonicalize", "-i", "-f", "-t", type, "-o", opts,
|
|
fsname, mnt, NULL
|
|
};
|
|
|
|
res = fuse_mount_spawn(&pid, cmd, (char * const *) argv, &oldmask);
|
|
if (res) {
|
|
fprintf(stderr, "%s: failed to execute %s: %s\n",
|
|
progname, cmd, strerror(res));
|
|
res = -1;
|
|
goto out_restore;
|
|
}
|
|
|
|
res = waitpid(pid, &status, 0);
|
|
if (res == -1)
|
|
fprintf(stderr, "%s: waitpid of %d: %s\n", progname,
|
|
pid, strerror(errno));
|
|
else
|
|
res = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : -1;
|
|
|
|
out_restore:
|
|
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
|
const char *mnt, const char *type, const char *opts)
|
|
{
|
|
if (!mtab_needs_update(mnt))
|
|
return 0;
|
|
|
|
return add_mount(progname, fsname, mnt, type, opts);
|
|
}
|
|
|
|
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
|
|
{
|
|
int res;
|
|
int status;
|
|
pid_t pid;
|
|
sigset_t blockmask;
|
|
sigset_t oldmask;
|
|
const char *cmd = "/bin/umount";
|
|
|
|
sigemptyset(&blockmask);
|
|
sigaddset(&blockmask, SIGCHLD);
|
|
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
|
if (res == -1) {
|
|
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
char const * const argv_lazy[] = {
|
|
cmd, "-i", "-l", rel_mnt, NULL
|
|
};
|
|
char const * const argv_normal[] = {
|
|
cmd, "-i", rel_mnt, NULL
|
|
};
|
|
char const * const *argv = lazy ? argv_lazy : argv_normal;
|
|
|
|
res = fuse_mount_spawn(&pid, cmd, (char * const *) argv, &oldmask);
|
|
if (res) {
|
|
fprintf(stderr, "%s: failed to execute %s: %s\n",
|
|
progname, cmd, strerror(res));
|
|
res = -1;
|
|
goto out_restore;
|
|
}
|
|
|
|
res = waitpid(pid, &status, 0);
|
|
if (res == -1)
|
|
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
|
else
|
|
res = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : -1;
|
|
|
|
out_restore:
|
|
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
|
return res;
|
|
|
|
}
|
|
|
|
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
|
|
const char *rel_mnt, int lazy)
|
|
{
|
|
if (!mtab_needs_update(abs_mnt)) {
|
|
int res = umount2(rel_mnt, lazy ? 2 : 0);
|
|
if (res == -1)
|
|
fprintf(stderr, "%s: failed to unmount %s: %s\n",
|
|
progname, abs_mnt, strerror(errno));
|
|
return res;
|
|
}
|
|
|
|
return exec_umount(progname, rel_mnt, lazy);
|
|
}
|
|
|
|
static int remove_mount(const char *progname, const char *mnt)
|
|
{
|
|
int res;
|
|
int status;
|
|
pid_t pid;
|
|
sigset_t blockmask;
|
|
sigset_t oldmask;
|
|
const char *cmd = "/bin/umount";
|
|
|
|
sigemptyset(&blockmask);
|
|
sigaddset(&blockmask, SIGCHLD);
|
|
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
|
if (res == -1) {
|
|
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
char const * const argv[] = {
|
|
cmd, "--no-canonicalize", "-i", "--fake", mnt, NULL
|
|
};
|
|
|
|
res = fuse_mount_spawn(&pid, cmd, (char * const *) argv, &oldmask);
|
|
if (res) {
|
|
fprintf(stderr, "%s: failed to execute %s: %s\n",
|
|
progname, cmd, strerror(res));
|
|
res = -1;
|
|
goto out_restore;
|
|
}
|
|
|
|
res = waitpid(pid, &status, 0);
|
|
if (res == -1)
|
|
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
|
else
|
|
res = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : -1;
|
|
|
|
out_restore:
|
|
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
|
return res;
|
|
}
|
|
|
|
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
|
|
{
|
|
if (!mtab_needs_update(mnt))
|
|
return 0;
|
|
|
|
return remove_mount(progname, mnt);
|
|
}
|
|
|
|
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
|
|
{
|
|
char buf[PATH_MAX];
|
|
char *copy;
|
|
char *dst;
|
|
char *end;
|
|
const char *lastcomp;
|
|
const char *toresolv;
|
|
|
|
if (!orig[0]) {
|
|
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
|
|
orig);
|
|
return NULL;
|
|
}
|
|
|
|
copy = strdup(orig);
|
|
if (copy == NULL) {
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
|
return NULL;
|
|
}
|
|
|
|
toresolv = copy;
|
|
lastcomp = NULL;
|
|
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
|
if (end[0] != '/') {
|
|
char *tmp;
|
|
end[1] = '\0';
|
|
tmp = strrchr(copy, '/');
|
|
if (tmp == NULL) {
|
|
lastcomp = copy;
|
|
toresolv = ".";
|
|
} else {
|
|
lastcomp = tmp + 1;
|
|
if (tmp == copy)
|
|
toresolv = "/";
|
|
}
|
|
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
|
lastcomp = NULL;
|
|
toresolv = copy;
|
|
}
|
|
else if (tmp)
|
|
tmp[0] = '\0';
|
|
}
|
|
if (realpath(toresolv, buf) == NULL) {
|
|
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
|
strerror(errno));
|
|
free(copy);
|
|
return NULL;
|
|
}
|
|
if (lastcomp == NULL)
|
|
dst = strdup(buf);
|
|
else {
|
|
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
|
if (dst) {
|
|
unsigned buflen = strlen(buf);
|
|
if (buflen && buf[buflen-1] == '/')
|
|
sprintf(dst, "%s%s", buf, lastcomp);
|
|
else
|
|
sprintf(dst, "%s/%s", buf, lastcomp);
|
|
}
|
|
}
|
|
free(copy);
|
|
if (dst == NULL)
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
|
return dst;
|
|
}
|
|
|
|
int fuse_mnt_check_fuseblk(void)
|
|
{
|
|
char buf[256];
|
|
FILE *f = fopen("/proc/filesystems", "r");
|
|
if (!f)
|
|
return 1;
|
|
|
|
while (fgets(buf, sizeof(buf), f))
|
|
if (strstr(buf, "fuseblk\n")) {
|
|
fclose(f);
|
|
return 1;
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
int fuse_mnt_parse_fuse_fd(const char *mountpoint)
|
|
{
|
|
unsigned int fd;
|
|
int len = 0;
|
|
|
|
if (mountpoint == NULL) {
|
|
fprintf(stderr, "Invalid null-ptr mount-point!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
|
|
len == strlen(mountpoint)) {
|
|
if (fd > INT_MAX) {
|
|
fprintf(stderr, "%s: fd=%u > INT_MAX\n", __func__, fd);
|
|
return -1;
|
|
}
|
|
return (int)fd;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int fuse_mnt_add_mount_helper(const char *mnt, const char *source,
|
|
const char *type, const char *mtab_opts)
|
|
{
|
|
#ifndef IGNORE_MTAB
|
|
if (geteuid() == 0) {
|
|
char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
|
|
int res;
|
|
|
|
if (!newmnt)
|
|
return -1;
|
|
|
|
res = fuse_mnt_add_mount("fuse", source, newmnt, type,
|
|
mtab_opts);
|
|
free(newmnt);
|
|
return res;
|
|
}
|
|
#endif
|
|
(void)mnt;
|
|
(void)source;
|
|
(void)type;
|
|
(void)mtab_opts;
|
|
return 0;
|
|
}
|
|
|
|
const char *fuse_mnt_get_devname(void)
|
|
{
|
|
const char *devname = getenv(FUSE_KERN_DEVICE_ENV);
|
|
struct stat stbuf;
|
|
|
|
if (!devname)
|
|
return FUSE_DEF_KERN_DEVICE;
|
|
|
|
/* Validation 1: Must start with /dev/ */
|
|
if (strncmp(devname, "/dev/", 5) != 0) {
|
|
fprintf(stderr,
|
|
"fuse: invalid device name from %s: %s (must start with /dev/)\n",
|
|
FUSE_KERN_DEVICE_ENV, devname);
|
|
return "/dev/fuse";
|
|
}
|
|
|
|
/* Validation 2: Must be a character device */
|
|
if (stat(devname, &stbuf) == 0) {
|
|
if (!S_ISCHR(stbuf.st_mode)) {
|
|
fprintf(stderr,
|
|
"fuse: invalid device from %s: %s (not a character device)\n",
|
|
FUSE_KERN_DEVICE_ENV, devname);
|
|
return "/dev/fuse";
|
|
}
|
|
/* Valid character device */
|
|
return devname;
|
|
}
|
|
/*
|
|
* stat() failed - device might not exist yet, but path is valid.
|
|
* Let it through and fail later in open() with a better error message.
|
|
*/
|
|
return devname;
|
|
}
|
|
|
|
char *fuse_mnt_build_source(const char *fsname, const char *subtype,
|
|
const char *devname, bool use_subtype_prefix)
|
|
{
|
|
char *source;
|
|
int ret;
|
|
|
|
if (use_subtype_prefix && fsname && subtype) {
|
|
ret = asprintf(&source, "%s#%s", subtype, fsname);
|
|
} else {
|
|
const char *src_str;
|
|
|
|
if (fsname)
|
|
src_str = fsname;
|
|
else if (subtype)
|
|
src_str = subtype;
|
|
else
|
|
src_str = devname;
|
|
ret = asprintf(&source, "%s", src_str);
|
|
}
|
|
|
|
if (ret == -1)
|
|
return NULL;
|
|
|
|
return source;
|
|
}
|
|
|
|
char *fuse_mnt_build_type(int blkdev, const char *subtype)
|
|
{
|
|
char *type;
|
|
int ret;
|
|
|
|
if (subtype)
|
|
ret = asprintf(&type, "%s.%s", blkdev ? "fuseblk" : "fuse",
|
|
subtype);
|
|
else
|
|
ret = asprintf(&type, "%s", blkdev ? "fuseblk" : "fuse");
|
|
|
|
if (ret == -1)
|
|
return NULL;
|
|
|
|
return type;
|
|
}
|