diff --git a/lib/ismounted.c b/lib/ismounted.c index 7c5f0fc..c2286da 100644 --- a/lib/ismounted.c +++ b/lib/ismounted.c @@ -25,12 +25,97 @@ #include #endif +#include /* sysconf() */ #include +#include /* O_* flags */ +#include +#include + #include "pathnames.h" +#include "compat.h" /* PATH_MAX, major(), minor() */ +#include "util.h" /* unlikely() */ #define LINE_BUFFER_SIZE 256 /* Line buffer size for reading mtab */ + +/* + * Pathname buffer size of loop device entry in sysfs + * + * Logically 16 + 10 + 10 + 1 = 37 bytes would be enough to store output in + * the format "/sys/dev/block/%d:%d", but rounding up to the power of 2. + */ +#define SYSFS_LOOP_DIR_PATH_BUFSZ 64 + +/** + * loop_get_backing_file - get the name of the loop device's backing file + * @dev: device id of loop device + * @buf: buffer to store device name + * @bufsz: buffer size + * + * This function obtains the path name of the backing file for the loopback + * device with device number @dev and stores it in @buf. + * + * Return: 0 if the backing file cannot be traced, the length of the buffered + * path string if the backing file is found, -1 on error. + */ +static ssize_t loop_get_backing_file(dev_t dev, char *buf, size_t bufsz) +{ + char dir_path[SYSFS_LOOP_DIR_PATH_BUFSZ]; + int dir_fd, fd; + ssize_t ret; + + assert(bufsz > 1); + + ret = snprintf(dir_path, SYSFS_LOOP_DIR_PATH_BUFSZ, + "/sys/dev/block/%u:%u", major(dev), minor(dev)); + if (unlikely(ret < 0)) + return -1; + if (unlikely(ret >= SYSFS_LOOP_DIR_PATH_BUFSZ)) { + errno = ENAMETOOLONG; + return -1; + } + + dir_fd = openat(AT_FDCWD, dir_path, O_RDONLY | O_CLOEXEC); + if (unlikely(dir_fd < 0)) + return -1; + + fd = openat(dir_fd, "loop/backing_file", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + if (errno == ENOENT) { + buf[0] = '\0'; + ret = 0; /* not found */ + } else { + ret = -1; /* error */ + } + goto out_close_dir; + } + + ret = read(fd, buf, bufsz - 1); + if (ret >= 0) { + char *s; + + buf[ret] = '\0'; + + /* Replace newline with a null character */ + s = strchrnul(buf, '\n'); + if (*s == '\n') + *s = '\0'; + ret = s - buf; + } + close(fd); + +out_close_dir: + close(dir_fd); + return ret; +} + +/** + * check_mount - determine if a block device or file is mounted + * @device: pathname of block device or disk image file + * + * Return: 1 if mounted, 0 if not, -1 if error. + */ int check_mount(const char *device) { struct mntent *mnt; @@ -38,12 +123,25 @@ int check_mount(const char *device) FILE *f; dev_t file_dev = 0, file_rdev = 0; ino_t file_ino = 0; + long pagesize = sysconf(_SC_PAGE_SIZE); + size_t path_buf_size = PATH_MAX + 1; + char *path_buf; + ssize_t len; + + /* + * Since the loop device source is obtained via sysfs, truncate + * the path buffer size to the same or smaller than the page size. + */ + if (pagesize > 0 && (size_t)pagesize < path_buf_size) + path_buf_size = (size_t)pagesize; + + path_buf = malloc(path_buf_size); + if (!path_buf) + goto failed; f = setmntent(_PATH_MOUNTED, "r"); - if (f == NULL) { - fprintf(stderr, "Error: cannot open %s!", _PATH_MOUNTED); - return -1; - } + if (!f) + goto failed; if (stat(device, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { @@ -63,6 +161,14 @@ int check_mount(const char *device) if (S_ISBLK(st_buf.st_mode)) { if (file_rdev && (file_rdev == st_buf.st_rdev)) break; + len = loop_get_backing_file( + st_buf.st_rdev, path_buf, + path_buf_size); + if (unlikely(len < 0)) + goto failed_endmntent; + if (len > 0 && + !strncmp(path_buf, device, path_buf_size)) + break; } else { if (file_dev && ((file_dev == st_buf.st_dev) && (file_ino == st_buf.st_ino))) @@ -72,5 +178,12 @@ int check_mount(const char *device) } endmntent(f); - return (mnt == NULL) ? 0 : -1; + free(path_buf); + return (mnt == NULL) ? 0 : 1; + +failed_endmntent: + endmntent(f); +failed: + free(path_buf); + return -1; } diff --git a/sbin/nilfs-resize/nilfs-resize.c b/sbin/nilfs-resize/nilfs-resize.c index 21f1b6c..df1ed19 100644 --- a/sbin/nilfs-resize/nilfs-resize.c +++ b/sbin/nilfs-resize/nilfs-resize.c @@ -1631,11 +1631,13 @@ int main(int argc, char *argv[]) size = devsize; } - - if (check_mount(device) == 0) { + ret = check_mount(device); + if (unlikely(ret < 0)) { + myprintf("Error checking mount status of %s: %s\n", device, + strerror(errno)); + } else if (!ret) { myprintf("Error: %s is not currently mounted. Offline resizing\n" " is not supported at present.\n", device); - goto out; } else { status = nilfs_resize_online(device, size); } diff --git a/sbin/nilfs-tune/nilfs-tune.c b/sbin/nilfs-tune/nilfs-tune.c index 92a5187..51197b2 100644 --- a/sbin/nilfs-tune/nilfs-tune.c +++ b/sbin/nilfs-tune/nilfs-tune.c @@ -586,12 +586,18 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (!opts.force && opts.flags == O_RDWR && (check_mount(device) < 0)) { - errx(EXIT_FAILURE, - "ERROR: %s is mounted. Abort execution.\n" - " Running nilfs-tune on a mounted file system may cause SEVERE damage.\n" - " You can use the \"-f\" option to force this operation.", - device); + if (!opts.force && opts.flags == O_RDWR) { + int ret = check_mount(device); + + if (ret < 0) + err(EXIT_FAILURE, "ERROR checking mount status of %s", + device); + if (ret > 0) + errx(EXIT_FAILURE, + "ERROR: %s is mounted. Abort execution.\n" + " Running nilfs-tune on a mounted file system may cause SEVERE damage.\n" + " You can use the \"-f\" option to force this operation.", + device); } return modify_nilfs(device, &opts);