commit 3c8db52fe77267109a6f01c178370747a23e64fd Author: Andrew Price Date: Sat Nov 16 02:10:52 2013 -0600 libgfs2: Add lgfs2_open_mnt* functions lgfs2_open_mnt is a replacement for is_pathname_mounted which tries to reduce races by opening paths speculatively and passing back the open fds once they're known to be correct. lgfs2_open_mnt_{dev,dir} build on this to provide a convenient way to open just the device or mount directory relating to a path which could be either. Resolves: bz#991204 Signed-off-by: Andrew Price diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h index f864a08..3e5d09c 100644 --- a/gfs2/libgfs2/libgfs2.h +++ b/gfs2/libgfs2/libgfs2.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "osi_list.h" @@ -715,6 +716,9 @@ extern int compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize, uint32_t *maxheight, uint32_t bsize1, int diptrs, int inptrs); extern int compute_constants(struct gfs2_sbd *sdp); extern int is_pathname_mounted(char *path_name, char *device_name, int *ro_mount); +extern int lgfs2_open_mnt(const char *path, int dirflags, int *dirfd, int devflags, int *devfd, struct mntent **mnt); +extern int lgfs2_open_mnt_dev(const char *path, int flags, struct mntent **mnt); +extern int lgfs2_open_mnt_dir(const char *path, int flags, struct mntent **mnt); extern int find_gfs2_meta(struct gfs2_sbd *sdp); extern int dir_exists(const char *dir); extern int mount_gfs2_meta(struct gfs2_sbd *sdp); diff --git a/gfs2/libgfs2/misc.c b/gfs2/libgfs2/misc.c index 7f500e6..195b983 100644 --- a/gfs2/libgfs2/misc.c +++ b/gfs2/libgfs2/misc.c @@ -163,6 +163,100 @@ int is_pathname_mounted(char *path_name, char *device_name, int *ro_mount) return 1; /* mounted */ } +/* Returns 0 if fd1 and fd2 refer to the same device/file, 1 otherwise, or -1 on error */ +static int fdcmp(int fd1, int fd2) +{ + struct stat st1, st2; + if ((fstat(fd1, &st1) != 0) || (fstat(fd2, &st2) != 0)) + return -1; + if (S_ISBLK(st1.st_mode) && S_ISBLK(st2.st_mode)) { + if (st1.st_rdev == st2.st_rdev) { + return 0; + } + } else if ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino)) { + return 0; + } + return 1; +} + +int lgfs2_open_mnt(const char *path, int dirflags, int *dirfd, int devflags, int *devfd, struct mntent **mnt) +{ + FILE *fp = setmntent("/proc/mounts", "r"); + if (fp == NULL) { + perror("open: /proc/mounts"); + return 1; + } + /* Assume path is mount point until we know better. */ + *dirfd = open(path, dirflags); + if (*dirfd < 0) + return 1; + + while ((*mnt = getmntent(fp)) != NULL) { + int fd; + if (strcmp((*mnt)->mnt_type, "gfs2") != 0) + continue; + *devfd = open((*mnt)->mnt_fsname, devflags); + /* Defer checking *devfd until later: whether it's ok to ignore + * the error depends on whether we find the mount point. */ + + if (strcmp(path, (*mnt)->mnt_dir) == 0) + break; + if (strcmp(path, (*mnt)->mnt_fsname) == 0 || fdcmp(*dirfd, *devfd) == 0) { + /* We have a match but our above assumption was + incorrect and *dirfd is actually the device. */ + close(*dirfd); + *dirfd = open((*mnt)->mnt_dir, dirflags); + break; + } + + fd = open((*mnt)->mnt_dir, dirflags); + if (fd >= 0) { + int diff = fdcmp(*dirfd, fd); + close(fd); + if (diff == 0) + break; + } + if (*devfd >= 0) + close(*devfd); + } + endmntent(fp); + if (*mnt == NULL) { + close(*dirfd); + return 0; /* Success. Answer is no. Both fds closed. */ + } + if (*dirfd < 0) { + close(*devfd); + return 1; + } + if (*devfd < 0) { + close(*dirfd); + return 1; + } + return 0; /* Success. Answer is yes. Both fds open. */ +} + +int lgfs2_open_mnt_dev(const char *path, int flags, struct mntent **mnt) +{ + int dirfd = -1; + int devfd = -1; + if (lgfs2_open_mnt(path, O_RDONLY, &dirfd, flags, &devfd, mnt) != 0) + return -1; + if (*mnt != NULL) + close(dirfd); + return devfd; +} + +int lgfs2_open_mnt_dir(const char *path, int flags, struct mntent **mnt) +{ + int dirfd = -1; + int devfd = -1; + if (lgfs2_open_mnt(path, flags, &dirfd, O_RDONLY, &devfd, mnt) != 0) + return -1; + if (*mnt != NULL) + close(devfd); + return dirfd; +} + static int lock_for_admin(struct gfs2_sbd *sdp) { int error;