get_fs_info() has been silently switching from a device to a mounted
path as needed; the caller's filehandle was unexpectedly closed &
reopened outside the caller's scope. Not so great.
The callers do want "fdmnt" to be the filehandle for the mount point
in all cases, though - the various ioctls act on this (not on an fd
for the device). But switching it in the local scope of get_fs_info
is incorrect; it just so happens that *usually* the fd number is
unchanged.
So - use the new helpers to detect when an argument is a block
device, and open the the mounted path more obviously / explicitly
for ioctl use, storing the filehandle in fdmnt.
Then, in get_fs_info, ignore the fd completely, and use the path on
the argument to determine if the caller wanted to act on just that
device, or on all devices for the filesystem.
Affects those commands which are documented to accept either
a block device or a path:
* btrfs device stats
* btrfs replace start
* btrfs scrub start
* btrfs scrub status
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
- fdmnt = open_file_or_dir(path);
+ fdmnt = open_path_or_dev_mnt(path);
+
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", path);
return 12;
}
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", path);
return 12;
}
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
if (check_argc_exact(argc - optind, 3))
usage(cmd_start_replace_usage);
path = argv[optind + 2];
if (check_argc_exact(argc - optind, 3))
usage(cmd_start_replace_usage);
path = argv[optind + 2];
- fdmnt = open_file_or_dir(path);
+
+ fdmnt = open_path_or_dev_mnt(path);
+
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
path, strerror(errno));
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
path, strerror(errno));
}
start_args.start.srcdevid = (__u64)atoi(srcdev);
}
start_args.start.srcdevid = (__u64)atoi(srcdev);
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
- fdmnt = open_file_or_dir(path);
+ fdmnt = open_path_or_dev_mnt(path);
+
if (fdmnt < 0) {
ERR(!do_quiet, "ERROR: can't access '%s'\n", path);
return 12;
}
if (fdmnt < 0) {
ERR(!do_quiet, "ERROR: can't access '%s'\n", path);
return 12;
}
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));
if (ret) {
ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));
- fdmnt = open_file_or_dir(path);
+ fdmnt = open_path_or_dev_mnt(path);
+
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
return 12;
}
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
return 12;
}
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));
if (ret) {
fprintf(stderr, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));
errno = EINVAL;
return -1;
}
errno = EINVAL;
return -1;
}
- fdmnt = open(mp, O_RDWR);
+ fdmnt = open_file_or_dir(mp);
} else {
fdmnt = open_file_or_dir(path);
}
} else {
fdmnt = open_file_or_dir(path);
}
return ret ? -errno : 0;
}
return ret ? -errno : 0;
}
-int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
+/*
+ * For a given path, fill in the ioctl fs_ and info_ args.
+ * If the path is a btrfs mountpoint, fill info for all devices.
+ * If the path is a btrfs device, fill in only that device.
+ *
+ * The path provided must be either on a mounted btrfs fs,
+ * or be a mounted btrfs device.
+ *
+ * Returns 0 on success, or a negative errno.
+ */
+int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret)
{
struct btrfs_ioctl_dev_info_args **di_ret)
{
int ret = 0;
int ndevs = 0;
int i = 1;
int ret = 0;
int ndevs = 0;
int i = 1;
memset(fi_args, 0, sizeof(*fi_args));
memset(fi_args, 0, sizeof(*fi_args));
- ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
- if (ret && (errno == EINVAL || errno == ENOTTY)) {
- /* path is not a mounted btrfs. Try if it's a device */
+ if (is_block_device(path)) {
+ /* Ensure it's mounted, then set path to the mountpoint */
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ fprintf(stderr, "Couldn't open %s: %s\n",
+ path, strerror(errno));
+ goto out;
+ }
ret = check_mounted_where(fd, path, mp, sizeof(mp),
&fs_devices_mnt);
ret = check_mounted_where(fd, path, mp, sizeof(mp),
&fs_devices_mnt);
- if (!ret)
- return -EINVAL;
+ if (!ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+ goto out;
+ path = mp;
+ /* Only fill in this one device */
fi_args->num_devices = 1;
fi_args->max_id = fs_devices_mnt->latest_devid;
i = fs_devices_mnt->latest_devid;
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
fi_args->num_devices = 1;
fi_args->max_id = fs_devices_mnt->latest_devid;
i = fs_devices_mnt->latest_devid;
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
- fd = open_file_or_dir(mp);
- if (fd < 0)
- return -errno;
- } else if (ret) {
- return -errno;
+ }
+
+ /* at this point path must not be for a block device */
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ /* fill in fi_args if not just a single device */
+ if (fi_args->num_devices != 1) {
+ ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ }
}
if (!fi_args->num_devices)
}
if (!fi_args->num_devices)
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
- if (!di_args)
- return -errno;
+ if (!di_args) {
+ ret = -errno;
+ goto out;
+ }
for (; i <= fi_args->max_id; ++i) {
BUG_ON(ndevs >= fi_args->num_devices);
for (; i <= fi_args->max_id; ++i) {
BUG_ON(ndevs >= fi_args->num_devices);
if (ret == -ENODEV)
continue;
if (ret)
if (ret == -ENODEV)
continue;
if (ret)
ndevs++;
}
BUG_ON(ndevs == 0);
ndevs++;
}
BUG_ON(ndevs == 0);
+ ret = 0;
+out:
+ if (fd != -1)
+ close(fd);
+ return ret;
}
#define isoctal(c) (((c) & ~7) == '0')
}
#define isoctal(c) (((c) & ~7) == '0')
int open_file_or_dir(const char *fname);
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
int open_file_or_dir(const char *fname);
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
-int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
+int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret);
int get_label(const char *btrfs_dev);
int set_label(const char *btrfs_dev, const char *label);
struct btrfs_ioctl_dev_info_args **di_ret);
int get_label(const char *btrfs_dev);
int set_label(const char *btrfs_dev, const char *label);