libnilfs: fix nilfs_open failure for mount points with escaped characters

nilfs_find_fs(), called from nilfs_open(), parses "/proc/mounts" using its
own routines, but these parsing routines have a problem in that they
cannot handle escaped path strings.  For example, if the mount point path
name contains spaces, the search will fail.

This is causing errors when running lscp, lssu, and other nilfs tools.

Fix this issue by avoiding custom parsing and using standard mount table
access routines such as setmntent(), getmntent_r(), endmntent(), and
hasmntopt().

Closes: https://github.com/nilfs-dev/nilfs-utils/issues/22
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
This commit is contained in:
Ryusuke Konishi 2024-04-03 02:54:35 +09:00
parent 78717a83c0
commit c9b400aa41

View File

@ -73,6 +73,7 @@
#include <linux/nilfs2_ondisk.h>
#include <errno.h>
#include <assert.h>
#include <mntent.h> /* setmntent, getmntent_r, endmntent, etc */
#include "nilfs.h"
#include "compat.h" /* PATH_MAX, etc */
#include "util.h"
@ -110,65 +111,8 @@ enum {
};
static inline int iseol(int c)
{
return c == '\n' || c == '\0';
}
static size_t tokenize(char *line, char **tokens, size_t ntoks)
{
char *p;
size_t n;
p = line;
for (n = 0; n < ntoks; n++) {
while (isspace(*p))
p++;
if (iseol(*p))
break;
tokens[n] = p++;
while (!isspace(*p) && !iseol(*p))
p++;
if (isspace(*p))
*p++ = '\0';
else
*p = '\0';
}
return n;
}
#define NMNTFLDS 6
#define MNTFLD_FS 0
#define MNTFLD_DIR 1
#define MNTFLD_TYPE 2
#define MNTFLD_OPTS 3
#define MNTFLD_FREQ 4
#define MNTFLD_PASSNO 5
#define MNTOPT_RW "rw"
#define MNTOPT_RO "ro"
#define MNTOPT_SEP ','
static int has_mntopt(const char *opts, const char *opt)
{
const char *p, *q;
size_t len, n;
p = opts;
len = strlen(opt);
while (p != NULL) {
q = strchr(p, MNTOPT_SEP);
if (q) {
n = max_t(size_t, q - p, len);
q++;
} else {
n = max_t(size_t, strlen(p), len);
}
if (strncmp(p, opt, n) == 0)
return 1;
p = q;
}
return 0;
}
#ifndef LINE_MAX
#define LINE_MAX 2048
@ -177,12 +121,13 @@ static int has_mntopt(const char *opts, const char *opt)
static int nilfs_find_fs(struct nilfs *nilfs, const char *dev, const char *dir,
const char *opt)
{
FILE *fp;
char line[LINE_MAX], *mntent[NMNTFLDS];
int ret, n;
struct mntent *mntent, mntbuf;
char buf[LINE_MAX];
char canonical[PATH_MAX + 2];
char *cdev = NULL, *cdir = NULL;
char *mdev, *mdir;
FILE *fp;
int ret;
ret = -1;
if (dev && myrealpath(dev, canonical, sizeof(canonical))) {
@ -199,19 +144,16 @@ static int nilfs_find_fs(struct nilfs *nilfs, const char *dev, const char *dir,
dir = cdir;
}
fp = fopen(_PATH_PROC_MOUNTS, "r");
fp = setmntent(_PATH_PROC_MOUNTS, "r");
if (unlikely(fp == NULL))
goto failed_dir;
while (fgets(line, sizeof(line), fp) != NULL) {
n = tokenize(line, mntent, NMNTFLDS);
assert(n == NMNTFLDS);
if (strcmp(mntent[MNTFLD_TYPE], NILFS_FSTYPE) != 0)
while ((mntent = getmntent_r(fp, &mntbuf, buf, sizeof(buf))) != NULL) {
if (strcmp(mntent->mnt_type, NILFS_FSTYPE) != 0)
continue;
if (dir != NULL) {
mdir = mntent[MNTFLD_DIR];
mdir = mntent->mnt_dir;
if (myrealpath(mdir, canonical, sizeof(canonical)))
mdir = canonical;
if (strcmp(mdir, dir) != 0)
@ -219,16 +161,16 @@ static int nilfs_find_fs(struct nilfs *nilfs, const char *dev, const char *dir,
}
if (dev != NULL) {
mdev = mntent[MNTFLD_FS];
mdev = mntent->mnt_fsname;
if (myrealpath(mdev, canonical, sizeof(canonical)))
mdev = canonical;
if (strcmp(mdev, dev) != 0)
continue;
}
if (has_mntopt(mntent[MNTFLD_OPTS], opt)) {
if (hasmntopt(mntent, opt)) {
free(nilfs->n_dev);
nilfs->n_dev = strdup(mntent[MNTFLD_FS]);
nilfs->n_dev = strdup(mntent->mnt_fsname);
if (unlikely(nilfs->n_dev == NULL)) {
free(nilfs->n_ioc);
nilfs->n_ioc = NULL;
@ -236,7 +178,7 @@ static int nilfs_find_fs(struct nilfs *nilfs, const char *dev, const char *dir,
goto failed_proc_mounts;
}
free(nilfs->n_ioc);
nilfs->n_ioc = strdup(mntent[MNTFLD_DIR]);
nilfs->n_ioc = strdup(mntent->mnt_dir);
if (unlikely(nilfs->n_ioc == NULL)) {
free(nilfs->n_dev);
nilfs->n_dev = NULL;
@ -250,7 +192,7 @@ static int nilfs_find_fs(struct nilfs *nilfs, const char *dev, const char *dir,
errno = ENXIO;
failed_proc_mounts:
fclose(fp);
endmntent(fp);
failed_dir:
free(cdir);