return 0;
}
-static u64 device_size(int fd, struct stat *st)
+u64 btrfs_device_size(int fd, struct stat *st)
{
u64 size;
if (S_ISREG(st->st_mode)) {
exit(1);
}
- block_count = device_size(fd, &st);
+ block_count = btrfs_device_size(fd, &st);
if (block_count == 0) {
fprintf(stderr, "unable to find %s size\n", file);
exit(1);
return ret;
}
+/*
+ * checks if a path is a block device node
+ * Returns negative errno on failure, otherwise
+ * returns 1 for blockdev, 0 for not-blockdev
+ */
+int is_block_device(const char *path) {
+ struct stat statbuf;
+
+ if (stat(path, &statbuf) < 0)
+ return -errno;
+
+ return S_ISBLK(statbuf.st_mode);
+}
+
+/*
+ * Find the mount point for a mounted device.
+ * On success, returns 0 with mountpoint in *mp.
+ * On failure, returns -errno (not mounted yields -EINVAL)
+ * Is noisy on failures, expects to be given a mounted device.
+ */
+int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) {
+ int ret;
+ int fd = -1;
+
+ ret = is_block_device(dev);
+ if (ret <= 0) {
+ if (!ret) {
+ fprintf(stderr, "%s is not a block device\n", dev);
+ ret = -EINVAL;
+ } else {
+ fprintf(stderr, "Could not check %s: %s\n",
+ dev, strerror(-ret));
+ }
+ goto out;
+ }
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ fprintf(stderr, "Could not open %s: %s\n", dev, strerror(errno));
+ goto out;
+ }
+
+ ret = check_mounted_where(fd, dev, mp, mp_size, NULL);
+ if (!ret) {
+ fprintf(stderr, "%s is not a mounted btrfs device\n", dev);
+ ret = -EINVAL;
+ } else { /* mounted, all good */
+ ret = 0;
+ }
+out:
+ if (fd != -1)
+ close(fd);
+ if (ret)
+ fprintf(stderr, "Could not get mountpoint for %s\n", dev);
+ return ret;
+}
+
+/*
+ * Given a pathname, return a filehandle to:
+ * the original pathname or,
+ * if the pathname is a mounted btrfs device, to its mountpoint.
+ *
+ * On error, return -1, errno should be set.
+ */
+int open_path_or_dev_mnt(const char *path)
+{
+ char mp[BTRFS_PATH_NAME_MAX + 1];
+ int fdmnt;
+
+ if (is_block_device(path)) {
+ int ret;
+
+ ret = get_btrfs_mount(path, mp, sizeof(mp));
+ if (ret < 0) {
+ /* not a mounted btrfs dev */
+ errno = EINVAL;
+ return -1;
+ }
+ fdmnt = open_file_or_dir(mp);
+ } else {
+ fdmnt = open_file_or_dir(path);
+ }
+
+ return fdmnt;
+}
+
/* checks if a device is a loop device */
int is_loop_device (const char* device) {
struct stat statbuf;
fd = open("/dev/btrfs-control", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open /dev/btrfs-control "
- "skipping device registration\n");
+ "skipping device registration: %s\n",
+ strerror(errno));
return;
}
strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
return -1;
}
+ close(fd);
return 0;
}
}
fprintf(stdout, "%s\n", label);
+ close(fd);
return 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)
{
+ int fd = -1;
int ret = 0;
int ndevs = 0;
int i = 1;
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);
- if (!ret)
- return -EINVAL;
+ if (!ret) {
+ ret = -EINVAL;
+ goto out;
+ }
if (ret < 0)
- return ret;
+ 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);
- 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)
- return 0;
+ goto out;
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);
if (ret == -ENODEV)
continue;
if (ret)
- return ret;
+ goto out;
ndevs++;
}
BUG_ON(ndevs == 0);
-
- return 0;
+ ret = 0;
+out:
+ if (fd != -1)
+ close(fd);
+ return ret;
}
#define isoctal(c) (((c) & ~7) == '0')