/*
* Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2008 Morey Roof. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
#define _XOPEN_SOURCE 700
#define __USE_XOPEN2K8
#define __XOPEN2K8 /* due to an error in dirent.h, to get dirfd() */
+#define _GNU_SOURCE /* O_NOATIME */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
-#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <mntent.h>
#include <linux/major.h>
#include <linux/kdev_t.h>
#include <limits.h>
+#include <blkid/blkid.h>
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
btrfs_set_super_bytenr(&super, blocks[0]);
btrfs_set_super_num_devices(&super, 1);
- strncpy((char *)&super.magic, BTRFS_MAGIC, sizeof(super.magic));
+ btrfs_set_super_magic(&super, BTRFS_MAGIC);
btrfs_set_super_generation(&super, 1);
btrfs_set_super_root(&super, blocks[1]);
btrfs_set_super_chunk_root(&super, blocks[3]);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[1]);
- BUG_ON(ret != leafsize);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* create the items for the extent tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, nritems);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[2]);
- BUG_ON(ret != leafsize);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* create the chunk tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, nritems);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[3]);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* create the device tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, nritems);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[4]);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* create the FS root */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, 0);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[5]);
- BUG_ON(ret != leafsize);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* finally create the csum root */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, 0);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[6]);
- BUG_ON(ret != leafsize);
+ if (ret < 0)
+ return -errno;
+ else if (ret != leafsize)
+ return -EIO;
/* and write out the super block */
BUG_ON(sizeof(super) > sectorsize);
buf->len = sectorsize;
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, sectorsize, blocks[0]);
- BUG_ON(ret != sectorsize);
-
+ if (ret < 0)
+ return -errno;
+ else if (ret != sectorsize)
+ return -EIO;
free(buf);
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)) {
u32 sectorsize)
{
struct btrfs_super_block *disk_super;
- struct btrfs_super_block *super = &root->fs_info->super_copy;
+ struct btrfs_super_block *super = root->fs_info->super_copy;
struct btrfs_device *device;
struct btrfs_dev_item *dev_item;
char *buf;
u64 num_devs;
int ret;
- device = kmalloc(sizeof(*device), GFP_NOFS);
+ device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
return -ENOMEM;
buf = kmalloc(sectorsize, GFP_NOFS);
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);
btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
if (root->fs_info->tree_root == root)
- btrfs_set_super_root_dir(&root->fs_info->super_copy, objectid);
+ btrfs_set_super_root_dir(root->fs_info->super_copy, objectid);
ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
if (ret)
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.
+ */
+static 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, DIR **dirstream)
+{
+ 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, dirstream);
+ } else {
+ fdmnt = open_file_or_dir(path, dirstream);
+ }
+
+ return fdmnt;
+}
+
/* checks if a device is a loop device */
int is_loop_device (const char* device) {
struct stat statbuf;
/* scan other devices */
if (is_btrfs && total_devs > 1) {
- if((ret = btrfs_scan_for_fsid(fs_devices_mnt, total_devs, 1)))
+ if((ret = btrfs_scan_for_fsid(1)))
return ret;
}
return ret;
}
-/* Gets the mount point of btrfs filesystem that is using the specified device.
- * Returns 0 is everything is good, <0 if we have an error.
- * TODO: Fix this fucntion and check_mounted to work with multiple drive BTRFS
- * setups.
- */
-int get_mountpt(char *dev, char *mntpt, size_t size)
-{
- struct mntent *mnt;
- FILE *f;
- int ret = 0;
-
- f = setmntent("/proc/mounts", "r");
- if (f == NULL)
- return -errno;
-
- while ((mnt = getmntent(f)) != NULL )
- {
- if (strcmp(dev, mnt->mnt_fsname) == 0)
- {
- strncpy(mntpt, mnt->mnt_dir, size);
- if (size)
- mntpt[size-1] = 0;
- break;
- }
- }
-
- if (mnt == NULL)
- {
- /* We didn't find an entry so lets report an error */
- ret = -1;
- }
-
- return ret;
-}
-
struct pending_dir {
struct list_head list;
- char name[256];
+ char name[PATH_MAX];
};
void btrfs_register_one_device(char *fname)
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);
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
e = errno;
if(ret<0){
- fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n",
+ fprintf(stderr, "ERROR: device scan failed '%s' - %s\n",
fname, strerror(e));
}
close(fd);
int ret;
int fd;
int dirname_len;
- int pathlen;
char *fullpath;
struct list_head pending_list;
struct btrfs_fs_devices *tmp_devices;
again:
dirname_len = strlen(pending->name);
- pathlen = 1024;
- fullpath = malloc(pathlen);
+ fullpath = malloc(PATH_MAX);
dirname = pending->name;
if (!fullpath) {
break;
if (dirent->d_name[0] == '.')
continue;
- if (dirname_len + strlen(dirent->d_name) + 2 > pathlen) {
+ if (dirname_len + strlen(dirent->d_name) + 2 > PATH_MAX) {
ret = -EFAULT;
goto fail;
}
- snprintf(fullpath, pathlen, "%s/%s", dirname, dirent->d_name);
+ snprintf(fullpath, PATH_MAX, "%s/%s", dirname, dirent->d_name);
ret = lstat(fullpath, &st);
if (ret < 0) {
fprintf(stderr, "failed to stat %s\n", fullpath);
return ret;
}
-int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
- int run_ioctls)
+int btrfs_scan_for_fsid(int run_ioctls)
{
int ret;
- ret = btrfs_scan_block_devices(run_ioctls);
+ ret = scan_for_btrfs(BTRFS_SCAN_PROC, run_ioctls);
if (ret)
- ret = btrfs_scan_one_dir("/dev", run_ioctls);
+ ret = scan_for_btrfs(BTRFS_SCAN_DEV, run_ioctls);
return ret;
}
ret = 0;
disk_super = (struct btrfs_super_block *)buf;
- if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC,
- sizeof(disk_super->magic)))
+ if (btrfs_super_magic(disk_super) != BTRFS_MAGIC)
goto brelse;
- if (!memcmp(disk_super->fsid, root->fs_info->super_copy.fsid,
+ if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid,
BTRFS_FSID_SIZE))
ret = 1;
brelse:
return ret;
}
-static char *size_strs[] = { "", "KB", "MB", "GB", "TB",
- "PB", "EB", "ZB", "YB"};
-char *pretty_sizes(u64 size)
+static char *size_strs[] = { "", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
+void pretty_size_snprintf(u64 size, char *str, size_t str_bytes)
{
int num_divs = 0;
- int pretty_len = 16;
float fraction;
- char *pretty;
+
+ if (str_bytes == 0)
+ return;
if( size < 1024 ){
fraction = size;
num_divs ++;
}
- if (num_divs > ARRAY_SIZE(size_strs))
- return NULL;
+ if (num_divs >= ARRAY_SIZE(size_strs)) {
+ str[0] = '\0';
+ return;
+ }
fraction = (float)last_size / 1024;
}
- pretty = malloc(pretty_len);
- snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs]);
- return pretty;
+ snprintf(str, str_bytes, "%.2f%s", fraction, size_strs[num_divs]);
+}
+
+/*
+ * __strncpy__null - strncpy with null termination
+ * @dest: the target array
+ * @src: the source string
+ * @n: maximum bytes to copy (size of *dest)
+ *
+ * Like strncpy, but ensures destination is null-terminated.
+ *
+ * Copies the string pointed to by src, including the terminating null
+ * byte ('\0'), to the buffer pointed to by dest, up to a maximum
+ * of n bytes. Then ensure that dest is null-terminated.
+ */
+char *__strncpy__null(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n);
+ if (n > 0)
+ dest[n - 1] = '\0';
+ return dest;
}
/*
* Returns:
0 if everything is safe and usable
-1 if the label is too long
- -2 if the label contains an invalid character
*/
-int check_label(char *input)
+static int check_label(const char *input)
{
- int i;
int len = strlen(input);
- if (len > BTRFS_LABEL_SIZE) {
+ if (len > BTRFS_LABEL_SIZE - 1) {
+ fprintf(stderr, "ERROR: Label %s is too long (max %d)\n",
+ input, BTRFS_LABEL_SIZE - 1);
return -1;
}
- for (i = 0; i < len; i++) {
- if (input[i] == '/' || input[i] == '\\') {
- return -2;
- }
- }
-
return 0;
}
+static int set_label_unmounted(const char *dev, const char *label)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *root;
+ int ret;
+
+ ret = check_mounted(dev);
+ if (ret < 0) {
+ fprintf(stderr, "FATAL: error checking %s mount status\n", dev);
+ return -1;
+ }
+ if (ret > 0) {
+ fprintf(stderr, "ERROR: dev %s is mounted, use mount point\n",
+ dev);
+ return -1;
+ }
+
+ /* Open the super_block at the default location
+ * and as read-write.
+ */
+ root = open_ctree(dev, 0, 1);
+ if (!root) /* errors are printed by open_ctree() */
+ return -1;
+
+ trans = btrfs_start_transaction(root, 1);
+ snprintf(root->fs_info->super_copy->label, BTRFS_LABEL_SIZE, "%s",
+ label);
+ btrfs_commit_transaction(trans, root);
+
+ /* Now we close it since we are done. */
+ close_ctree(root);
+ return 0;
+}
+
+static int set_label_mounted(const char *mount_path, const char *label)
+{
+ int fd;
+
+ fd = open(mount_path, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+ return -1;
+ }
+
+ if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
+ fprintf(stderr, "ERROR: unable to set label %s\n",
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int get_label_unmounted(const char *dev)
+{
+ struct btrfs_root *root;
+ int ret;
+
+ ret = check_mounted(dev);
+ if (ret < 0) {
+ fprintf(stderr, "FATAL: error checking %s mount status\n", dev);
+ return -1;
+ }
+ if (ret > 0) {
+ fprintf(stderr, "ERROR: dev %s is mounted, use mount point\n",
+ dev);
+ return -1;
+ }
+
+ /* Open the super_block at the default location
+ * and as read-only.
+ */
+ root = open_ctree(dev, 0, 0);
+ if(!root)
+ return -1;
+
+ fprintf(stdout, "%s\n", root->fs_info->super_copy->label);
+
+ /* Now we close it since we are done. */
+ close_ctree(root);
+ return 0;
+}
+
+/*
+ * If a partition is mounted, try to get the filesystem label via its
+ * mounted path rather than device. Return the corresponding error
+ * the user specified the device path.
+ */
+int get_label_mounted(const char *mount_path, char *labelp)
+{
+ char label[BTRFS_LABEL_SIZE];
+ int fd;
+
+ fd = open(mount_path, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+ return -1;
+ }
+
+ memset(label, '\0', sizeof(label));
+ if (ioctl(fd, BTRFS_IOC_GET_FSLABEL, label) < 0) {
+ fprintf(stderr, "ERROR: unable get label %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ strncpy(labelp, label, sizeof(label));
+ close(fd);
+ return 0;
+}
+
+int get_label(const char *btrfs_dev)
+{
+ int ret;
+ char label[BTRFS_LABEL_SIZE];
+
+ if (is_existing_blk_or_reg_file(btrfs_dev))
+ ret = get_label_unmounted(btrfs_dev);
+ else {
+ ret = get_label_mounted(btrfs_dev, label);
+ if (!ret)
+ fprintf(stdout, "%s\n", label);
+ }
+ return ret;
+}
+
+int set_label(const char *btrfs_dev, const char *label)
+{
+ if (check_label(label))
+ return -1;
+
+ return is_existing_blk_or_reg_file(btrfs_dev) ?
+ set_label_unmounted(btrfs_dev, label) :
+ set_label_mounted(btrfs_dev, label);
+}
+
int btrfs_scan_block_devices(int run_ioctl)
{
return -ENOENT;
}
/* skip the header */
- for(i=0; i < 2 ; i++)
- if(!fgets(buf, 1023, proc_partitions)){
- fprintf(stderr, "Unable to read '/proc/partitions' for scanning\n");
- fclose(proc_partitions);
- return -ENOENT;
- }
+ for (i = 0; i < 2; i++)
+ if (!fgets(buf, 1023, proc_partitions)) {
+ fprintf(stderr,
+ "Unable to read '/proc/partitions' for scanning\n");
+ fclose(proc_partitions);
+ return -ENOENT;
+ }
strcpy(fullpath,"/dev/");
while(fgets(buf, 1023, proc_partitions)) {
fd = open(fullpath, O_RDONLY);
if (fd < 0) {
- fprintf(stderr, "failed to read %s\n", fullpath);
+ if (errno != ENOMEDIUM)
+ fprintf(stderr, "failed to open %s: %s\n",
+ fullpath, strerror(errno));
continue;
}
ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
char c;
u64 mult = 1;
- for (i=0 ; s[i] && isdigit(s[i]) ; i++) ;
+ for (i = 0; s && s[i] && isdigit(s[i]); i++) ;
if (!i) {
fprintf(stderr, "ERROR: size value is empty\n");
exit(50);
return strtoull(s, NULL, 10) * mult;
}
-int open_file_or_dir(const char *fname)
+int open_file_or_dir(const char *fname, DIR **dirstream)
{
int ret;
struct stat st;
- DIR *dirstream;
int fd;
ret = stat(fname, &st);
return -1;
}
if (S_ISDIR(st.st_mode)) {
- dirstream = opendir(fname);
- if (!dirstream) {
+ *dirstream = opendir(fname);
+ if (!*dirstream)
return -2;
- }
- fd = dirfd(dirstream);
+ fd = dirfd(*dirstream);
} else {
fd = open(fname, O_RDWR);
}
if (fd < 0) {
- return -3;
+ fd = -3;
+ if (*dirstream)
+ closedir(*dirstream);
}
return fd;
}
+void close_file_or_dir(int fd, DIR *dirstream)
+{
+ if (dirstream)
+ closedir(dirstream);
+ else if (fd >= 0)
+ close(fd);
+}
+
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args)
{
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;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct btrfs_ioctl_dev_info_args *di_args;
char mp[BTRFS_PATH_NAME_MAX + 1];
+ DIR *dirstream = NULL;
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, &dirstream);
+ 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);
+ ret = 0;
+out:
+ close_file_or_dir(fd, dirstream);
+ return ret;
+}
+
+#define isoctal(c) (((c) & ~7) == '0')
+
+static inline void translate(char *f, char *t)
+{
+ while (*f != '\0') {
+ if (*f == '\\' &&
+ isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) {
+ *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7);
+ f += 4;
+ } else
+ *t++ = *f++;
+ }
+ *t = '\0';
+ return;
+}
+
+/*
+ * Checks if the swap device.
+ * Returns 1 if swap device, < 0 on error or 0 if not swap device.
+ */
+int is_swap_device(const char *file)
+{
+ FILE *f;
+ struct stat st_buf;
+ dev_t dev;
+ ino_t ino = 0;
+ char tmp[PATH_MAX];
+ char buf[PATH_MAX];
+ char *cp;
+ int ret = 0;
+
+ if (stat(file, &st_buf) < 0)
+ return -errno;
+ if (S_ISBLK(st_buf.st_mode))
+ dev = st_buf.st_rdev;
+ else if (S_ISREG(st_buf.st_mode)) {
+ dev = st_buf.st_dev;
+ ino = st_buf.st_ino;
+ } else
+ return 0;
+
+ if ((f = fopen("/proc/swaps", "r")) == NULL)
+ return 0;
+
+ /* skip the first line */
+ if (fgets(tmp, sizeof(tmp), f) == NULL)
+ goto out;
+
+ while (fgets(tmp, sizeof(tmp), f) != NULL) {
+ if ((cp = strchr(tmp, ' ')) != NULL)
+ *cp = '\0';
+ if ((cp = strchr(tmp, '\t')) != NULL)
+ *cp = '\0';
+ translate(tmp, buf);
+ if (stat(buf, &st_buf) != 0)
+ continue;
+ if (S_ISBLK(st_buf.st_mode)) {
+ if (dev == st_buf.st_rdev) {
+ ret = 1;
+ break;
+ }
+ } else if (S_ISREG(st_buf.st_mode)) {
+ if (dev == st_buf.st_dev && ino == st_buf.st_ino) {
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+out:
+ fclose(f);
+
+ return ret;
+}
+
+/*
+ * Check for existing filesystem or partition table on device.
+ * Returns:
+ * 1 for existing fs or partition
+ * 0 for nothing found
+ * -1 for internal error
+ */
+static int
+check_overwrite(
+ char *device)
+{
+ const char *type;
+ blkid_probe pr = NULL;
+ int ret;
+ blkid_loff_t size;
+
+ if (!device || !*device)
+ return 0;
+
+ ret = -1; /* will reset on success of all setup calls */
+
+ pr = blkid_new_probe_from_filename(device);
+ if (!pr)
+ goto out;
+
+ size = blkid_probe_get_size(pr);
+ if (size < 0)
+ goto out;
+
+ /* nothing to overwrite on a 0-length device */
+ if (size == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = blkid_probe_enable_partitions(pr, 1);
+ if (ret < 0)
+ goto out;
+
+ ret = blkid_do_fullprobe(pr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Blkid returns 1 for nothing found and 0 when it finds a signature,
+ * but we want the exact opposite, so reverse the return value here.
+ *
+ * In addition print some useful diagnostics about what actually is
+ * on the device.
+ */
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
+ fprintf(stderr,
+ "%s appears to contain an existing "
+ "filesystem (%s).\n", device, type);
+ } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
+ fprintf(stderr,
+ "%s appears to contain a partition "
+ "table (%s).\n", device, type);
+ } else {
+ fprintf(stderr,
+ "%s appears to contain something weird "
+ "according to blkid\n", device);
+ }
+ ret = 1;
+
+out:
+ if (pr)
+ blkid_free_probe(pr);
+ if (ret == -1)
+ fprintf(stderr,
+ "probe of %s failed, cannot detect "
+ "existing filesystem.\n", device);
+ return ret;
+}
+/* Check if disk is suitable for btrfs
+ * returns:
+ * 1: something is wrong, estr provides the error
+ * 0: all is fine
+ */
+int test_dev_for_mkfs(char *file, int force_overwrite, char *estr)
+{
+ int ret, fd;
+ size_t sz = 100;
+ struct stat st;
+
+ ret = is_swap_device(file);
+ if (ret < 0) {
+ snprintf(estr, sz, "error checking %s status: %s\n", file,
+ strerror(-ret));
+ return 1;
+ }
+ if (ret == 1) {
+ snprintf(estr, sz, "%s is a swap device\n", file);
+ return 1;
+ }
+ if (!force_overwrite) {
+ if (check_overwrite(file)) {
+ snprintf(estr, sz, "Use the -f option to force overwrite.\n");
+ return 1;
+ }
+ }
+ ret = check_mounted(file);
+ if (ret < 0) {
+ snprintf(estr, sz, "error checking %s mount status\n",
+ file);
+ return 1;
+ }
+ if (ret == 1) {
+ snprintf(estr, sz, "%s is mounted\n", file);
+ return 1;
+ }
+ /* check if the device is busy */
+ fd = open(file, O_RDWR|O_EXCL);
+ if (fd < 0) {
+ snprintf(estr, sz, "unable to open %s: %s\n", file,
+ strerror(errno));
+ return 1;
+ }
+ if (fstat(fd, &st)) {
+ snprintf(estr, sz, "unable to stat %s: %s\n", file,
+ strerror(errno));
+ return 1;
+ }
+ if (!S_ISBLK(st.st_mode)) {
+ fprintf(stderr, "'%s' is not a block device\n", file);
+ return 1;
+ }
+ close(fd);
return 0;
}
+
+/*
+ * scans devs for the btrfs
+*/
+int scan_for_btrfs(int where, int update_kernel)
+{
+ int ret = 0;
+
+ switch (where) {
+ case BTRFS_SCAN_PROC:
+ ret = btrfs_scan_block_devices(update_kernel);
+ break;
+ case BTRFS_SCAN_DEV:
+ ret = btrfs_scan_one_dir("/dev", update_kernel);
+ break;
+ }
+ return ret;
+}