diff --git a/meson.build b/meson.build index 0b3bd6800..351ce35d0 100644 --- a/meson.build +++ b/meson.build @@ -151,6 +151,38 @@ else message('Disabling versioned libc symbols') endif +# Older versions of musl libc don't unescape entries in /etc/mtab +# Try to detect this behavour, and work around, if necessary. +detect_getmntent_needs_unescape = ''' +#define _GNU_SOURCE +#include +#include +#include +#include + +#define dir_space_tab "dir\\040space\\011tab" + +int main() +{ + const char *fake_mtab = "name " dir_space_tab " type opts 0 0\n"; + FILE *f = fmemopen((void *)fake_mtab, strlen(fake_mtab) + 1, "r"); + struct mntent *entp = getmntent(f); + fclose(f); + if(NULL == entp) + exit(EXIT_FAILURE); + if (0 == strcmp(entp->mnt_dir, dir_space_tab)) + printf("needs escaping\n"); + else + printf("no need to escape\n"); +} +''' + +result = cc.run(detect_getmntent_needs_unescape) +if result.compiled() and result.returncode() == 0 and result.stdout().strip() == 'needs escaping' + message('getmntent does not unescape') + add_project_arguments('-DGETMNTENT_NEEDS_UNESCAPING', language: 'c') +endif + # Write private test results into fuse_config.h (stored in build directory) configure_file(output: 'fuse_config.h', configuration : private_cfg) diff --git a/util/fusermount.c b/util/fusermount.c index f8124cb35..04cc769f5 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -7,7 +7,7 @@ */ /* This program does the mounting and unmounting of FUSE filesystems */ -#define _GNU_SOURCE /* for clone */ +#define _GNU_SOURCE /* for clone and strchrnul */ #include "fuse_config.h" #include "mount_util.h" @@ -63,6 +63,59 @@ static int mount_max = 1000; static int auto_unmount = 0; +#ifdef GETMNTENT_NEEDS_UNESCAPING +// Older versions of musl libc don't unescape entries in /etc/mtab + +// unescapes octal seqences like \040 in-place +// That's ok, because unescaping can not extend the length of the string. +static void unescape(char* buf) { + char* src = buf; + char* dest = buf; + while (1) { + char* next_src = strchrnul(src, '\\'); + int offset = next_src - src; + memmove(dest, src, offset); + src = next_src; + dest += offset; + + if(*src == '\0') { + *dest = *src; + return; + } + src++; + + if('0' <= src[0] && src[0] < '2' && + '0' <= src[1] && src[1] < '8' && + '0' <= src[2] && src[2] < '8') { + *dest++ = (src[0] - '0') << 6 + | (src[1] - '0') << 3 + | (src[2] - '0') << 0; + src += 3; + } else if (src[0] == '\\') { + *dest++ = '\\'; + src += 1; + } else { + *dest++ = '\\'; + } + } +} + +static struct mntent *GETMNTENT(FILE *stream) +{ + struct mntent * entp = getmntent(stream); + if(entp != NULL) { + unescape(entp->mnt_fsname); + unescape(entp->mnt_dir); + unescape(entp->mnt_type); + unescape(entp->mnt_opts); + } + return entp; +} +#else +#define GETMNTENT getmntent +#endif // GETMNTENT_NEEDS_UNESCAPING + + static const char *get_user_name(void) { struct passwd *pw = getpwuid(getuid()); @@ -169,7 +222,7 @@ static int may_unmount(const char *mnt, int quiet) uidlen = sprintf(uidstr, "%u", getuid()); found = 0; - while ((entp = getmntent(fp)) != NULL) { + while ((entp = GETMNTENT(fp)) != NULL) { if (!found && strcmp(entp->mnt_dir, mnt) == 0 && (strcmp(entp->mnt_type, "fuse") == 0 || strcmp(entp->mnt_type, "fuseblk") == 0 || @@ -261,7 +314,7 @@ static int check_is_mount_child(void *p) } count = 0; - while (getmntent(fp) != NULL) + while (GETMNTENT(fp) != NULL) count++; endmntent(fp); @@ -280,7 +333,7 @@ static int check_is_mount_child(void *p) } found = 0; - while ((entp = getmntent(fp)) != NULL) { + while ((entp = GETMNTENT(fp)) != NULL) { if (count > 0) { count--; continue; @@ -464,7 +517,7 @@ static int count_fuse_fs(void) strerror(errno)); return -1; } - while ((entp = getmntent(fp)) != NULL) { + while ((entp = GETMNTENT(fp)) != NULL) { if (strcmp(entp->mnt_type, "fuse") == 0 || strncmp(entp->mnt_type, "fuse.", 5) == 0) count ++;