Skip to content

Commit

Permalink
libmountchk: determine if image file is mounted via a loop device
Browse files Browse the repository at this point in the history
The check_mount() function used to check the mount of a block device
does not support determining the mount status when the source is an
image file mounted via a loop device.

As a result, nilfs-tune can rewrite the loop device mount source file
without the force option "-f".

Fix this issue by making check_mount() detect if a loop device is
attached and a file system is mounted on it.

[ fixed a typo in comments ]

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
  • Loading branch information
konis committed Feb 19, 2024
1 parent 919ac39 commit 73ce8fc
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 14 deletions.
123 changes: 118 additions & 5 deletions lib/ismounted.c
Expand Up @@ -25,25 +25,123 @@
#include <mntent.h>
#endif

#include <unistd.h> /* sysconf() */
#include <sys/stat.h>
#include <fcntl.h> /* O_* flags */
#include <errno.h>
#include <assert.h>

#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;
struct stat st_buf;
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)) {
Expand All @@ -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)))
Expand All @@ -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;
}
8 changes: 5 additions & 3 deletions sbin/nilfs-resize/nilfs-resize.c
Expand Up @@ -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);
}
Expand Down
18 changes: 12 additions & 6 deletions sbin/nilfs-tune/nilfs-tune.c
Expand Up @@ -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);
Expand Down

0 comments on commit 73ce8fc

Please sign in to comment.