parse_size(): add new suffixes
[platform/upstream/btrfs-progs.git] / utils.c
diff --git a/utils.c b/utils.c
index ad980ae..b487be6 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <mntent.h>
+#include <ctype.h>
 #include <linux/loop.h>
 #include <linux/major.h>
 #include <linux/kdev_t.h>
 static inline int ioctl(int fd, int define, u64 *size) { return 0; }
 #endif
 
+#ifndef BLKDISCARD
+#define BLKDISCARD     _IO(0x12,119)
+#endif
+
+static int
+discard_blocks(int fd, u64 start, u64 len)
+{
+       u64 range[2] = { start, len };
+
+       if (ioctl(fd, BLKDISCARD, &range) < 0)
+               return errno;
+       return 0;
+}
+
 static u64 reference_root_table[] = {
        [1] =   BTRFS_ROOT_TREE_OBJECTID,
        [2] =   BTRFS_EXTENT_TREE_OBJECTID,
@@ -198,6 +213,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        BUG_ON(ret != leafsize);
 
        /* create the items for the extent tree */
+       memset(buf->data+sizeof(struct btrfs_header), 0,
+               leafsize-sizeof(struct btrfs_header));
        nritems = 0;
        itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
        for (i = 1; i < 7; i++) {
@@ -243,6 +260,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        BUG_ON(ret != leafsize);
 
        /* create the chunk tree */
+       memset(buf->data+sizeof(struct btrfs_header), 0,
+               leafsize-sizeof(struct btrfs_header));
        nritems = 0;
        item_size = sizeof(*dev_item);
        itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - item_size;
@@ -324,6 +343,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        ret = pwrite(fd, buf->data, leafsize, blocks[3]);
 
        /* create the device tree */
+       memset(buf->data+sizeof(struct btrfs_header), 0,
+               leafsize-sizeof(struct btrfs_header));
        nritems = 0;
        itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) -
                sizeof(struct btrfs_dev_extent);
@@ -357,6 +378,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        ret = pwrite(fd, buf->data, leafsize, blocks[4]);
 
        /* create the FS root */
+       memset(buf->data+sizeof(struct btrfs_header), 0,
+               leafsize-sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(buf, blocks[5]);
        btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
        btrfs_set_header_nritems(buf, 0);
@@ -365,6 +388,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        BUG_ON(ret != leafsize);
 
        /* finally create the csum root */
+       memset(buf->data+sizeof(struct btrfs_header), 0,
+               leafsize-sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(buf, blocks[6]);
        btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
        btrfs_set_header_nritems(buf, 0);
@@ -513,7 +538,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 }
 
 int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
-                        int *mixed)
+                          u64 max_block_count, int *mixed, int nodiscard)
 {
        u64 block_count;
        u64 bytenr;
@@ -531,12 +556,23 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
                fprintf(stderr, "unable to find %s size\n", file);
                exit(1);
        }
+       if (max_block_count)
+               block_count = min(block_count, max_block_count);
        zero_end = 1;
 
        if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
                printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
                *mixed = 1;
        }
+
+       if (!nodiscard) {
+               /*
+                * We intentionally ignore errors from the discard ioctl.  It is
+                * not necessary for the mkfs functionality but just an optimization.
+                */
+               discard_blocks(fd, 0, block_count);
+       }
+
        ret = zero_dev_start(fd);
        if (ret) {
                fprintf(stderr, "failed to zero device start %d\n", ret);
@@ -566,13 +602,22 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
 {
        int ret;
        struct btrfs_inode_item inode_item;
+       time_t now = time(NULL);
 
        memset(&inode_item, 0, sizeof(inode_item));
        btrfs_set_stack_inode_generation(&inode_item, trans->transid);
        btrfs_set_stack_inode_size(&inode_item, 0);
        btrfs_set_stack_inode_nlink(&inode_item, 1);
        btrfs_set_stack_inode_nbytes(&inode_item, root->leafsize);
-       btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0555);
+       btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
+       btrfs_set_stack_timespec_sec(&inode_item.atime, now);
+       btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
+       btrfs_set_stack_timespec_sec(&inode_item.ctime, now);
+       btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0);
+       btrfs_set_stack_timespec_sec(&inode_item.mtime, now);
+       btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0);
+       btrfs_set_stack_timespec_sec(&inode_item.otime, 0);
+       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);
@@ -617,9 +662,11 @@ int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len)
        ret_ioctl = ioctl(loop_fd, LOOP_GET_STATUS, &loopinfo);
        close(loop_fd);
 
-       if (ret_ioctl == 0)
+       if (ret_ioctl == 0) {
                strncpy(loop_file, loopinfo.lo_name, max_len);
-       else
+               if (max_len > 0)
+                       loop_file[max_len-1] = 0;
+       } else
                return -errno;
 
        return 0;
@@ -647,6 +694,8 @@ int is_same_blk_file(const char* a, const char* b)
        if(stat(a, &st_buf_a) < 0 ||
           stat(b, &st_buf_b) < 0)
        {
+               if (errno == ENOENT)
+                       return 0;
                return -errno;
        }
 
@@ -683,9 +732,11 @@ int is_same_loop_file(const char* a, const char* b)
 
        /* Resolve a if it is a loop device */
        if((ret = is_loop_device(a)) < 0) {
-          return ret;
-       } else if(ret) {
-               if((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0)
+               if (ret == -ENOENT)
+                       return 0;
+               return ret;
+       } else if (ret) {
+               if ((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0)
                        return ret;
 
                final_a = res_a;
@@ -694,9 +745,11 @@ int is_same_loop_file(const char* a, const char* b)
        }
 
        /* Resolve b if it is a loop device */
-       if((ret = is_loop_device(b)) < 0) {
-          return ret;
-       } else if(ret) {
+       if ((ret = is_loop_device(b)) < 0) {
+               if (ret == -ENOENT)
+                       return 0;
+               return ret;
+       } else if (ret) {
                if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0)
                        return ret;
 
@@ -750,13 +803,8 @@ int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, const char* file)
  */
 int check_mounted(const char* file)
 {
-       int ret;
        int fd;
-       u64 total_devs = 1;
-       int is_btrfs;
-       struct btrfs_fs_devices* fs_devices_mnt = NULL;
-       FILE *f;
-       struct mntent *mnt;
+       int ret;
 
        fd = open(file, O_RDONLY);
        if (fd < 0) {
@@ -764,11 +812,26 @@ int check_mounted(const char* file)
                return -errno;
        }
 
+       ret =  check_mounted_where(fd, file, NULL, 0, NULL);
+       close(fd);
+
+       return ret;
+}
+
+int check_mounted_where(int fd, const char *file, char *where, int size,
+                       struct btrfs_fs_devices **fs_dev_ret)
+{
+       int ret;
+       u64 total_devs = 1;
+       int is_btrfs;
+       struct btrfs_fs_devices *fs_devices_mnt = NULL;
+       FILE *f;
+       struct mntent *mnt;
+
        /* scan the initial device */
        ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt,
                                    &total_devs, BTRFS_SUPER_INFO_OFFSET);
        is_btrfs = (ret >= 0);
-       close(fd);
 
        /* scan other devices */
        if (is_btrfs && total_devs > 1) {
@@ -804,6 +867,13 @@ int check_mounted(const char* file)
        }
 
        /* Did we find an entry in mnt table? */
+       if (mnt && size && where) {
+               strncpy(where, mnt->mnt_dir, size);
+               where[size-1] = 0;
+       }
+       if (fs_dev_ret)
+               *fs_dev_ret = fs_devices_mnt;
+
        ret = (mnt != NULL);
 
 out_mntloop_err:
@@ -812,6 +882,41 @@ out_mntloop_err:
        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];
@@ -831,6 +936,7 @@ void btrfs_register_one_device(char *fname)
                return;
        }
        strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
+       args.name[BTRFS_PATH_NAME_MAX-1] = 0;
        ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
        e = errno;
        if(ret<0){
@@ -927,6 +1033,7 @@ again:
                                     list);
                list_del(&pending->list);
                closedir(dirp);
+               dirp = NULL;
                goto again;
        }
        ret = 0;
@@ -940,7 +1047,12 @@ fail:
 int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
                        int run_ioctls)
 {
-       return btrfs_scan_one_dir("/dev", run_ioctls);
+       int ret;
+
+       ret = btrfs_scan_block_devices(run_ioctls);
+       if (ret)
+               ret = btrfs_scan_one_dir("/dev", run_ioctls);
+       return ret;
 }
 
 int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
@@ -980,25 +1092,176 @@ char *pretty_sizes(u64 size)
 {
        int num_divs = 0;
         int pretty_len = 16;
-       u64 last_size = size;
-       u64 fract_size = size;
        float fraction;
        char *pretty;
 
-       while(size > 0) {
-               fract_size = last_size;
-               last_size = size;
-               size /= 1024;
-               num_divs++;
-       }
-       if (num_divs == 0)
-               num_divs = 1;
-       if (num_divs > ARRAY_SIZE(size_strs))
-               return NULL;
+       if( size < 1024 ){
+               fraction = size;
+               num_divs = 0;
+       } else {
+               u64 last_size = size;
+               num_divs = 0;
+               while(size >= 1024){
+                       last_size = size;
+                       size /= 1024;
+                       num_divs ++;
+               }
 
-       fraction = (float)fract_size / 1024;
+               if (num_divs > ARRAY_SIZE(size_strs))
+                       return NULL;
+               fraction = (float)last_size / 1024;
+       }
        pretty = malloc(pretty_len);
-       snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs-1]);
+       snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs]);
        return pretty;
 }
 
+/*
+ * Checks to make sure that the label matches our requirements.
+ * 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)
+{
+       int i;
+       int len = strlen(input);
+
+       if (len > BTRFS_LABEL_SIZE) {
+               return -1;
+       }
+
+       for (i = 0; i < len; i++) {
+               if (input[i] == '/' || input[i] == '\\') {
+                       return -2;
+               }
+       }
+
+       return 0;
+}
+
+int btrfs_scan_block_devices(int run_ioctl)
+{
+
+       struct stat st;
+       int ret;
+       int fd;
+       struct btrfs_fs_devices *tmp_devices;
+       u64 num_devices;
+       FILE *proc_partitions;
+       int i;
+       char buf[1024];
+       char fullpath[110];
+       int scans = 0;
+       int special;
+
+scan_again:
+       proc_partitions = fopen("/proc/partitions","r");
+       if (!proc_partitions) {
+               fprintf(stderr, "Unable to open '/proc/partitions' for scanning\n");
+               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;
+       }
+
+       strcpy(fullpath,"/dev/");
+       while(fgets(buf, 1023, proc_partitions)) {
+               i = sscanf(buf," %*d %*d %*d %99s", fullpath+5);
+
+               /*
+                * multipath and MD devices may register as a btrfs filesystem
+                * both through the original block device and through
+                * the special (/dev/mapper or /dev/mdX) entry.
+                * This scans the special entries last
+                */
+               special = strncmp(fullpath, "/dev/dm-", strlen("/dev/dm-")) == 0;
+               if (!special)
+                       special = strncmp(fullpath, "/dev/md", strlen("/dev/md")) == 0;
+
+               if (scans == 0 && special)
+                       continue;
+               if (scans > 0 && !special)
+                       continue;
+
+               ret = lstat(fullpath, &st);
+               if (ret < 0) {
+                       fprintf(stderr, "failed to stat %s\n", fullpath);
+                       continue;
+               }
+               if (!S_ISBLK(st.st_mode)) {
+                       continue;
+               }
+
+               fd = open(fullpath, O_RDONLY);
+               if (fd < 0) {
+                       fprintf(stderr, "failed to read %s\n", fullpath);
+                       continue;
+               }
+               ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
+                                           &num_devices,
+                                           BTRFS_SUPER_INFO_OFFSET);
+               if (ret == 0 && run_ioctl > 0) {
+                       btrfs_register_one_device(fullpath);
+               }
+               close(fd);
+       }
+
+       fclose(proc_partitions);
+
+       if (scans == 0) {
+               scans++;
+               goto scan_again;
+       }
+       return 0;
+}
+
+u64 parse_size(char *s)
+{
+       int i;
+       char c;
+       u64 mult = 1;
+
+       for (i=0 ; s[i] && isdigit(s[i]) ; i++) ;
+       if (!i) {
+               fprintf(stderr, "ERROR: size value is empty\n");
+               exit(50);
+       }
+
+       if (s[i]) {
+               c = tolower(s[i]);
+               switch (c) {
+               case 'e':
+                       mult *= 1024;
+               case 'p':
+                       mult *= 1024;
+               case 't':
+                       mult *= 1024;
+               case 'g':
+                       mult *= 1024;
+               case 'm':
+                       mult *= 1024;
+               case 'k':
+                       mult *= 1024;
+               case 'b':
+                       break;
+               default:
+                       fprintf(stderr, "ERROR: Unknown size descriptor "
+                               "'%c'\n", c);
+                       exit(1);
+               }
+       }
+       if (s[i] && s[i+1]) {
+               fprintf(stderr, "ERROR: Illegal suffix contains "
+                       "character '%c' in wrong position\n",
+                       s[i+1]);
+               exit(51);
+       }
+       return strtoull(s, NULL, 10) * mult;
+}
+