2008-01-04 23:38:22 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Oracle. All rights reserved.
|
2013-01-30 16:32:47 +08:00
|
|
|
* Copyright (C) 2008 Morey Roof. All rights reserved.
|
2008-01-04 23:38:22 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public
|
|
|
|
* License v2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public
|
|
|
|
* License along with this program; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 021110-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2013-01-21 05:04:07 +08:00
|
|
|
#include <string.h>
|
2008-03-25 03:04:49 +08:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mount.h>
|
2008-01-04 23:38:22 +08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2008-04-01 22:36:46 +08:00
|
|
|
#include <mntent.h>
|
2012-10-30 01:53:18 +08:00
|
|
|
#include <ctype.h>
|
2010-09-09 10:57:02 +08:00
|
|
|
#include <linux/loop.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/kdev_t.h>
|
|
|
|
#include <limits.h>
|
2013-04-15 14:38:09 +08:00
|
|
|
#include <blkid/blkid.h>
|
2014-02-14 03:19:01 +08:00
|
|
|
#include <sys/vfs.h>
|
2015-08-26 17:04:22 +08:00
|
|
|
#include <sys/statfs.h>
|
|
|
|
#include <linux/magic.h>
|
2016-01-14 17:30:35 +08:00
|
|
|
#include <getopt.h>
|
2014-02-14 03:19:01 +08:00
|
|
|
|
2008-01-04 23:38:22 +08:00
|
|
|
#include "kerncompat.h"
|
|
|
|
#include "radix-tree.h"
|
|
|
|
#include "ctree.h"
|
|
|
|
#include "disk-io.h"
|
|
|
|
#include "transaction.h"
|
|
|
|
#include "crc32c.h"
|
|
|
|
#include "utils.h"
|
2008-03-25 03:04:49 +08:00
|
|
|
#include "volumes.h"
|
2008-03-25 03:05:44 +08:00
|
|
|
#include "ioctl.h"
|
2016-01-14 17:30:35 +08:00
|
|
|
#include "commands.h"
|
2008-03-25 03:04:49 +08:00
|
|
|
|
2011-04-22 04:24:07 +08:00
|
|
|
#ifndef BLKDISCARD
|
|
|
|
#define BLKDISCARD _IO(0x12,119)
|
|
|
|
#endif
|
|
|
|
|
2014-10-31 12:11:20 +08:00
|
|
|
static int btrfs_scan_done = 0;
|
|
|
|
|
2014-06-30 11:54:11 +08:00
|
|
|
static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
|
|
|
|
|
2015-06-09 05:30:21 +08:00
|
|
|
const char *get_argv0_buf(void)
|
|
|
|
{
|
|
|
|
return argv0_buf;
|
|
|
|
}
|
|
|
|
|
2014-06-30 11:54:11 +08:00
|
|
|
void fixup_argv0(char **argv, const char *token)
|
|
|
|
{
|
|
|
|
int len = strlen(argv0_buf);
|
|
|
|
|
|
|
|
snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
|
|
|
|
argv[0] = argv0_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_argv0(char **argv)
|
|
|
|
{
|
2014-11-19 01:05:20 +08:00
|
|
|
strncpy(argv0_buf, argv[0], sizeof(argv0_buf));
|
|
|
|
argv0_buf[sizeof(argv0_buf) - 1] = 0;
|
2014-06-30 11:54:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int check_argc_exact(int nargs, int expected)
|
|
|
|
{
|
|
|
|
if (nargs < expected)
|
|
|
|
fprintf(stderr, "%s: too few arguments\n", argv0_buf);
|
|
|
|
if (nargs > expected)
|
|
|
|
fprintf(stderr, "%s: too many arguments\n", argv0_buf);
|
|
|
|
|
|
|
|
return nargs != expected;
|
|
|
|
}
|
|
|
|
|
|
|
|
int check_argc_min(int nargs, int expected)
|
|
|
|
{
|
|
|
|
if (nargs < expected) {
|
|
|
|
fprintf(stderr, "%s: too few arguments\n", argv0_buf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int check_argc_max(int nargs, int expected)
|
|
|
|
{
|
|
|
|
if (nargs > expected) {
|
|
|
|
fprintf(stderr, "%s: too many arguments\n", argv0_buf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-27 23:19:37 +08:00
|
|
|
/*
|
|
|
|
* Discard the given range in one go
|
|
|
|
*/
|
|
|
|
static int discard_range(int fd, u64 start, u64 len)
|
2011-04-22 04:24:07 +08:00
|
|
|
{
|
|
|
|
u64 range[2] = { start, len };
|
|
|
|
|
|
|
|
if (ioctl(fd, BLKDISCARD, &range) < 0)
|
|
|
|
return errno;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-27 23:19:37 +08:00
|
|
|
/*
|
|
|
|
* Discard blocks in the given range in 1G chunks, the process is interruptible
|
|
|
|
*/
|
|
|
|
static int discard_blocks(int fd, u64 start, u64 len)
|
|
|
|
{
|
|
|
|
while (len > 0) {
|
|
|
|
/* 1G granularity */
|
|
|
|
u64 chunk_size = min_t(u64, len, 1*1024*1024*1024);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = discard_range(fd, start, chunk_size);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
len -= chunk_size;
|
|
|
|
start += chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
static u64 reference_root_table[] = {
|
2008-01-04 23:38:22 +08:00
|
|
|
[1] = BTRFS_ROOT_TREE_OBJECTID,
|
|
|
|
[2] = BTRFS_EXTENT_TREE_OBJECTID,
|
2008-03-25 03:03:18 +08:00
|
|
|
[3] = BTRFS_CHUNK_TREE_OBJECTID,
|
|
|
|
[4] = BTRFS_DEV_TREE_OBJECTID,
|
|
|
|
[5] = BTRFS_FS_TREE_OBJECTID,
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
[6] = BTRFS_CSUM_TREE_OBJECTID,
|
2008-01-04 23:38:22 +08:00
|
|
|
};
|
|
|
|
|
2014-05-15 01:39:07 +08:00
|
|
|
int test_uuid_unique(char *fs_uuid)
|
|
|
|
{
|
|
|
|
int unique = 1;
|
|
|
|
blkid_dev_iterate iter = NULL;
|
|
|
|
blkid_dev dev = NULL;
|
|
|
|
blkid_cache cache = NULL;
|
|
|
|
|
2016-01-04 09:01:30 +08:00
|
|
|
if (blkid_get_cache(&cache, NULL) < 0) {
|
2014-05-15 01:39:07 +08:00
|
|
|
printf("ERROR: lblkid cache get failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
blkid_probe_all(cache);
|
|
|
|
iter = blkid_dev_iterate_begin(cache);
|
|
|
|
blkid_dev_set_search(iter, "UUID", fs_uuid);
|
|
|
|
|
|
|
|
while (blkid_dev_next(iter, &dev) == 0) {
|
|
|
|
dev = blkid_verify(cache, dev);
|
|
|
|
if (dev) {
|
|
|
|
unique = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blkid_dev_iterate_end(iter);
|
|
|
|
blkid_put_cache(cache);
|
|
|
|
|
|
|
|
return unique;
|
|
|
|
}
|
|
|
|
|
2014-12-18 04:14:09 +08:00
|
|
|
/*
|
|
|
|
* @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
|
|
|
|
*/
|
2015-07-01 23:54:43 +08:00
|
|
|
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
|
2008-01-04 23:38:22 +08:00
|
|
|
{
|
|
|
|
struct btrfs_super_block super;
|
2015-11-07 01:52:09 +08:00
|
|
|
struct extent_buffer *buf;
|
2008-01-04 23:38:22 +08:00
|
|
|
struct btrfs_root_item root_item;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
struct btrfs_extent_item *extent_item;
|
|
|
|
struct btrfs_inode_item *inode_item;
|
2008-03-25 03:03:18 +08:00
|
|
|
struct btrfs_chunk *chunk;
|
|
|
|
struct btrfs_dev_item *dev_item;
|
|
|
|
struct btrfs_dev_extent *dev_extent;
|
2008-04-16 03:42:08 +08:00
|
|
|
u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
|
2008-03-25 03:03:18 +08:00
|
|
|
u8 *ptr;
|
2008-01-04 23:38:22 +08:00
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
u32 itemoff;
|
|
|
|
u32 nritems = 0;
|
|
|
|
u64 first_free;
|
|
|
|
u64 ref_root;
|
2008-03-25 03:03:18 +08:00
|
|
|
u32 array_size;
|
|
|
|
u32 item_size;
|
2015-07-01 23:49:21 +08:00
|
|
|
int skinny_metadata = !!(cfg->features &
|
2013-08-17 21:48:29 +08:00
|
|
|
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
|
2015-07-01 23:49:21 +08:00
|
|
|
u64 num_bytes;
|
2008-01-04 23:38:22 +08:00
|
|
|
|
2015-11-07 01:52:09 +08:00
|
|
|
buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
|
|
|
|
first_free &= ~((u64)cfg->sectorsize - 1);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
2008-04-18 22:31:42 +08:00
|
|
|
memset(&super, 0, sizeof(super));
|
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize;
|
|
|
|
if (cfg->fs_uuid && *cfg->fs_uuid) {
|
|
|
|
if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) {
|
|
|
|
fprintf(stderr, "could not parse UUID: %s\n",
|
|
|
|
cfg->fs_uuid);
|
2014-05-15 01:39:07 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-07-01 23:49:21 +08:00
|
|
|
if (!test_uuid_unique(cfg->fs_uuid)) {
|
|
|
|
fprintf(stderr, "non-unique UUID: %s\n", cfg->fs_uuid);
|
2014-05-15 01:39:07 +08:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uuid_generate(super.fsid);
|
2015-07-01 23:49:21 +08:00
|
|
|
if (cfg->fs_uuid)
|
|
|
|
uuid_unparse(super.fsid, cfg->fs_uuid);
|
2014-05-15 01:39:07 +08:00
|
|
|
}
|
2008-04-16 03:42:08 +08:00
|
|
|
uuid_generate(super.dev_item.uuid);
|
|
|
|
uuid_generate(chunk_tree_uuid);
|
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_super_bytenr(&super, cfg->blocks[0]);
|
2008-03-25 03:04:49 +08:00
|
|
|
btrfs_set_super_num_devices(&super, 1);
|
2013-06-26 13:27:08 +08:00
|
|
|
btrfs_set_super_magic(&super, BTRFS_MAGIC);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_super_generation(&super, 1);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_super_root(&super, cfg->blocks[1]);
|
|
|
|
btrfs_set_super_chunk_root(&super, cfg->blocks[3]);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_super_total_bytes(&super, num_bytes);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_super_bytes_used(&super, 6 * cfg->nodesize);
|
|
|
|
btrfs_set_super_sectorsize(&super, cfg->sectorsize);
|
|
|
|
btrfs_set_super_leafsize(&super, cfg->nodesize);
|
|
|
|
btrfs_set_super_nodesize(&super, cfg->nodesize);
|
|
|
|
btrfs_set_super_stripesize(&super, cfg->stripesize);
|
2008-12-02 22:58:23 +08:00
|
|
|
btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
|
2008-10-30 02:07:47 +08:00
|
|
|
btrfs_set_super_chunk_root_generation(&super, 1);
|
2010-12-10 02:27:03 +08:00
|
|
|
btrfs_set_super_cache_generation(&super, -1);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_super_incompat_flags(&super, cfg->features);
|
|
|
|
if (cfg->label)
|
|
|
|
strncpy(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
/* create the tree of root objects */
|
2015-07-01 23:49:21 +08:00
|
|
|
memset(buf->data, 0, cfg->nodesize);
|
|
|
|
buf->len = cfg->nodesize;
|
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[1]);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
btrfs_set_header_nritems(buf, 4);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_header_generation(buf, 1);
|
2009-05-30 04:35:30 +08:00
|
|
|
btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
|
2013-10-01 17:59:22 +08:00
|
|
|
write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
|
|
|
|
BTRFS_FSID_SIZE);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
2013-10-02 20:28:27 +08:00
|
|
|
write_extent_buffer(buf, chunk_tree_uuid,
|
2008-04-16 03:42:08 +08:00
|
|
|
btrfs_header_chunk_tree_uuid(buf),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
|
2008-01-04 23:38:22 +08:00
|
|
|
/* create the items for the root tree */
|
|
|
|
memset(&root_item, 0, sizeof(root_item));
|
|
|
|
inode_item = &root_item.inode;
|
|
|
|
btrfs_set_stack_inode_generation(inode_item, 1);
|
|
|
|
btrfs_set_stack_inode_size(inode_item, 3);
|
|
|
|
btrfs_set_stack_inode_nlink(inode_item, 1);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
|
|
|
|
btrfs_set_root_refs(&root_item, 1);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_root_used(&root_item, cfg->nodesize);
|
2008-10-30 02:07:47 +08:00
|
|
|
btrfs_set_root_generation(&root_item, 1);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
memset(&disk_key, 0, sizeof(disk_key));
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 0);
|
2008-03-25 03:04:37 +08:00
|
|
|
nritems = 0;
|
2008-01-04 23:38:22 +08:00
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item);
|
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[2]);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
|
2008-03-25 03:04:37 +08:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-01-04 23:38:22 +08:00
|
|
|
sizeof(root_item));
|
2008-03-25 03:04:37 +08:00
|
|
|
write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
|
|
|
|
nritems), sizeof(root_item));
|
|
|
|
nritems++;
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[4]);
|
2008-03-25 03:04:37 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-01-04 23:38:22 +08:00
|
|
|
sizeof(root_item));
|
2008-03-25 03:04:37 +08:00
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
|
|
|
sizeof(root_item));
|
|
|
|
nritems++;
|
2008-03-25 03:03:18 +08:00
|
|
|
|
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[5]);
|
2008-03-25 03:04:37 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-03-25 03:04:37 +08:00
|
|
|
sizeof(root_item));
|
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
2008-03-25 03:03:18 +08:00
|
|
|
sizeof(root_item));
|
2008-03-25 03:04:37 +08:00
|
|
|
nritems++;
|
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[6]);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
sizeof(root_item));
|
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
|
|
|
sizeof(root_item));
|
|
|
|
nritems++;
|
|
|
|
|
2008-03-25 03:03:18 +08:00
|
|
|
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
/* create the items for the extent tree */
|
2015-02-02 22:51:15 +08:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 23:49:21 +08:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-25 03:04:37 +08:00
|
|
|
nritems = 0;
|
2015-07-01 23:49:21 +08:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
for (i = 1; i < 7; i++) {
|
2013-08-17 21:48:29 +08:00
|
|
|
item_size = sizeof(struct btrfs_extent_item);
|
|
|
|
if (!skinny_metadata)
|
|
|
|
item_size += sizeof(struct btrfs_tree_block_info);
|
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
BUG_ON(cfg->blocks[i] < first_free);
|
|
|
|
BUG_ON(cfg->blocks[i] < cfg->blocks[i - 1]);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
/* create extent item */
|
2013-08-17 21:48:29 +08:00
|
|
|
itemoff -= item_size;
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
|
2013-08-17 21:48:29 +08:00
|
|
|
if (skinny_metadata) {
|
|
|
|
btrfs_set_disk_key_type(&disk_key,
|
|
|
|
BTRFS_METADATA_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 0);
|
|
|
|
} else {
|
|
|
|
btrfs_set_disk_key_type(&disk_key,
|
|
|
|
BTRFS_EXTENT_ITEM_KEY);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
|
2013-08-17 21:48:29 +08:00
|
|
|
}
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
|
2008-01-04 23:38:22 +08:00
|
|
|
itemoff);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2013-08-17 21:48:29 +08:00
|
|
|
item_size);
|
2008-01-04 23:38:22 +08:00
|
|
|
extent_item = btrfs_item_ptr(buf, nritems,
|
|
|
|
struct btrfs_extent_item);
|
|
|
|
btrfs_set_extent_refs(buf, extent_item, 1);
|
2009-05-30 04:35:30 +08:00
|
|
|
btrfs_set_extent_generation(buf, extent_item, 1);
|
|
|
|
btrfs_set_extent_flags(buf, extent_item,
|
|
|
|
BTRFS_EXTENT_FLAG_TREE_BLOCK);
|
2008-01-04 23:38:22 +08:00
|
|
|
nritems++;
|
|
|
|
|
|
|
|
/* create extent ref */
|
|
|
|
ref_root = reference_root_table[i];
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
|
2009-05-30 04:35:30 +08:00
|
|
|
btrfs_set_disk_key_offset(&disk_key, ref_root);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
|
2008-01-04 23:38:22 +08:00
|
|
|
itemoff);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
|
2008-01-04 23:38:22 +08:00
|
|
|
nritems++;
|
|
|
|
}
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[2]);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-04 23:38:22 +08:00
|
|
|
|
2008-03-25 03:03:18 +08:00
|
|
|
/* create the chunk tree */
|
2015-02-02 22:51:15 +08:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 23:49:21 +08:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-25 03:03:18 +08:00
|
|
|
nritems = 0;
|
2008-04-16 03:42:08 +08:00
|
|
|
item_size = sizeof(*dev_item);
|
2015-07-01 23:49:21 +08:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size;
|
2008-03-25 03:03:18 +08:00
|
|
|
|
2008-04-16 03:42:08 +08:00
|
|
|
/* first device 1 (there is no device 0) */
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 1);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
|
2008-04-16 03:42:08 +08:00
|
|
|
|
|
|
|
dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
|
|
|
|
btrfs_set_device_id(buf, dev_item, 1);
|
2008-11-18 23:40:06 +08:00
|
|
|
btrfs_set_device_generation(buf, dev_item, 0);
|
2008-04-16 03:42:08 +08:00
|
|
|
btrfs_set_device_total_bytes(buf, dev_item, num_bytes);
|
|
|
|
btrfs_set_device_bytes_used(buf, dev_item,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
|
|
|
|
btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
|
|
|
|
btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
|
2008-04-16 03:42:08 +08:00
|
|
|
btrfs_set_device_type(buf, dev_item, 0);
|
|
|
|
|
|
|
|
write_extent_buffer(buf, super.dev_item.uuid,
|
|
|
|
(unsigned long)btrfs_device_uuid(dev_item),
|
|
|
|
BTRFS_UUID_SIZE);
|
2008-11-18 23:40:06 +08:00
|
|
|
write_extent_buffer(buf, super.fsid,
|
|
|
|
(unsigned long)btrfs_device_fsid(dev_item),
|
|
|
|
BTRFS_UUID_SIZE);
|
2008-04-16 03:42:08 +08:00
|
|
|
read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item,
|
|
|
|
sizeof(*dev_item));
|
|
|
|
|
|
|
|
nritems++;
|
|
|
|
item_size = btrfs_chunk_item_size(1);
|
|
|
|
itemoff = itemoff - item_size;
|
|
|
|
|
|
|
|
/* then we have chunk 0 */
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 0);
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
|
2008-03-25 03:03:18 +08:00
|
|
|
|
|
|
|
chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
|
2008-04-16 03:42:08 +08:00
|
|
|
btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
|
|
|
|
btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024);
|
|
|
|
btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM);
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
|
|
|
|
btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
|
|
|
|
btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_chunk_num_stripes(buf, chunk, 1);
|
|
|
|
btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
|
|
|
|
btrfs_set_stripe_offset_nr(buf, chunk, 0, 0);
|
2008-04-16 03:42:08 +08:00
|
|
|
nritems++;
|
|
|
|
|
|
|
|
write_extent_buffer(buf, super.dev_item.uuid,
|
|
|
|
(unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe),
|
|
|
|
BTRFS_UUID_SIZE);
|
2008-03-25 03:03:18 +08:00
|
|
|
|
|
|
|
/* copy the key for the chunk to the system array */
|
|
|
|
ptr = super.sys_chunk_array;
|
|
|
|
array_size = sizeof(disk_key);
|
|
|
|
|
|
|
|
memcpy(ptr, &disk_key, sizeof(disk_key));
|
|
|
|
ptr += sizeof(disk_key);
|
|
|
|
|
|
|
|
/* copy the chunk to the system array */
|
|
|
|
read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size);
|
|
|
|
array_size += item_size;
|
|
|
|
ptr += item_size;
|
2008-03-25 03:04:37 +08:00
|
|
|
btrfs_set_super_sys_array_size(&super, array_size);
|
2008-03-25 03:03:18 +08:00
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[3]);
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-03-25 03:03:18 +08:00
|
|
|
|
|
|
|
/* create the device tree */
|
2015-02-02 22:51:15 +08:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 23:49:21 +08:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-25 03:03:18 +08:00
|
|
|
nritems = 0;
|
2015-07-01 23:49:21 +08:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) -
|
2008-03-25 03:03:18 +08:00
|
|
|
sizeof(struct btrfs_dev_extent);
|
|
|
|
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, 1);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 0);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 17:55:26 +08:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-03-25 03:03:18 +08:00
|
|
|
sizeof(struct btrfs_dev_extent));
|
|
|
|
dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
|
2008-04-16 03:42:08 +08:00
|
|
|
btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
|
|
|
|
BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
|
|
|
|
BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0);
|
|
|
|
|
|
|
|
write_extent_buffer(buf, chunk_tree_uuid,
|
|
|
|
(unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_dev_extent_length(buf, dev_extent,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
|
|
|
nritems++;
|
|
|
|
|
2015-07-01 23:49:21 +08:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[4]);
|
2008-03-25 03:03:18 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-03-25 03:03:18 +08:00
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
/* create the FS root */
|
2015-02-02 22:51:15 +08:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 23:49:21 +08:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[5]);
|
2008-01-04 23:38:22 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, 0);
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
/* finally create the csum root */
|
2015-02-02 22:51:15 +08:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 23:49:21 +08:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[6]);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, 0);
|
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 23:49:21 +08:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 06:00:31 +08:00
|
|
|
|
2008-03-25 03:03:18 +08:00
|
|
|
/* and write out the super block */
|
2015-07-01 23:49:21 +08:00
|
|
|
BUG_ON(sizeof(super) > cfg->sectorsize);
|
2015-11-19 22:55:05 +08:00
|
|
|
memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
|
2008-03-25 03:03:18 +08:00
|
|
|
memcpy(buf->data, &super, sizeof(super));
|
2015-11-19 22:55:05 +08:00
|
|
|
buf->len = BTRFS_SUPER_INFO_SIZE;
|
2008-12-02 22:58:23 +08:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-11-19 22:55:05 +08:00
|
|
|
ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
|
|
|
|
if (ret != BTRFS_SUPER_INFO_SIZE) {
|
2013-11-07 07:15:45 +08:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2008-03-25 03:03:18 +08:00
|
|
|
|
2013-11-07 07:15:45 +08:00
|
|
|
out:
|
2008-01-04 23:38:22 +08:00
|
|
|
free(buf);
|
2013-11-07 07:15:45 +08:00
|
|
|
return ret;
|
2008-01-04 23:38:22 +08:00
|
|
|
}
|
|
|
|
|
2015-03-24 02:20:37 +08:00
|
|
|
static const struct btrfs_fs_feature {
|
|
|
|
const char *name;
|
|
|
|
u64 flag;
|
|
|
|
const char *desc;
|
|
|
|
} mkfs_features[] = {
|
|
|
|
{ "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS,
|
|
|
|
"mixed data and metadata block groups" },
|
|
|
|
{ "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,
|
|
|
|
"increased hardlink limit per file to 65536" },
|
|
|
|
{ "raid56", BTRFS_FEATURE_INCOMPAT_RAID56,
|
|
|
|
"raid56 extended format" },
|
|
|
|
{ "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA,
|
|
|
|
"reduced-size metadata extent refs" },
|
|
|
|
{ "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES,
|
|
|
|
"no explicit hole extents for files" },
|
|
|
|
/* Keep this one last */
|
|
|
|
{ "list-all", BTRFS_FEATURE_LIST_ALL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int parse_one_fs_feature(const char *name, u64 *flags)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
|
|
|
|
if (name[0] == '^' &&
|
|
|
|
!strcmp(mkfs_features[i].name, name + 1)) {
|
|
|
|
*flags &= ~ mkfs_features[i].flag;
|
|
|
|
found = 1;
|
|
|
|
} else if (!strcmp(mkfs_features[i].name, name)) {
|
|
|
|
*flags |= mkfs_features[i].flag;
|
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return !found;
|
|
|
|
}
|
|
|
|
|
2015-06-05 01:08:15 +08:00
|
|
|
void btrfs_parse_features_to_string(char *buf, u64 flags)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
buf[0] = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
|
|
|
|
if (flags & mkfs_features[i].flag) {
|
|
|
|
if (*buf)
|
|
|
|
strcat(buf, ", ");
|
|
|
|
strcat(buf, mkfs_features[i].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-24 02:20:37 +08:00
|
|
|
void btrfs_process_fs_features(u64 flags)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) {
|
|
|
|
if (flags & mkfs_features[i].flag) {
|
|
|
|
printf("Turning ON incompat feature '%s': %s\n",
|
|
|
|
mkfs_features[i].name,
|
|
|
|
mkfs_features[i].desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-24 02:49:51 +08:00
|
|
|
void btrfs_list_all_fs_features(u64 mask_disallowed)
|
2015-03-24 02:20:37 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2015-03-24 02:49:51 +08:00
|
|
|
fprintf(stderr, "Filesystem features available:\n");
|
2015-03-24 02:20:37 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) {
|
|
|
|
char *is_default = "";
|
|
|
|
|
2015-03-24 02:49:51 +08:00
|
|
|
if (mkfs_features[i].flag & mask_disallowed)
|
|
|
|
continue;
|
2015-03-24 02:20:37 +08:00
|
|
|
if (mkfs_features[i].flag & BTRFS_MKFS_DEFAULT_FEATURES)
|
|
|
|
is_default = ", default";
|
|
|
|
fprintf(stderr, "%-20s- %s (0x%llx%s)\n",
|
|
|
|
mkfs_features[i].name,
|
|
|
|
mkfs_features[i].desc,
|
|
|
|
mkfs_features[i].flag,
|
|
|
|
is_default);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return NULL if all features were parsed fine, otherwise return the name of
|
|
|
|
* the first unparsed.
|
|
|
|
*/
|
|
|
|
char* btrfs_parse_fs_features(char *namelist, u64 *flags)
|
|
|
|
{
|
|
|
|
char *this_char;
|
|
|
|
char *save_ptr = NULL; /* Satisfy static checkers */
|
|
|
|
|
|
|
|
for (this_char = strtok_r(namelist, ",", &save_ptr);
|
|
|
|
this_char != NULL;
|
|
|
|
this_char = strtok_r(NULL, ",", &save_ptr)) {
|
|
|
|
if (parse_one_fs_feature(this_char, flags))
|
|
|
|
return this_char;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-28 04:55:41 +08:00
|
|
|
u64 btrfs_device_size(int fd, struct stat *st)
|
2008-03-25 03:04:49 +08:00
|
|
|
{
|
|
|
|
u64 size;
|
|
|
|
if (S_ISREG(st->st_mode)) {
|
|
|
|
return st->st_size;
|
|
|
|
}
|
|
|
|
if (!S_ISBLK(st->st_mode)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, BLKGETSIZE64, &size) >= 0) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zero_blocks(int fd, off_t start, size_t len)
|
|
|
|
{
|
|
|
|
char *buf = malloc(len);
|
|
|
|
int ret = 0;
|
|
|
|
ssize_t written;
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
memset(buf, 0, len);
|
|
|
|
written = pwrite(fd, buf, len, start);
|
|
|
|
if (written != len)
|
|
|
|
ret = -EIO;
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-07-17 05:02:37 +08:00
|
|
|
#define ZERO_DEV_BYTES (2 * 1024 * 1024)
|
|
|
|
|
|
|
|
/* don't write outside the device by clamping the region to the device size */
|
|
|
|
static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size)
|
2008-03-25 03:04:49 +08:00
|
|
|
{
|
2014-07-17 05:02:37 +08:00
|
|
|
off_t end = max(start, start + len);
|
2008-03-25 03:04:49 +08:00
|
|
|
|
|
|
|
#ifdef __sparc__
|
2014-07-17 05:02:37 +08:00
|
|
|
/* and don't overwrite the disk labels on sparc */
|
|
|
|
start = max(start, 1024);
|
|
|
|
end = max(end, 1024);
|
2008-03-25 03:04:49 +08:00
|
|
|
#endif
|
|
|
|
|
2014-07-17 05:02:37 +08:00
|
|
|
start = min_t(u64, start, dev_size);
|
|
|
|
end = min_t(u64, end, dev_size);
|
2008-03-25 03:04:49 +08:00
|
|
|
|
2014-07-17 05:02:37 +08:00
|
|
|
return zero_blocks(fd, start, end - start);
|
2008-03-25 03:04:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
|
2008-04-18 22:31:42 +08:00
|
|
|
struct btrfs_root *root, int fd, char *path,
|
2015-11-03 11:23:37 +08:00
|
|
|
u64 device_total_bytes, u32 io_width, u32 io_align,
|
2008-04-18 22:31:42 +08:00
|
|
|
u32 sectorsize)
|
2008-03-25 03:04:49 +08:00
|
|
|
{
|
|
|
|
struct btrfs_super_block *disk_super;
|
2013-03-07 00:32:51 +08:00
|
|
|
struct btrfs_super_block *super = root->fs_info->super_copy;
|
2008-04-04 04:35:48 +08:00
|
|
|
struct btrfs_device *device;
|
2008-03-25 03:04:49 +08:00
|
|
|
struct btrfs_dev_item *dev_item;
|
2015-08-27 23:38:18 +08:00
|
|
|
char *buf = NULL;
|
2015-11-03 11:23:37 +08:00
|
|
|
u64 fs_total_bytes;
|
2008-03-25 03:04:49 +08:00
|
|
|
u64 num_devs;
|
|
|
|
int ret;
|
|
|
|
|
2015-11-03 11:23:36 +08:00
|
|
|
device_total_bytes = (device_total_bytes / sectorsize) * sectorsize;
|
|
|
|
|
2013-04-26 10:44:45 +08:00
|
|
|
device = kzalloc(sizeof(*device), GFP_NOFS);
|
2008-04-04 04:35:48 +08:00
|
|
|
if (!device)
|
2015-08-27 23:38:18 +08:00
|
|
|
goto err_nomem;
|
|
|
|
buf = kzalloc(sectorsize, GFP_NOFS);
|
|
|
|
if (!buf)
|
|
|
|
goto err_nomem;
|
2008-03-25 03:04:49 +08:00
|
|
|
BUG_ON(sizeof(*disk_super) > sectorsize);
|
|
|
|
|
|
|
|
disk_super = (struct btrfs_super_block *)buf;
|
|
|
|
dev_item = &disk_super->dev_item;
|
|
|
|
|
2008-04-04 04:35:48 +08:00
|
|
|
uuid_generate(device->uuid);
|
|
|
|
device->devid = 0;
|
|
|
|
device->type = 0;
|
|
|
|
device->io_width = io_width;
|
|
|
|
device->io_align = io_align;
|
|
|
|
device->sector_size = sectorsize;
|
2008-04-05 03:42:17 +08:00
|
|
|
device->fd = fd;
|
2008-11-18 23:40:06 +08:00
|
|
|
device->writeable = 1;
|
2015-11-03 11:23:37 +08:00
|
|
|
device->total_bytes = device_total_bytes;
|
2008-04-04 04:35:48 +08:00
|
|
|
device->bytes_used = 0;
|
|
|
|
device->total_ios = 0;
|
|
|
|
device->dev_root = root->fs_info->dev_root;
|
2014-12-18 04:14:08 +08:00
|
|
|
device->name = strdup(path);
|
2015-08-27 23:38:18 +08:00
|
|
|
if (!device->name)
|
|
|
|
goto err_nomem;
|
2008-04-04 04:35:48 +08:00
|
|
|
|
2015-10-22 11:58:05 +08:00
|
|
|
INIT_LIST_HEAD(&device->dev_list);
|
2008-04-04 04:35:48 +08:00
|
|
|
ret = btrfs_add_device(trans, root, device);
|
2008-03-25 03:04:49 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
|
2015-11-03 11:23:37 +08:00
|
|
|
fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes;
|
|
|
|
btrfs_set_super_total_bytes(super, fs_total_bytes);
|
2008-03-25 03:04:49 +08:00
|
|
|
|
|
|
|
num_devs = btrfs_super_num_devices(super) + 1;
|
|
|
|
btrfs_set_super_num_devices(super, num_devs);
|
|
|
|
|
|
|
|
memcpy(disk_super, super, sizeof(*disk_super));
|
|
|
|
|
2008-12-18 05:10:07 +08:00
|
|
|
btrfs_set_super_bytenr(disk_super, BTRFS_SUPER_INFO_OFFSET);
|
2008-04-04 04:35:48 +08:00
|
|
|
btrfs_set_stack_device_id(dev_item, device->devid);
|
|
|
|
btrfs_set_stack_device_type(dev_item, device->type);
|
|
|
|
btrfs_set_stack_device_io_align(dev_item, device->io_align);
|
|
|
|
btrfs_set_stack_device_io_width(dev_item, device->io_width);
|
|
|
|
btrfs_set_stack_device_sector_size(dev_item, device->sector_size);
|
|
|
|
btrfs_set_stack_device_total_bytes(dev_item, device->total_bytes);
|
|
|
|
btrfs_set_stack_device_bytes_used(dev_item, device->bytes_used);
|
2008-04-16 03:42:08 +08:00
|
|
|
memcpy(&dev_item->uuid, device->uuid, BTRFS_UUID_SIZE);
|
2008-03-25 03:04:49 +08:00
|
|
|
|
|
|
|
ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
|
|
|
|
BUG_ON(ret != sectorsize);
|
|
|
|
|
2008-04-04 04:35:48 +08:00
|
|
|
kfree(buf);
|
|
|
|
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
|
2008-11-18 23:40:06 +08:00
|
|
|
device->fs_devices = root->fs_info->fs_devices;
|
2008-03-25 03:04:49 +08:00
|
|
|
return 0;
|
2015-08-27 23:38:18 +08:00
|
|
|
|
|
|
|
err_nomem:
|
|
|
|
kfree(device);
|
|
|
|
kfree(buf);
|
|
|
|
return -ENOMEM;
|
2008-03-25 03:04:49 +08:00
|
|
|
}
|
|
|
|
|
2015-09-01 22:00:12 +08:00
|
|
|
static int btrfs_wipe_existing_sb(int fd)
|
2014-10-02 07:22:09 +08:00
|
|
|
{
|
|
|
|
const char *off = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
loff_t offset;
|
|
|
|
char buf[BUFSIZ];
|
2015-09-01 22:00:12 +08:00
|
|
|
int ret = 0;
|
2014-10-02 07:22:09 +08:00
|
|
|
blkid_probe pr = NULL;
|
|
|
|
|
|
|
|
pr = blkid_new_probe();
|
|
|
|
if (!pr)
|
2015-09-01 22:00:12 +08:00
|
|
|
return -1;
|
2014-10-02 07:22:09 +08:00
|
|
|
|
2015-09-01 22:00:12 +08:00
|
|
|
if (blkid_probe_set_device(pr, fd, 0, 0)) {
|
|
|
|
ret = -1;
|
2014-10-02 07:22:09 +08:00
|
|
|
goto out;
|
2015-09-01 22:00:12 +08:00
|
|
|
}
|
2014-10-02 07:22:09 +08:00
|
|
|
|
2015-09-01 22:00:12 +08:00
|
|
|
ret = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
|
|
|
|
if (!ret)
|
|
|
|
ret = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
|
2014-10-02 07:22:09 +08:00
|
|
|
|
2015-09-01 22:00:12 +08:00
|
|
|
if (ret || len == 0 || off == NULL) {
|
|
|
|
/*
|
|
|
|
* If lookup fails, the probe did not find any values, eg. for
|
|
|
|
* a file image or a loop device. Soft error.
|
|
|
|
*/
|
|
|
|
ret = 1;
|
2014-10-02 07:22:09 +08:00
|
|
|
goto out;
|
2015-09-01 22:00:12 +08:00
|
|
|
}
|
2014-10-02 07:22:09 +08:00
|
|
|
|
|
|
|
offset = strtoll(off, NULL, 10);
|
|
|
|
if (len > sizeof(buf))
|
|
|
|
len = sizeof(buf);
|
|
|
|
|
|
|
|
memset(buf, 0, len);
|
2015-09-01 22:00:12 +08:00
|
|
|
ret = pwrite(fd, buf, len, offset);
|
|
|
|
if (ret != len) {
|
|
|
|
fprintf(stderr, "ERROR: cannot wipe existing superblock\n");
|
|
|
|
ret = -1;
|
|
|
|
}
|
2014-10-02 07:22:09 +08:00
|
|
|
fsync(fd);
|
|
|
|
|
|
|
|
out:
|
|
|
|
blkid_free_probe(pr);
|
2015-09-01 22:00:12 +08:00
|
|
|
return ret;
|
2014-10-02 07:22:09 +08:00
|
|
|
}
|
|
|
|
|
2010-12-10 02:31:08 +08:00
|
|
|
int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
|
2015-10-15 01:39:37 +08:00
|
|
|
u64 max_block_count, int discard)
|
2008-03-25 03:04:49 +08:00
|
|
|
{
|
|
|
|
u64 block_count;
|
|
|
|
struct stat st;
|
2008-12-06 01:21:31 +08:00
|
|
|
int i, ret;
|
2008-03-25 03:04:49 +08:00
|
|
|
|
|
|
|
ret = fstat(fd, &st);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "unable to stat %s\n", file);
|
2013-12-18 12:07:55 +08:00
|
|
|
return 1;
|
2008-03-25 03:04:49 +08:00
|
|
|
}
|
|
|
|
|
2013-03-28 04:55:41 +08:00
|
|
|
block_count = btrfs_device_size(fd, &st);
|
2008-03-25 03:04:49 +08:00
|
|
|
if (block_count == 0) {
|
|
|
|
fprintf(stderr, "unable to find %s size\n", file);
|
2013-12-18 12:07:55 +08:00
|
|
|
return 1;
|
2008-03-25 03:04:49 +08:00
|
|
|
}
|
2012-07-27 20:37:55 +08:00
|
|
|
if (max_block_count)
|
|
|
|
block_count = min(block_count, max_block_count);
|
2008-03-25 03:04:49 +08:00
|
|
|
|
2013-09-17 22:54:00 +08:00
|
|
|
if (discard) {
|
2012-07-06 22:11:10 +08:00
|
|
|
/*
|
2013-09-21 00:42:07 +08:00
|
|
|
* We intentionally ignore errors from the discard ioctl. It
|
|
|
|
* is not necessary for the mkfs functionality but just an
|
|
|
|
* optimization.
|
2012-07-06 22:11:10 +08:00
|
|
|
*/
|
2014-04-22 21:30:27 +08:00
|
|
|
if (discard_range(fd, 0, 0) == 0) {
|
2015-01-26 16:26:38 +08:00
|
|
|
printf("Performing full device TRIM (%s) ...\n",
|
2013-09-21 00:42:07 +08:00
|
|
|
pretty_size(block_count));
|
|
|
|
discard_blocks(fd, 0, block_count);
|
|
|
|
}
|
2012-07-06 22:11:10 +08:00
|
|
|
}
|
2011-04-22 04:24:07 +08:00
|
|
|
|
2014-07-17 05:02:37 +08:00
|
|
|
ret = zero_dev_clamped(fd, 0, ZERO_DEV_BYTES, block_count);
|
|
|
|
for (i = 0 ; !ret && i < BTRFS_SUPER_MIRROR_MAX; i++)
|
|
|
|
ret = zero_dev_clamped(fd, btrfs_sb_offset(i),
|
|
|
|
BTRFS_SUPER_INFO_SIZE, block_count);
|
|
|
|
if (!ret && zero_end)
|
|
|
|
ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES,
|
|
|
|
ZERO_DEV_BYTES, block_count);
|
2013-12-18 12:07:55 +08:00
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "ERROR: failed to zero device '%s' - %s\n",
|
|
|
|
file, strerror(-ret));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-07-17 05:02:37 +08:00
|
|
|
|
2015-09-01 22:00:12 +08:00
|
|
|
ret = btrfs_wipe_existing_sb(fd);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "ERROR: cannot wipe superblocks on '%s'\n",
|
|
|
|
file);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-02 07:22:09 +08:00
|
|
|
|
2014-07-17 05:02:37 +08:00
|
|
|
*block_count_ret = block_count;
|
2008-03-25 03:04:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 23:38:22 +08:00
|
|
|
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root, u64 objectid)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_inode_item inode_item;
|
2010-11-17 09:07:52 +08:00
|
|
|
time_t now = time(NULL);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
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);
|
2015-02-02 22:51:15 +08:00
|
|
|
btrfs_set_stack_inode_nbytes(&inode_item, root->nodesize);
|
2012-07-17 18:18:10 +08:00
|
|
|
btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
|
2010-11-17 09:07:52 +08:00
|
|
|
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);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
if (root->fs_info->tree_root == root)
|
2013-03-07 00:32:51 +08:00
|
|
|
btrfs_set_super_root_dir(root->fs_info->super_copy, objectid);
|
2008-01-04 23:38:22 +08:00
|
|
|
|
|
|
|
ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
2008-07-25 00:13:32 +08:00
|
|
|
ret = btrfs_insert_inode_ref(trans, root, "..", 2, objectid, objectid, 0);
|
2008-01-04 23:38:22 +08:00
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
btrfs_set_root_dirid(&root->root_item, objectid);
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
|
|
return ret;
|
|
|
|
}
|
2008-03-25 03:05:44 +08:00
|
|
|
|
2013-03-12 07:12:59 +08:00
|
|
|
/*
|
|
|
|
* checks if a path is a block device node
|
|
|
|
* Returns negative errno on failure, otherwise
|
|
|
|
* returns 1 for blockdev, 0 for not-blockdev
|
|
|
|
*/
|
2013-10-22 13:53:22 +08:00
|
|
|
int is_block_device(const char *path)
|
|
|
|
{
|
2013-03-12 07:12:59 +08:00
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
if (stat(path, &statbuf) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2015-09-25 23:59:43 +08:00
|
|
|
return !!S_ISBLK(statbuf.st_mode);
|
2013-03-12 07:12:59 +08:00
|
|
|
}
|
|
|
|
|
2013-10-22 13:53:22 +08:00
|
|
|
/*
|
|
|
|
* check if given path is a mount point
|
|
|
|
* return 1 if yes. 0 if no. -1 for error
|
|
|
|
*/
|
|
|
|
int is_mount_point(const char *path)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
struct mntent *mnt;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
f = setmntent("/proc/self/mounts", "r");
|
|
|
|
if (f == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((mnt = getmntent(f)) != NULL) {
|
|
|
|
if (strcmp(mnt->mnt_dir, path))
|
|
|
|
continue;
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
endmntent(f);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-05 14:24:27 +08:00
|
|
|
static int is_reg_file(const char *path)
|
|
|
|
{
|
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
if (stat(path, &statbuf) < 0)
|
|
|
|
return -errno;
|
|
|
|
return S_ISREG(statbuf.st_mode);
|
|
|
|
}
|
|
|
|
|
2014-12-25 09:16:34 +08:00
|
|
|
/*
|
|
|
|
* This function checks if the given input parameter is
|
|
|
|
* an uuid or a path
|
2015-01-05 14:24:27 +08:00
|
|
|
* return <0 : some error in the given input
|
|
|
|
* return BTRFS_ARG_UNKNOWN: unknown input
|
|
|
|
* return BTRFS_ARG_UUID: given input is uuid
|
|
|
|
* return BTRFS_ARG_MNTPOINT: given input is path
|
|
|
|
* return BTRFS_ARG_REG: given input is regular file
|
2016-02-05 16:07:34 +08:00
|
|
|
* return BTRFS_ARG_BLKDEV: given input is block device
|
2014-12-25 09:16:34 +08:00
|
|
|
*/
|
|
|
|
int check_arg_type(const char *input)
|
|
|
|
{
|
|
|
|
uuid_t uuid;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
if (!input)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (realpath(input, path)) {
|
|
|
|
if (is_block_device(path) == 1)
|
|
|
|
return BTRFS_ARG_BLKDEV;
|
|
|
|
|
|
|
|
if (is_mount_point(path) == 1)
|
|
|
|
return BTRFS_ARG_MNTPOINT;
|
|
|
|
|
2015-01-05 14:24:27 +08:00
|
|
|
if (is_reg_file(path))
|
|
|
|
return BTRFS_ARG_REG;
|
|
|
|
|
2014-12-25 09:16:34 +08:00
|
|
|
return BTRFS_ARG_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(input) == (BTRFS_UUID_UNPARSED_SIZE - 1) &&
|
|
|
|
!uuid_parse(input, uuid))
|
|
|
|
return BTRFS_ARG_UUID;
|
|
|
|
|
|
|
|
return BTRFS_ARG_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2013-03-12 07:12:59 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-10-22 13:53:21 +08:00
|
|
|
int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
|
|
|
|
{
|
2013-03-12 07:12:59 +08:00
|
|
|
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) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
} else { /* mounted, all good */
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (fd != -1)
|
|
|
|
close(fd);
|
|
|
|
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.
|
|
|
|
*/
|
2015-10-12 21:23:02 +08:00
|
|
|
int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
|
2013-03-12 07:12:59 +08:00
|
|
|
{
|
2015-06-12 19:18:44 +08:00
|
|
|
char mp[PATH_MAX];
|
2015-10-12 21:23:02 +08:00
|
|
|
int ret;
|
2013-03-12 07:12:59 +08:00
|
|
|
|
2015-10-12 21:23:02 +08:00
|
|
|
if (is_block_device(path)) {
|
2013-03-12 07:12:59 +08:00
|
|
|
ret = get_btrfs_mount(path, mp, sizeof(mp));
|
|
|
|
if (ret < 0) {
|
|
|
|
/* not a mounted btrfs dev */
|
2015-10-12 21:23:02 +08:00
|
|
|
error_on(verbose, "'%s' is not a mounted btrfs device",
|
|
|
|
path);
|
2013-03-12 07:12:59 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-12 21:23:02 +08:00
|
|
|
ret = open_file_or_dir(mp, dirstream);
|
|
|
|
error_on(verbose && ret < 0, "can't access '%s': %s",
|
|
|
|
path, strerror(errno));
|
|
|
|
} else {
|
|
|
|
ret = btrfs_open_dir(path, dirstream, 1);
|
2013-03-12 07:12:59 +08:00
|
|
|
}
|
|
|
|
|
2015-10-12 21:23:02 +08:00
|
|
|
return ret;
|
2013-03-12 07:12:59 +08:00
|
|
|
}
|
|
|
|
|
2015-08-26 17:04:22 +08:00
|
|
|
/*
|
|
|
|
* Do the following checks before calling open_file_or_dir():
|
|
|
|
* 1: path is in a btrfs filesystem
|
|
|
|
* 2: path is a directory
|
|
|
|
*/
|
|
|
|
int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
|
|
|
|
{
|
|
|
|
struct statfs stfs;
|
|
|
|
struct stat st;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (statfs(path, &stfs) != 0) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: can't access '%s': %s\n",
|
|
|
|
path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stfs.f_type != BTRFS_SUPER_MAGIC) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: not a btrfs filesystem: %s\n",
|
|
|
|
path);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(path, &st) != 0) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: can't access '%s': %s\n",
|
|
|
|
path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: not a directory: %s\n",
|
|
|
|
path);
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = open_file_or_dir(path, dirstream);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: can't access '%s': %s\n",
|
|
|
|
path, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
/* checks if a device is a loop device */
|
2013-08-10 04:20:47 +08:00
|
|
|
static int is_loop_device (const char* device) {
|
2010-09-09 10:57:02 +08:00
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
if(stat(device, &statbuf) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return (S_ISBLK(statbuf.st_mode) &&
|
|
|
|
MAJOR(statbuf.st_rdev) == LOOP_MAJOR);
|
|
|
|
}
|
|
|
|
|
2015-11-14 01:09:05 +08:00
|
|
|
/*
|
|
|
|
* Takes a loop device path (e.g. /dev/loop0) and returns
|
|
|
|
* the associated file (e.g. /images/my_btrfs.img) using
|
|
|
|
* loopdev API
|
|
|
|
*/
|
|
|
|
static int resolve_loop_device_with_loopdev(const char* loop_dev, char* loop_file)
|
|
|
|
{
|
|
|
|
int fd;
|
2015-11-14 02:15:27 +08:00
|
|
|
int ret;
|
2015-11-14 01:09:05 +08:00
|
|
|
struct loop_info64 lo64;
|
|
|
|
|
|
|
|
fd = open(loop_dev, O_RDONLY | O_NONBLOCK);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
2015-11-14 02:15:27 +08:00
|
|
|
ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-11-14 01:09:05 +08:00
|
|
|
|
2015-11-14 01:20:38 +08:00
|
|
|
memcpy(loop_file, lo64.lo_file_name, sizeof(lo64.lo_file_name));
|
|
|
|
loop_file[sizeof(lo64.lo_file_name)] = 0;
|
2015-11-14 01:09:05 +08:00
|
|
|
|
2015-11-14 02:15:27 +08:00
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return ret;
|
2015-11-14 01:09:05 +08:00
|
|
|
}
|
2010-09-09 10:57:02 +08:00
|
|
|
|
|
|
|
/* Takes a loop device path (e.g. /dev/loop0) and returns
|
|
|
|
* the associated file (e.g. /images/my_btrfs.img) */
|
2013-08-10 04:20:47 +08:00
|
|
|
static int resolve_loop_device(const char* loop_dev, char* loop_file,
|
|
|
|
int max_len)
|
2010-09-09 10:57:02 +08:00
|
|
|
{
|
2013-01-21 05:04:07 +08:00
|
|
|
int ret;
|
|
|
|
FILE *f;
|
|
|
|
char fmt[20];
|
|
|
|
char p[PATH_MAX];
|
|
|
|
char real_loop_dev[PATH_MAX];
|
2010-09-09 10:57:02 +08:00
|
|
|
|
2013-01-21 05:04:07 +08:00
|
|
|
if (!realpath(loop_dev, real_loop_dev))
|
|
|
|
return -errno;
|
|
|
|
snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
|
2015-11-14 01:09:05 +08:00
|
|
|
if (!(f = fopen(p, "r"))) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
/*
|
|
|
|
* It's possibly a partitioned loop device, which is
|
|
|
|
* resolvable with loopdev API.
|
|
|
|
*/
|
|
|
|
return resolve_loop_device_with_loopdev(loop_dev, loop_file);
|
2010-09-09 10:57:02 +08:00
|
|
|
return -errno;
|
2015-11-14 01:09:05 +08:00
|
|
|
}
|
2010-09-09 10:57:02 +08:00
|
|
|
|
2013-01-21 05:04:07 +08:00
|
|
|
snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
|
|
|
|
ret = fscanf(f, fmt, loop_file);
|
|
|
|
fclose(f);
|
|
|
|
if (ret == EOF)
|
2010-09-09 10:57:02 +08:00
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-03 10:13:11 +08:00
|
|
|
/*
|
|
|
|
* Checks whether a and b are identical or device
|
2010-09-09 10:57:02 +08:00
|
|
|
* files associated with the same block device
|
|
|
|
*/
|
2013-08-10 04:20:47 +08:00
|
|
|
static int is_same_blk_file(const char* a, const char* b)
|
2010-09-09 10:57:02 +08:00
|
|
|
{
|
|
|
|
struct stat st_buf_a, st_buf_b;
|
|
|
|
char real_a[PATH_MAX];
|
|
|
|
char real_b[PATH_MAX];
|
|
|
|
|
2015-01-03 10:10:55 +08:00
|
|
|
if (!realpath(a, real_a))
|
|
|
|
strncpy_null(real_a, a);
|
2013-09-21 00:52:18 +08:00
|
|
|
|
|
|
|
if (!realpath(b, real_b))
|
2015-01-03 10:10:55 +08:00
|
|
|
strncpy_null(real_b, b);
|
2010-09-09 10:57:02 +08:00
|
|
|
|
|
|
|
/* Identical path? */
|
2015-01-03 10:13:11 +08:00
|
|
|
if (strcmp(real_a, real_b) == 0)
|
2010-09-09 10:57:02 +08:00
|
|
|
return 1;
|
|
|
|
|
2015-01-03 10:13:11 +08:00
|
|
|
if (stat(a, &st_buf_a) < 0 || stat(b, &st_buf_b) < 0) {
|
2011-10-28 04:23:14 +08:00
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
2010-09-09 10:57:02 +08:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Same blockdevice? */
|
2015-01-03 10:13:11 +08:00
|
|
|
if (S_ISBLK(st_buf_a.st_mode) && S_ISBLK(st_buf_b.st_mode) &&
|
|
|
|
st_buf_a.st_rdev == st_buf_b.st_rdev) {
|
2010-09-09 10:57:02 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hardlink? */
|
|
|
|
if (st_buf_a.st_dev == st_buf_b.st_dev &&
|
2015-01-03 10:13:11 +08:00
|
|
|
st_buf_a.st_ino == st_buf_b.st_ino) {
|
2010-09-09 10:57:02 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* checks if a and b are identical or device
|
|
|
|
* files associated with the same block device or
|
|
|
|
* if one file is a loop device that uses the other
|
|
|
|
* file.
|
|
|
|
*/
|
2013-08-10 04:20:47 +08:00
|
|
|
static int is_same_loop_file(const char* a, const char* b)
|
2010-09-09 10:57:02 +08:00
|
|
|
{
|
|
|
|
char res_a[PATH_MAX];
|
|
|
|
char res_b[PATH_MAX];
|
2013-09-21 00:52:18 +08:00
|
|
|
const char* final_a = NULL;
|
|
|
|
const char* final_b = NULL;
|
2010-09-09 10:57:02 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Resolve a if it is a loop device */
|
|
|
|
if((ret = is_loop_device(a)) < 0) {
|
2011-10-28 04:23:14 +08:00
|
|
|
if (ret == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
return ret;
|
|
|
|
} else if (ret) {
|
2013-09-21 00:52:18 +08:00
|
|
|
ret = resolve_loop_device(a, res_a, sizeof(res_a));
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno != EPERM)
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
final_a = res_a;
|
|
|
|
}
|
2010-09-09 10:57:02 +08:00
|
|
|
} else {
|
|
|
|
final_a = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resolve b if it is a loop device */
|
2011-10-28 04:23:14 +08:00
|
|
|
if ((ret = is_loop_device(b)) < 0) {
|
|
|
|
if (ret == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
return ret;
|
|
|
|
} else if (ret) {
|
2013-09-21 00:52:18 +08:00
|
|
|
ret = resolve_loop_device(b, res_b, sizeof(res_b));
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno != EPERM)
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
final_b = res_b;
|
|
|
|
}
|
2010-09-09 10:57:02 +08:00
|
|
|
} else {
|
|
|
|
final_b = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
return is_same_blk_file(final_a, final_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Checks if a file exists and is a block or regular file*/
|
2013-08-10 04:20:47 +08:00
|
|
|
static int is_existing_blk_or_reg_file(const char* filename)
|
2010-09-09 10:57:02 +08:00
|
|
|
{
|
|
|
|
struct stat st_buf;
|
|
|
|
|
|
|
|
if(stat(filename, &st_buf) < 0) {
|
|
|
|
if(errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (S_ISBLK(st_buf.st_mode) || S_ISREG(st_buf.st_mode));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Checks if a file is used (directly or indirectly via a loop device)
|
|
|
|
* by a device in fs_devices
|
|
|
|
*/
|
2013-08-10 04:20:47 +08:00
|
|
|
static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
|
|
|
|
const char* file)
|
2010-09-09 10:57:02 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct list_head *head;
|
|
|
|
struct list_head *cur;
|
|
|
|
struct btrfs_device *device;
|
|
|
|
|
|
|
|
head = &fs_devices->devices;
|
|
|
|
list_for_each(cur, head) {
|
|
|
|
device = list_entry(cur, struct btrfs_device, dev_list);
|
|
|
|
|
|
|
|
if((ret = is_same_loop_file(device->name, file)))
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-05 04:43:11 +08:00
|
|
|
/*
|
|
|
|
* Resolve a pathname to a device mapper node to /dev/mapper/<name>
|
|
|
|
* Returns NULL on invalid input or malloc failure; Other failures
|
|
|
|
* will be handled by the caller using the input pathame.
|
|
|
|
*/
|
|
|
|
char *canonicalize_dm_name(const char *ptname)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
size_t sz;
|
|
|
|
char path[PATH_MAX], name[PATH_MAX], *res = NULL;
|
|
|
|
|
|
|
|
if (!ptname || !*ptname)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
|
|
|
|
if (!(f = fopen(path, "r")))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* read <name>\n from sysfs */
|
|
|
|
if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
|
|
|
|
name[sz - 1] = '\0';
|
|
|
|
snprintf(path, sizeof(path), "/dev/mapper/%s", name);
|
|
|
|
|
|
|
|
if (access(path, F_OK) == 0)
|
|
|
|
res = strdup(path);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Resolve a pathname to a canonical device node, e.g. /dev/sda1 or
|
|
|
|
* to a device mapper pathname.
|
|
|
|
* Returns NULL on invalid input or malloc failure; Other failures
|
|
|
|
* will be handled by the caller using the input pathame.
|
|
|
|
*/
|
|
|
|
char *canonicalize_path(const char *path)
|
|
|
|
{
|
|
|
|
char *canonical, *p;
|
|
|
|
|
|
|
|
if (!path || !*path)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
canonical = realpath(path, NULL);
|
|
|
|
if (!canonical)
|
|
|
|
return strdup(path);
|
|
|
|
p = strrchr(canonical, '/');
|
|
|
|
if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) {
|
|
|
|
char *dm = canonicalize_dm_name(p + 1);
|
|
|
|
|
|
|
|
if (dm) {
|
|
|
|
free(canonical);
|
|
|
|
return dm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return canonical;
|
|
|
|
}
|
|
|
|
|
2008-04-01 22:36:46 +08:00
|
|
|
/*
|
|
|
|
* returns 1 if the device was mounted, < 0 on error or 0 if everything
|
2010-09-09 10:57:02 +08:00
|
|
|
* is safe to continue.
|
2008-04-01 22:36:46 +08:00
|
|
|
*/
|
2010-09-09 10:57:02 +08:00
|
|
|
int check_mounted(const char* file)
|
2008-04-01 22:36:46 +08:00
|
|
|
{
|
2010-09-09 10:57:02 +08:00
|
|
|
int fd;
|
2011-07-18 20:37:23 +08:00
|
|
|
int ret;
|
2008-04-01 22:36:46 +08:00
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
fd = open(file, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf (stderr, "check_mounted(): Could not open %s\n", file);
|
2008-04-01 22:36:46 +08:00
|
|
|
return -errno;
|
2010-09-09 10:57:02 +08:00
|
|
|
}
|
2008-04-01 22:36:46 +08:00
|
|
|
|
2011-07-18 20:37:23 +08:00
|
|
|
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;
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
/* scan the initial device */
|
|
|
|
ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt,
|
2014-07-03 17:36:36 +08:00
|
|
|
&total_devs, BTRFS_SUPER_INFO_OFFSET, 0);
|
2010-09-09 10:57:02 +08:00
|
|
|
is_btrfs = (ret >= 0);
|
|
|
|
|
|
|
|
/* scan other devices */
|
|
|
|
if (is_btrfs && total_devs > 1) {
|
2014-10-31 12:11:20 +08:00
|
|
|
ret = btrfs_scan_lblkid();
|
2014-09-13 09:21:21 +08:00
|
|
|
if (ret)
|
2010-09-09 10:57:02 +08:00
|
|
|
return ret;
|
2008-04-01 22:36:46 +08:00
|
|
|
}
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
/* iterate over the list of currently mountes filesystems */
|
2013-11-07 10:53:43 +08:00
|
|
|
if ((f = setmntent ("/proc/self/mounts", "r")) == NULL)
|
2010-09-09 10:57:02 +08:00
|
|
|
return -errno;
|
|
|
|
|
2008-04-01 22:36:46 +08:00
|
|
|
while ((mnt = getmntent (f)) != NULL) {
|
2010-09-09 10:57:02 +08:00
|
|
|
if(is_btrfs) {
|
|
|
|
if(strcmp(mnt->mnt_type, "btrfs") != 0)
|
|
|
|
continue;
|
2008-04-01 22:36:46 +08:00
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
ret = blk_file_in_dev_list(fs_devices_mnt, mnt->mnt_fsname);
|
|
|
|
} else {
|
|
|
|
/* ignore entries in the mount table that are not
|
|
|
|
associated with a file*/
|
|
|
|
if((ret = is_existing_blk_or_reg_file(mnt->mnt_fsname)) < 0)
|
|
|
|
goto out_mntloop_err;
|
|
|
|
else if(!ret)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = is_same_loop_file(file, mnt->mnt_fsname);
|
2008-04-01 22:36:46 +08:00
|
|
|
}
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
if(ret < 0)
|
|
|
|
goto out_mntloop_err;
|
|
|
|
else if(ret)
|
|
|
|
break;
|
2008-04-01 22:36:46 +08:00
|
|
|
}
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
/* Did we find an entry in mnt table? */
|
2012-04-21 03:27:25 +08:00
|
|
|
if (mnt && size && where) {
|
2011-07-18 20:37:23 +08:00
|
|
|
strncpy(where, mnt->mnt_dir, size);
|
2012-04-21 03:27:25 +08:00
|
|
|
where[size-1] = 0;
|
|
|
|
}
|
2011-07-18 20:37:23 +08:00
|
|
|
if (fs_dev_ret)
|
|
|
|
*fs_dev_ret = fs_devices_mnt;
|
|
|
|
|
2010-09-09 10:57:02 +08:00
|
|
|
ret = (mnt != NULL);
|
|
|
|
|
|
|
|
out_mntloop_err:
|
2008-04-01 22:36:46 +08:00
|
|
|
endmntent (f);
|
2010-09-09 10:57:02 +08:00
|
|
|
|
2008-04-01 22:36:46 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-25 03:05:44 +08:00
|
|
|
struct pending_dir {
|
|
|
|
struct list_head list;
|
2013-01-24 03:31:24 +08:00
|
|
|
char name[PATH_MAX];
|
2008-03-25 03:05:44 +08:00
|
|
|
};
|
|
|
|
|
2014-10-15 08:50:38 +08:00
|
|
|
int btrfs_register_one_device(const char *fname)
|
2008-03-25 03:05:44 +08:00
|
|
|
{
|
|
|
|
struct btrfs_ioctl_vol_args args;
|
|
|
|
int fd;
|
|
|
|
int ret;
|
|
|
|
|
2014-10-15 08:46:54 +08:00
|
|
|
fd = open("/dev/btrfs-control", O_RDWR);
|
2009-01-08 03:57:11 +08:00
|
|
|
if (fd < 0) {
|
2009-01-17 01:05:01 +08:00
|
|
|
fprintf(stderr, "failed to open /dev/btrfs-control "
|
2013-03-14 11:17:19 +08:00
|
|
|
"skipping device registration: %s\n",
|
|
|
|
strerror(errno));
|
2014-10-15 08:50:38 +08:00
|
|
|
return -errno;
|
2009-01-08 03:57:11 +08:00
|
|
|
}
|
2015-06-12 20:36:51 +08:00
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
strncpy_null(args.name, fname);
|
2008-03-25 03:05:44 +08:00
|
|
|
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
|
2014-10-15 08:50:38 +08:00
|
|
|
if (ret < 0) {
|
2013-03-01 18:10:01 +08:00
|
|
|
fprintf(stderr, "ERROR: device scan failed '%s' - %s\n",
|
2016-01-12 18:20:18 +08:00
|
|
|
fname, strerror(errno));
|
|
|
|
ret = -errno;
|
2010-12-21 04:06:19 +08:00
|
|
|
}
|
2008-03-25 03:05:44 +08:00
|
|
|
close(fd);
|
2014-10-15 08:50:38 +08:00
|
|
|
return ret;
|
2008-03-25 03:05:44 +08:00
|
|
|
}
|
|
|
|
|
2014-10-31 12:11:19 +08:00
|
|
|
/*
|
|
|
|
* Register all devices in the fs_uuid list created in the user
|
|
|
|
* space. Ensure btrfs_scan_lblkid() is called before this func.
|
|
|
|
*/
|
|
|
|
int btrfs_register_all_devices(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct btrfs_fs_devices *fs_devices;
|
|
|
|
struct btrfs_device *device;
|
|
|
|
struct list_head *all_uuids;
|
|
|
|
|
|
|
|
all_uuids = btrfs_scanned_uuids();
|
|
|
|
|
|
|
|
list_for_each_entry(fs_devices, all_uuids, list) {
|
|
|
|
list_for_each_entry(device, &fs_devices->devices, dev_list) {
|
2015-11-06 23:53:55 +08:00
|
|
|
if (*device->name) {
|
2014-10-31 12:11:19 +08:00
|
|
|
err = btrfs_register_one_device(device->name);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
if (err > 0)
|
|
|
|
return -err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-18 22:45:17 +08:00
|
|
|
int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
|
|
|
|
int super_offset)
|
|
|
|
{
|
|
|
|
struct btrfs_super_block *disk_super;
|
|
|
|
char *buf;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
buf = malloc(BTRFS_SUPER_INFO_SIZE);
|
|
|
|
if (!buf) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = pread(fd, buf, BTRFS_SUPER_INFO_SIZE, super_offset);
|
|
|
|
if (ret != BTRFS_SUPER_INFO_SIZE)
|
|
|
|
goto brelse;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
disk_super = (struct btrfs_super_block *)buf;
|
2013-06-26 13:27:08 +08:00
|
|
|
if (btrfs_super_magic(disk_super) != BTRFS_MAGIC)
|
2008-04-18 22:45:17 +08:00
|
|
|
goto brelse;
|
|
|
|
|
2013-03-07 00:32:51 +08:00
|
|
|
if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid,
|
2008-04-18 22:45:17 +08:00
|
|
|
BTRFS_FSID_SIZE))
|
|
|
|
ret = 1;
|
|
|
|
brelse:
|
|
|
|
free(buf);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2008-04-23 02:06:31 +08:00
|
|
|
|
2015-02-28 02:37:24 +08:00
|
|
|
/*
|
|
|
|
* Note: this function uses a static per-thread buffer. Do not call this
|
|
|
|
* function more than 10 times within one argument list!
|
|
|
|
*/
|
|
|
|
const char *pretty_size_mode(u64 size, unsigned mode)
|
|
|
|
{
|
|
|
|
static __thread int ps_index = 0;
|
|
|
|
static __thread char ps_array[10][32];
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
ret = ps_array[ps_index];
|
|
|
|
ps_index++;
|
|
|
|
ps_index %= 10;
|
|
|
|
(void)pretty_size_snprintf(size, ret, 32, mode);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-14 05:34:35 +08:00
|
|
|
static const char* unit_suffix_binary[] =
|
2014-04-29 00:04:48 +08:00
|
|
|
{ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
|
2014-06-14 05:34:35 +08:00
|
|
|
static const char* unit_suffix_decimal[] =
|
2014-11-05 01:22:51 +08:00
|
|
|
{ "B", "kB", "MB", "GB", "TB", "PB", "EB"};
|
2014-04-29 00:04:48 +08:00
|
|
|
|
2014-08-31 02:27:00 +08:00
|
|
|
int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mode)
|
2008-04-23 02:06:31 +08:00
|
|
|
{
|
2014-04-29 00:04:48 +08:00
|
|
|
int num_divs;
|
2008-04-23 02:06:31 +08:00
|
|
|
float fraction;
|
2014-08-31 02:27:00 +08:00
|
|
|
u64 base = 0;
|
|
|
|
int mult = 0;
|
2014-06-14 05:34:35 +08:00
|
|
|
const char** suffix = NULL;
|
2014-04-29 00:04:48 +08:00
|
|
|
u64 last_size;
|
2013-07-10 04:24:43 +08:00
|
|
|
|
2014-04-29 00:04:48 +08:00
|
|
|
if (str_size == 0)
|
2013-10-07 15:21:47 +08:00
|
|
|
return 0;
|
2008-04-23 02:06:31 +08:00
|
|
|
|
2014-08-31 02:27:00 +08:00
|
|
|
if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) {
|
2014-04-29 00:04:48 +08:00
|
|
|
snprintf(str, str_size, "%llu", size);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-04-23 02:06:31 +08:00
|
|
|
|
2014-08-31 02:27:00 +08:00
|
|
|
if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_BINARY) {
|
2014-04-29 00:04:48 +08:00
|
|
|
base = 1024;
|
2014-08-31 02:27:00 +08:00
|
|
|
mult = 1024;
|
2014-04-29 00:04:48 +08:00
|
|
|
suffix = unit_suffix_binary;
|
2014-08-31 02:27:00 +08:00
|
|
|
} else if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_DECIMAL) {
|
2014-04-29 00:04:48 +08:00
|
|
|
base = 1000;
|
2014-08-31 02:27:00 +08:00
|
|
|
mult = 1000;
|
2014-04-29 00:04:48 +08:00
|
|
|
suffix = unit_suffix_decimal;
|
2012-10-02 04:11:27 +08:00
|
|
|
}
|
2014-04-29 00:04:48 +08:00
|
|
|
|
|
|
|
/* Unknown mode */
|
|
|
|
if (!base) {
|
2014-08-31 02:27:00 +08:00
|
|
|
fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d\n",
|
2014-04-29 00:04:48 +08:00
|
|
|
unit_mode);
|
|
|
|
assert(0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_divs = 0;
|
|
|
|
last_size = size;
|
2014-08-31 02:27:00 +08:00
|
|
|
switch (unit_mode & UNITS_MODE_MASK) {
|
|
|
|
case UNITS_TBYTES: base *= mult; num_divs++;
|
|
|
|
case UNITS_GBYTES: base *= mult; num_divs++;
|
|
|
|
case UNITS_MBYTES: base *= mult; num_divs++;
|
|
|
|
case UNITS_KBYTES: num_divs++;
|
|
|
|
break;
|
|
|
|
case UNITS_BYTES:
|
|
|
|
base = 1;
|
|
|
|
num_divs = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
while (size >= mult) {
|
|
|
|
last_size = size;
|
|
|
|
size /= mult;
|
|
|
|
num_divs++;
|
|
|
|
}
|
2016-03-09 10:10:55 +08:00
|
|
|
/*
|
|
|
|
* If the value is smaller than base, we didn't do any
|
|
|
|
* division, in that case, base should be 1, not original
|
|
|
|
* base, or the unit will be wrong
|
|
|
|
*/
|
|
|
|
if (num_divs == 0)
|
|
|
|
base = 1;
|
2014-04-29 00:04:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (num_divs >= ARRAY_SIZE(unit_suffix_binary)) {
|
|
|
|
str[0] = '\0';
|
|
|
|
printf("INTERNAL ERROR: unsupported unit suffix, index %d\n",
|
|
|
|
num_divs);
|
|
|
|
assert(0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fraction = (float)last_size / base;
|
|
|
|
|
|
|
|
return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]);
|
2008-04-23 02:06:31 +08:00
|
|
|
}
|
|
|
|
|
2013-01-26 03:27:47 +08:00
|
|
|
/*
|
|
|
|
* __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;
|
|
|
|
}
|
|
|
|
|
2010-12-06 01:46:44 +08:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2013-01-30 16:32:43 +08:00
|
|
|
static int check_label(const char *input)
|
2010-12-06 01:46:44 +08:00
|
|
|
{
|
|
|
|
int len = strlen(input);
|
|
|
|
|
2013-01-30 16:32:43 +08:00
|
|
|
if (len > BTRFS_LABEL_SIZE - 1) {
|
|
|
|
fprintf(stderr, "ERROR: Label %s is too long (max %d)\n",
|
|
|
|
input, BTRFS_LABEL_SIZE - 1);
|
2010-12-06 01:46:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-06-16 03:55:25 +08:00
|
|
|
|
2013-01-30 16:32:47 +08:00
|
|
|
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.
|
|
|
|
*/
|
2013-10-29 02:28:43 +08:00
|
|
|
root = open_ctree(dev, 0, OPEN_CTREE_WRITES);
|
2013-01-30 16:32:47 +08:00
|
|
|
if (!root) /* errors are printed by open_ctree() */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2013-03-07 00:32:51 +08:00
|
|
|
snprintf(root->fs_info->super_copy->label, BTRFS_LABEL_SIZE, "%s",
|
2013-01-30 16:32:47 +08:00
|
|
|
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) {
|
2014-02-05 20:57:51 +08:00
|
|
|
fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
|
2013-01-30 16:32:47 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-12 07:12:58 +08:00
|
|
|
close(fd);
|
2013-01-30 16:32:47 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-15 16:46:22 +08:00
|
|
|
int get_label_unmounted(const char *dev, char *label)
|
2013-01-30 16:32:47 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the super_block at the default location
|
|
|
|
* and as read-only.
|
|
|
|
*/
|
|
|
|
root = open_ctree(dev, 0, 0);
|
|
|
|
if(!root)
|
|
|
|
return -1;
|
|
|
|
|
2013-11-12 21:41:42 +08:00
|
|
|
memcpy(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE);
|
2013-01-30 16:32:47 +08:00
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2013-07-15 13:30:54 +08:00
|
|
|
int get_label_mounted(const char *mount_path, char *labelp)
|
2013-01-30 16:32:47 +08:00
|
|
|
{
|
|
|
|
char label[BTRFS_LABEL_SIZE];
|
|
|
|
int fd;
|
2015-09-15 16:46:22 +08:00
|
|
|
int ret;
|
2013-01-30 16:32:47 +08:00
|
|
|
|
|
|
|
fd = open(mount_path, O_RDONLY | O_NOATIME);
|
|
|
|
if (fd < 0) {
|
2014-02-05 20:57:51 +08:00
|
|
|
fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
|
2013-01-30 16:32:47 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(label, '\0', sizeof(label));
|
2015-09-15 16:46:22 +08:00
|
|
|
ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno != ENOTTY)
|
|
|
|
fprintf(stderr, "ERROR: unable to get label %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
ret = -errno;
|
2013-01-30 16:32:47 +08:00
|
|
|
close(fd);
|
2015-09-15 16:46:22 +08:00
|
|
|
return ret;
|
2013-01-30 16:32:47 +08:00
|
|
|
}
|
|
|
|
|
2013-07-15 13:30:54 +08:00
|
|
|
strncpy(labelp, label, sizeof(label));
|
2013-03-12 07:12:58 +08:00
|
|
|
close(fd);
|
2013-01-30 16:32:47 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-12 21:41:42 +08:00
|
|
|
int get_label(const char *btrfs_dev, char *label)
|
2013-01-30 16:32:47 +08:00
|
|
|
{
|
2013-07-15 13:30:54 +08:00
|
|
|
int ret;
|
|
|
|
|
2014-07-31 11:23:44 +08:00
|
|
|
ret = is_existing_blk_or_reg_file(btrfs_dev);
|
|
|
|
if (!ret)
|
2013-07-15 13:30:54 +08:00
|
|
|
ret = get_label_mounted(btrfs_dev, label);
|
2014-07-31 11:23:44 +08:00
|
|
|
else if (ret > 0)
|
|
|
|
ret = get_label_unmounted(btrfs_dev, label);
|
2013-11-12 21:41:42 +08:00
|
|
|
|
2013-07-15 13:30:54 +08:00
|
|
|
return ret;
|
2013-01-30 16:32:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int set_label(const char *btrfs_dev, const char *label)
|
|
|
|
{
|
2014-07-31 11:23:44 +08:00
|
|
|
int ret;
|
|
|
|
|
2013-01-30 16:32:47 +08:00
|
|
|
if (check_label(label))
|
|
|
|
return -1;
|
|
|
|
|
2014-07-31 11:23:44 +08:00
|
|
|
ret = is_existing_blk_or_reg_file(btrfs_dev);
|
|
|
|
if (!ret)
|
|
|
|
ret = set_label_mounted(btrfs_dev, label);
|
|
|
|
else if (ret > 0)
|
|
|
|
ret = set_label_unmounted(btrfs_dev, label);
|
|
|
|
|
|
|
|
return ret;
|
2013-01-30 16:32:47 +08:00
|
|
|
}
|
|
|
|
|
2015-02-27 16:26:36 +08:00
|
|
|
/*
|
|
|
|
* Unsafe subvolume check.
|
|
|
|
*
|
|
|
|
* This only checks ino == BTRFS_FIRST_FREE_OBJECTID, even it is not in a
|
|
|
|
* btrfs mount point.
|
|
|
|
* Must use together with other reliable method like btrfs ioctl.
|
|
|
|
*/
|
|
|
|
static int __is_subvol(const char *path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = lstat(path, &st);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return st.st_ino == BTRFS_FIRST_FREE_OBJECTID;
|
|
|
|
}
|
|
|
|
|
2014-06-13 10:55:57 +08:00
|
|
|
/*
|
|
|
|
* A not-so-good version fls64. No fascinating optimization since
|
|
|
|
* no one except parse_size use it
|
|
|
|
*/
|
|
|
|
static int fls64(u64 x)
|
2012-10-30 01:53:17 +08:00
|
|
|
{
|
2012-10-30 01:53:18 +08:00
|
|
|
int i;
|
2014-06-13 10:55:57 +08:00
|
|
|
|
|
|
|
for (i = 0; i <64; i++)
|
2014-07-27 00:49:55 +08:00
|
|
|
if (x << i & (1ULL << 63))
|
2014-06-13 10:55:57 +08:00
|
|
|
return 64 - i;
|
|
|
|
return 64 - i;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 parse_size(char *s)
|
|
|
|
{
|
2012-10-30 01:53:17 +08:00
|
|
|
char c;
|
2014-06-13 10:55:57 +08:00
|
|
|
char *endptr;
|
2012-10-30 01:53:17 +08:00
|
|
|
u64 mult = 1;
|
2014-06-13 10:55:57 +08:00
|
|
|
u64 ret;
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
fprintf(stderr, "ERROR: Size value is empty\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (s[0] == '-') {
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Size value '%s' is less equal than 0\n", s);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ret = strtoull(s, &endptr, 10);
|
|
|
|
if (endptr == s) {
|
|
|
|
fprintf(stderr, "ERROR: Size value '%s' is invalid\n", s);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (endptr[0] && endptr[1]) {
|
|
|
|
fprintf(stderr, "ERROR: Illegal suffix contains character '%c' in wrong position\n",
|
|
|
|
endptr[1]);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* strtoll returns LLONG_MAX when overflow, if this happens,
|
|
|
|
* need to call strtoull to get the real size
|
|
|
|
*/
|
|
|
|
if (errno == ERANGE && ret == ULLONG_MAX) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Size value '%s' is too large for u64\n", s);
|
|
|
|
exit(1);
|
2012-10-30 01:53:18 +08:00
|
|
|
}
|
2014-06-13 10:55:57 +08:00
|
|
|
if (endptr[0]) {
|
|
|
|
c = tolower(endptr[0]);
|
2012-10-30 01:53:17 +08:00
|
|
|
switch (c) {
|
2012-10-30 01:53:19 +08:00
|
|
|
case 'e':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:19 +08:00
|
|
|
case 'p':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:19 +08:00
|
|
|
case 't':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:17 +08:00
|
|
|
case 'g':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:17 +08:00
|
|
|
case 'm':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:17 +08:00
|
|
|
case 'k':
|
|
|
|
mult *= 1024;
|
2013-11-07 07:15:54 +08:00
|
|
|
/* fallthrough */
|
2012-10-30 01:53:17 +08:00
|
|
|
case 'b':
|
|
|
|
break;
|
|
|
|
default:
|
2014-06-13 10:55:57 +08:00
|
|
|
fprintf(stderr, "ERROR: Unknown size descriptor '%c'\n",
|
|
|
|
c);
|
2012-10-30 01:53:17 +08:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2014-06-13 10:55:57 +08:00
|
|
|
/* Check whether ret * mult overflow */
|
|
|
|
if (fls64(ret) + fls64(mult) - 1 > 64) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Size value '%s' is too large for u64\n", s);
|
|
|
|
exit(1);
|
2012-10-30 01:53:18 +08:00
|
|
|
}
|
2014-06-13 10:55:57 +08:00
|
|
|
ret *= mult;
|
|
|
|
return ret;
|
2012-10-30 01:53:17 +08:00
|
|
|
}
|
|
|
|
|
2015-02-27 16:26:35 +08:00
|
|
|
u64 parse_qgroupid(const char *p)
|
|
|
|
{
|
|
|
|
char *s = strchr(p, '/');
|
|
|
|
const char *ptr_src_end = p + strlen(p);
|
|
|
|
char *ptr_parse_end = NULL;
|
|
|
|
u64 level;
|
|
|
|
u64 id;
|
2015-02-27 16:26:36 +08:00
|
|
|
int fd;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (p[0] == '/')
|
|
|
|
goto path;
|
2015-02-27 16:26:35 +08:00
|
|
|
|
2015-02-27 16:26:36 +08:00
|
|
|
/* Numeric format like '0/257' is the primary case */
|
2015-02-27 16:26:35 +08:00
|
|
|
if (!s) {
|
|
|
|
id = strtoull(p, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != ptr_src_end)
|
2015-02-27 16:26:36 +08:00
|
|
|
goto path;
|
2015-02-27 16:26:35 +08:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
level = strtoull(p, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != s)
|
2015-02-27 16:26:36 +08:00
|
|
|
goto path;
|
2015-02-27 16:26:35 +08:00
|
|
|
|
|
|
|
id = strtoull(s + 1, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != ptr_src_end)
|
2015-02-27 16:26:36 +08:00
|
|
|
goto path;
|
2015-02-27 16:26:35 +08:00
|
|
|
|
|
|
|
return (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
|
2015-02-27 16:26:36 +08:00
|
|
|
|
|
|
|
path:
|
|
|
|
/* Path format like subv at 'my_subvol' is the fallback case */
|
|
|
|
ret = __is_subvol(p);
|
|
|
|
if (ret < 0 || !ret)
|
|
|
|
goto err;
|
|
|
|
fd = open(p, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
goto err;
|
|
|
|
ret = lookup_ino_rootid(fd, &id);
|
|
|
|
close(fd);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
return id;
|
|
|
|
|
2015-02-27 16:26:35 +08:00
|
|
|
err:
|
2015-02-27 16:26:36 +08:00
|
|
|
fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p);
|
2015-02-27 16:26:35 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2014-04-16 03:47:27 +08:00
|
|
|
int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags)
|
2013-01-28 13:22:30 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct stat st;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
ret = stat(fname, &st);
|
|
|
|
if (ret < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
2013-07-15 19:36:50 +08:00
|
|
|
*dirstream = opendir(fname);
|
|
|
|
if (!*dirstream)
|
2013-12-12 15:46:18 +08:00
|
|
|
return -1;
|
2013-07-15 19:36:50 +08:00
|
|
|
fd = dirfd(*dirstream);
|
2013-12-12 15:46:18 +08:00
|
|
|
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
|
2014-04-16 03:47:27 +08:00
|
|
|
fd = open(fname, open_flags);
|
2013-12-12 15:46:18 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* we set this on purpose, in case the caller output
|
|
|
|
* strerror(errno) as success
|
|
|
|
*/
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
2013-01-28 13:22:30 +08:00
|
|
|
}
|
|
|
|
if (fd < 0) {
|
2013-12-12 15:46:18 +08:00
|
|
|
fd = -1;
|
2015-06-09 21:24:51 +08:00
|
|
|
if (*dirstream) {
|
2013-07-15 19:36:50 +08:00
|
|
|
closedir(*dirstream);
|
2015-06-09 21:24:51 +08:00
|
|
|
*dirstream = NULL;
|
|
|
|
}
|
2013-01-28 13:22:30 +08:00
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
2012-05-15 18:05:44 +08:00
|
|
|
|
2014-04-16 03:47:27 +08:00
|
|
|
int open_file_or_dir(const char *fname, DIR **dirstream)
|
|
|
|
{
|
|
|
|
return open_file_or_dir3(fname, dirstream, O_RDWR);
|
|
|
|
}
|
|
|
|
|
2013-07-15 19:36:50 +08:00
|
|
|
void close_file_or_dir(int fd, DIR *dirstream)
|
|
|
|
{
|
|
|
|
if (dirstream)
|
|
|
|
closedir(dirstream);
|
|
|
|
else if (fd >= 0)
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2014-02-14 03:19:44 +08:00
|
|
|
int get_device_info(int fd, u64 devid,
|
2013-08-10 04:20:47 +08:00
|
|
|
struct btrfs_ioctl_dev_info_args *di_args)
|
2012-05-15 18:05:44 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
di_args->devid = devid;
|
|
|
|
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
|
2016-01-12 20:35:50 +08:00
|
|
|
return ret < 0 ? -errno : 0;
|
2012-05-15 18:05:44 +08:00
|
|
|
}
|
|
|
|
|
2014-11-13 09:19:29 +08:00
|
|
|
static u64 find_max_device_id(struct btrfs_ioctl_search_args *search_args,
|
|
|
|
int nr_items)
|
|
|
|
{
|
|
|
|
struct btrfs_dev_item *dev_item;
|
|
|
|
char *buf = search_args->buf;
|
|
|
|
|
|
|
|
buf += (nr_items - 1) * (sizeof(struct btrfs_ioctl_search_header)
|
|
|
|
+ sizeof(struct btrfs_dev_item));
|
|
|
|
buf += sizeof(struct btrfs_ioctl_search_header);
|
|
|
|
|
|
|
|
dev_item = (struct btrfs_dev_item *)buf;
|
|
|
|
|
|
|
|
return btrfs_stack_device_id(dev_item);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int search_chunk_tree_for_fs_info(int fd,
|
|
|
|
struct btrfs_ioctl_fs_info_args *fi_args)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int max_items;
|
|
|
|
u64 start_devid = 1;
|
|
|
|
struct btrfs_ioctl_search_args search_args;
|
|
|
|
struct btrfs_ioctl_search_key *search_key = &search_args.key;
|
|
|
|
|
|
|
|
fi_args->num_devices = 0;
|
|
|
|
|
|
|
|
max_items = BTRFS_SEARCH_ARGS_BUFSIZE
|
|
|
|
/ (sizeof(struct btrfs_ioctl_search_header)
|
|
|
|
+ sizeof(struct btrfs_dev_item));
|
|
|
|
|
|
|
|
search_key->tree_id = BTRFS_CHUNK_TREE_OBJECTID;
|
|
|
|
search_key->min_objectid = BTRFS_DEV_ITEMS_OBJECTID;
|
|
|
|
search_key->max_objectid = BTRFS_DEV_ITEMS_OBJECTID;
|
|
|
|
search_key->min_type = BTRFS_DEV_ITEM_KEY;
|
|
|
|
search_key->max_type = BTRFS_DEV_ITEM_KEY;
|
|
|
|
search_key->min_transid = 0;
|
|
|
|
search_key->max_transid = (u64)-1;
|
|
|
|
search_key->nr_items = max_items;
|
|
|
|
search_key->max_offset = (u64)-1;
|
|
|
|
|
|
|
|
again:
|
|
|
|
search_key->min_offset = start_devid;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_args);
|
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
fi_args->num_devices += (u64)search_key->nr_items;
|
|
|
|
|
|
|
|
if (search_key->nr_items == max_items) {
|
|
|
|
start_devid = find_max_device_id(&search_args,
|
|
|
|
search_key->nr_items) + 1;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the lastest max_id to stay consistent with the num_devices */
|
|
|
|
if (search_key->nr_items == 0)
|
|
|
|
/*
|
|
|
|
* last tree_search returns an empty buf, use the devid of
|
|
|
|
* the last dev_item of the previous tree_search
|
|
|
|
*/
|
|
|
|
fi_args->max_id = start_devid - 1;
|
|
|
|
else
|
|
|
|
fi_args->max_id = find_max_device_id(&search_args,
|
|
|
|
search_key->nr_items);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-12 12:17:40 +08:00
|
|
|
/*
|
|
|
|
* 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,
|
2012-05-15 18:05:44 +08:00
|
|
|
struct btrfs_ioctl_dev_info_args **di_ret)
|
|
|
|
{
|
2013-03-12 12:17:40 +08:00
|
|
|
int fd = -1;
|
2012-05-15 18:05:44 +08:00
|
|
|
int ret = 0;
|
|
|
|
int ndevs = 0;
|
2014-02-24 19:43:37 +08:00
|
|
|
int i = 0;
|
2015-01-06 17:30:44 +08:00
|
|
|
int replacing = 0;
|
2012-05-15 18:05:44 +08:00
|
|
|
struct btrfs_fs_devices *fs_devices_mnt = NULL;
|
|
|
|
struct btrfs_ioctl_dev_info_args *di_args;
|
2015-01-06 17:30:44 +08:00
|
|
|
struct btrfs_ioctl_dev_info_args tmp;
|
2015-06-12 19:18:44 +08:00
|
|
|
char mp[PATH_MAX];
|
2013-07-15 19:36:50 +08:00
|
|
|
DIR *dirstream = NULL;
|
2012-05-15 18:05:44 +08:00
|
|
|
|
|
|
|
memset(fi_args, 0, sizeof(*fi_args));
|
|
|
|
|
2015-08-28 22:11:30 +08:00
|
|
|
if (is_block_device(path) == 1) {
|
2014-02-24 19:43:38 +08:00
|
|
|
struct btrfs_super_block *disk_super;
|
|
|
|
char buf[BTRFS_SUPER_INFO_SIZE];
|
|
|
|
u64 devid;
|
|
|
|
|
2013-03-12 12:17:40 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
2012-05-15 18:05:44 +08:00
|
|
|
ret = check_mounted_where(fd, path, mp, sizeof(mp),
|
|
|
|
&fs_devices_mnt);
|
2013-03-12 12:17:40 +08:00
|
|
|
if (!ret) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-15 18:05:44 +08:00
|
|
|
if (ret < 0)
|
2013-03-12 12:17:40 +08:00
|
|
|
goto out;
|
|
|
|
path = mp;
|
|
|
|
/* Only fill in this one device */
|
2012-05-15 18:05:44 +08:00
|
|
|
fi_args->num_devices = 1;
|
2014-02-24 19:43:38 +08:00
|
|
|
|
|
|
|
disk_super = (struct btrfs_super_block *)buf;
|
2014-07-03 17:36:36 +08:00
|
|
|
ret = btrfs_read_dev_super(fd, disk_super,
|
|
|
|
BTRFS_SUPER_INFO_OFFSET, 0);
|
2014-02-24 19:43:38 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
|
|
|
|
|
|
fi_args->max_id = devid;
|
|
|
|
i = devid;
|
|
|
|
|
2012-05-15 18:05:44 +08:00
|
|
|
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
|
|
|
|
close(fd);
|
2013-03-12 12:17:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* at this point path must not be for a block device */
|
2013-07-15 19:36:50 +08:00
|
|
|
fd = open_file_or_dir(path, &dirstream);
|
2013-03-12 12:17:40 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-11-13 09:19:29 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The fs_args->num_devices does not include seed devices
|
|
|
|
*/
|
|
|
|
ret = search_chunk_tree_for_fs_info(fd, fi_args);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2015-01-06 17:30:44 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* search_chunk_tree_for_fs_info() will lacks the devid 0
|
|
|
|
* so manual probe for it here.
|
|
|
|
*/
|
|
|
|
ret = get_device_info(fd, 0, &tmp);
|
|
|
|
if (!ret) {
|
|
|
|
fi_args->num_devices++;
|
|
|
|
ndevs++;
|
|
|
|
replacing = 1;
|
|
|
|
if (i == 0)
|
|
|
|
i++;
|
|
|
|
}
|
2012-05-15 18:05:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!fi_args->num_devices)
|
2013-03-12 12:17:40 +08:00
|
|
|
goto out;
|
2012-05-15 18:05:44 +08:00
|
|
|
|
2014-10-30 12:43:03 +08:00
|
|
|
di_args = *di_ret = malloc((fi_args->num_devices) * sizeof(*di_args));
|
2013-03-12 12:17:40 +08:00
|
|
|
if (!di_args) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-15 18:05:44 +08:00
|
|
|
|
2015-01-06 17:30:44 +08:00
|
|
|
if (replacing)
|
|
|
|
memcpy(di_args, &tmp, sizeof(tmp));
|
2012-05-15 18:05:44 +08:00
|
|
|
for (; i <= fi_args->max_id; ++i) {
|
|
|
|
ret = get_device_info(fd, i, &di_args[ndevs]);
|
|
|
|
if (ret == -ENODEV)
|
|
|
|
continue;
|
|
|
|
if (ret)
|
2013-03-12 12:17:40 +08:00
|
|
|
goto out;
|
2012-05-15 18:05:44 +08:00
|
|
|
ndevs++;
|
|
|
|
}
|
|
|
|
|
2014-02-24 19:43:39 +08:00
|
|
|
/*
|
|
|
|
* only when the only dev we wanted to find is not there then
|
|
|
|
* let any error be returned
|
|
|
|
*/
|
|
|
|
if (fi_args->num_devices != 1) {
|
|
|
|
BUG_ON(ndevs == 0);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
2013-03-12 12:17:40 +08:00
|
|
|
out:
|
2013-07-15 19:36:50 +08:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-03-12 12:17:40 +08:00
|
|
|
return ret;
|
2012-05-15 18:05:44 +08:00
|
|
|
}
|
2013-02-14 15:53:04 +08:00
|
|
|
|
|
|
|
#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.
|
|
|
|
*/
|
2013-08-10 04:20:47 +08:00
|
|
|
static int is_swap_device(const char *file)
|
2013-02-14 15:53:04 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2013-04-15 14:38:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2014-05-16 16:20:56 +08:00
|
|
|
static int group_profile_devs_min(u64 flag)
|
|
|
|
{
|
|
|
|
switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
|
|
|
|
case 0: /* single */
|
|
|
|
case BTRFS_BLOCK_GROUP_DUP:
|
|
|
|
return 1;
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID0:
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID1:
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID5:
|
|
|
|
return 2;
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID6:
|
|
|
|
return 3;
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID10:
|
|
|
|
return 4;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 20:11:25 +08:00
|
|
|
int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
|
btrfs-progs: mkfs: allow --data DUP for single device
Current code don't support DUP profile on single device, except it
is in mixed mode, because of following reasons:
1: Some SSD do deduplication internally, so the duplication on
the filesystem side has no effect.
2: On a physical device, if the entire disk broken, --data DUP does not
help.
3: Half performance compared to single profile.
4: We have a workaround: create multi-partition on a single device,
and btffs will treat them as multi device.
Instead of refusing --data DUP, we give the user a choice and print
a wrning.
Test:
1: Tested by xfstests
Run with modified xfstests, I add test items of -d dup in single
device into btrfs/* and common/rc, run tests of btrfs/*,
with all mount option, no regression diffed with v4.3.
2: Tested by btrfs-progs
Checked following commands in "-m dup -d dup" fs with memleck
checking, all passed:
mkfs.btrfs -f --data dup --metadata dup /dev/sda6
btrfs filesystem show /dev/sda6
btrfs filesystem label /dev/sda6 btrfs_label_test
btrfs filesystem label /dev/sda6
btrfs device scan --all-devices
btrfs device scan /dev/sda6
btrfs device scan /dev/sda6
btrfs device ready /dev/sda6
btrfs check /dev/sda6
btrfs check -s 1 /dev/sda6
btrfs check --repair /dev/sda6
btrfs check --init-csum-tree /dev/sda6
btrfs check --init-extent-tree /dev/sda6
btrfs check --check-data-csum /dev/sda6
btrfs check --qgroup-report /dev/sda6
btrfs rescue super-recover -y /dev/sda6
btrfs rescue zero-log /dev/sda6
btrfs restore -l /dev/sda6
btrfs restore /dev/sda6 /
btrfs restore -s /dev/sda6 /
btrfs restore -x /dev/sda6 /
btrfs restore -m /dev/sda6 /
btrfs restore -S /dev/sda6 /
btrfs restore -v /dev/sda6 /
btrfs restore -i /dev/sda6 /
btrfs restore -o /dev/sda6 /
btrfs restore -u0 /dev/sda6 /
btrfs restore -u1 /dev/sda6 /
btrfs restore -D /dev/sda6 /
btrfs property list /dev/sda6
btrfs property get /dev/sda6 label
btrfs property set /dev/sda6 label test
btrfs property set /dev/sda6 label btrfs_label_test
btrfs help
btrfs help --full
btrfs version
btrfsck /dev/sda6
btrfs-find-root /dev/sda6
btrfs-find-root -a /dev/sda6
btrfs-map-logical -l1 /dev/sda6
btrfs-map-logical -l1 -c1 /dev/sda6
btrfs-map-logical -l1 -o /tmp/btrfs-map-logic-out /dev/sda6
btrfs-map-logical -l1 -b1 /dev/sda6
btrfs-select-super -s 0 /dev/sda6
btrfs-select-super -s 1 /dev/sda6
btrfstune -S 1 /dev/sda6
btrfstune -f -S 0 /dev/sda6
btrfstune -r /dev/sda6
btrfstune -x /dev/sda6
btrfstune -n /dev/sda6
btrfstune -f -U 00000000-0000-0000-0000-000000000000 /dev/sda6
btrfstune -f -u /dev/sda6
btrfs-calc-size /dev/sda6
btrfs-calc-size -v /dev/sda6
btrfs-calc-size -b /dev/sda6
btrfs-debug-tree /dev/sda6
btrfs-debug-tree -e /dev/sda6
btrfs-debug-tree -d /dev/sda6
btrfs-debug-tree -r /dev/sda6
btrfs-debug-tree -R /dev/sda6
btrfs-debug-tree -u /dev/sda6
btrfs-debug-tree -b 0 /dev/sda6
btrfs-debug-tree -t 0 /dev/sda6
btrfs-debug-tree -t 2 /dev/sda6
btrfs-show-super /dev/sda6
btrfs-show-super -i 0 /dev/sda6
btrfs-show-super -i 1 /dev/sda6
btrfs-show-super -i 2 /dev/sda6
btrfs-show-super -a /dev/sda6
btrfs-show-super -f /dev/sda6
btrfs-show-super -F /dev/sda6
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume create /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default 258 /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default /mnt/btrfs-progs-tests
btrfs subvolume snapshot /mnt/btrfs-progs-tests/mysubvol /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume show /mnt/btrfs-progs-tests
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs filesystem df /mnt/btrfs-progs-tests
btrfs filesystem show /mnt/btrfs-progs-tests
btrfs filesystem sync /mnt/btrfs-progs-tests
btrfs filesystem label /mnt/btrfs-progs-tests btrfs_label_test
btrfs filesystem label /mnt/btrfs-progs-tests
btrfs filesystem usage /mnt/btrfs-progs-tests
btrfs filesystem defragment -s 1024 -l 2048 /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_0
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_1
btrfs filesystem defragment -f /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_2
btrfs filesystem defragment -czlib /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_3
btrfs filesystem defragment -clzo /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_4
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment -r /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment /mnt/btrfs-progs-tests
btrfs filesystem resize 1:-10M /mnt/btrfs-progs-tests
btrfs filesystem resize 1:max /mnt/btrfs-progs-tests
btrfs balance start /mnt/btrfs-progs-tests
btrfs balance start -v /mnt/btrfs-progs-tests
btrfs balance start -f /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance pause /mnt/btrfs-progs-tests
btrfs balance status /mnt/btrfs-progs-tests
btrfs balance resume /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance cancel /mnt/btrfs-progs-tests
btrfs balance start -dprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -dconvert=single /mnt/btrfs-progs-tests
btrfs balance start -ddevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -mprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -mconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -mdevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -sprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -sconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -sdevid=1 /mnt/btrfs-progs-tests
btrfs device add -f /dev/sda10 /mnt/btrfs-progs-tests
btrfs device del /dev/sda10 /mnt/btrfs-progs-tests
btrfs device stats /dev/sda6
btrfs device stats -z /dev/sda6
btrfs device stats /mnt/btrfs-progs-tests
btrfs device stats -z /mnt/btrfs-progs-tests
btrfs device usage /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start -B /mnt/btrfs-progs-tests
btrfs scrub start -B -d /mnt/btrfs-progs-tests
btrfs scrub start -B -r /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status -d /mnt/btrfs-progs-tests
btrfs scrub status -R /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status -d /dev/sda6
btrfs scrub status -R /dev/sda6
btrfs scrub status /dev/sda6
btrfs subvolume snapshot -r /mnt/btrfs-progs-tests /mnt/btrfs-progs-tests/snap1
btrfs send -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send -e -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send --no-data -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs quota enable /mnt/btrfs-progs-tests
btrfs quota rescan /mnt/btrfs-progs-tests
btrfs quota rescan -s /mnt/btrfs-progs-tests
btrfs quota rescan -w /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs quota enable /mnt/btrfs-progs-tests
btrfs qgroup create 1/5 /mnt/btrfs-progs-tests
btrfs qgroup create 2/5 /mnt/btrfs-progs-tests
btrfs qgroup assign 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup limit 1G 1/5 /mnt/btrfs-progs-tests
btrfs qgroup show /mnt/btrfs-progs-tests
btrfs qgroup show -p -c -r -e -F -f /mnt/btrfs-progs-tests
btrfs qgroup remove 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 1/5 /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda6 /dev/sda10 /mnt/btrfs-progs-tests
btrfs replace status /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda10 /dev/sda6 /mnt/btrfs-progs-tests
btrfs-convert /dev/sda6
btrfs-convert -r /dev/sda6
btrfs-convert -d /dev/sda6
btrfs-convert -i /dev/sda6
btrfs-convert -n /dev/sda6
btrfs-convert -N 4096 /dev/sda6
btrfs-convert -l test /dev/sda6
btrfs-convert -L /dev/sda6
btrfs-convert --no-progress /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 9 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 1 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 32 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 0 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 1 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 32 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -o /tmp/btrfs_image.img /dev/sda6
3: Manual check relation source by:
grep DUP *.c
Confirmed that all source are modified.
4: Use this raid type manually, do some operations in fs,
no error found in command and dmesg.
5: Combination of dup conversion with fsck
Confirmed OK with relative kernel patch titled:
[PATCH] btrfs: Support convert to -d dup for btrfs-convert
export TEST_DEV='/dev/vdc'
export TEST_DIR='/var/ltf/tester/mnt'
do_dup_test()
{
local m_from="$1"
local d_from="$2"
local m_to="$3"
local d_to="$4"
echo "Convert from -m $m_from -d $d_from to -m $m_to -d $d_to"
umount "$TEST_DIR" &>/dev/null
./mkfs.btrfs -f -m "$m_from" -d "$d_from" "$TEST_DEV" >/dev/null || return 1
mount "$TEST_DEV" "$TEST_DIR" || return 1
cp -a /sbin/* "$TEST_DIR"
[[ "$m_from" != "$m_to" ]] && {
./btrfs balance start -f -mconvert="$m_to" "$TEST_DIR" || return 1
}
[[ "$d_from" != "$d_to" ]] && {
local opt=()
[[ "$d_to" == single ]] && opt+=("-f")
./btrfs balance start "${opt[@]}" -dconvert="$d_to" "$TEST_DIR" || return 1
}
umount "$TEST_DIR" || return 1
./btrfsck "$TEST_DEV" || return 1
echo
return 0
}
test_all()
{
for m_from in single dup; do
for d_from in single dup; do
for m_to in single dup; do
for d_to in single dup; do
do_dup_test "$m_from" "$d_from" "$m_to" "$d_to" || return 1
done
done
done
done
}
test_all
Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Tested-by: Austin S. Hemmelgarn <ahferroin7@gmail.com>
[ minor updates in the changelog ]
Signed-off-by: David Sterba <dsterba@suse.com>
2015-11-19 17:36:24 +08:00
|
|
|
u64 dev_cnt, int mixed, int ssd)
|
2013-08-07 20:11:25 +08:00
|
|
|
{
|
|
|
|
u64 allowed = 0;
|
|
|
|
|
|
|
|
switch (dev_cnt) {
|
|
|
|
default:
|
|
|
|
case 4:
|
|
|
|
allowed |= BTRFS_BLOCK_GROUP_RAID10;
|
|
|
|
case 3:
|
|
|
|
allowed |= BTRFS_BLOCK_GROUP_RAID6;
|
|
|
|
case 2:
|
|
|
|
allowed |= BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
|
|
|
|
BTRFS_BLOCK_GROUP_RAID5;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
allowed |= BTRFS_BLOCK_GROUP_DUP;
|
|
|
|
}
|
|
|
|
|
2014-05-16 16:20:56 +08:00
|
|
|
if (dev_cnt > 1 &&
|
|
|
|
((metadata_profile | data_profile) & BTRFS_BLOCK_GROUP_DUP)) {
|
2015-06-11 06:51:15 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: DUP is not allowed when FS has multiple devices\n");
|
2014-05-16 16:20:56 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2013-08-07 20:11:25 +08:00
|
|
|
if (metadata_profile & ~allowed) {
|
2015-06-11 06:51:15 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: unable to create FS with metadata profile %s "
|
2014-05-16 16:20:56 +08:00
|
|
|
"(have %llu devices but %d devices are required)\n",
|
|
|
|
btrfs_group_profile_str(metadata_profile), dev_cnt,
|
|
|
|
group_profile_devs_min(metadata_profile));
|
2013-08-07 20:11:25 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (data_profile & ~allowed) {
|
2015-06-11 06:51:15 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: unable to create FS with data profile %s "
|
2014-05-16 16:20:56 +08:00
|
|
|
"(have %llu devices but %d devices are required)\n",
|
|
|
|
btrfs_group_profile_str(data_profile), dev_cnt,
|
|
|
|
group_profile_devs_min(data_profile));
|
2013-08-07 20:11:25 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
btrfs-progs: mkfs: allow --data DUP for single device
Current code don't support DUP profile on single device, except it
is in mixed mode, because of following reasons:
1: Some SSD do deduplication internally, so the duplication on
the filesystem side has no effect.
2: On a physical device, if the entire disk broken, --data DUP does not
help.
3: Half performance compared to single profile.
4: We have a workaround: create multi-partition on a single device,
and btffs will treat them as multi device.
Instead of refusing --data DUP, we give the user a choice and print
a wrning.
Test:
1: Tested by xfstests
Run with modified xfstests, I add test items of -d dup in single
device into btrfs/* and common/rc, run tests of btrfs/*,
with all mount option, no regression diffed with v4.3.
2: Tested by btrfs-progs
Checked following commands in "-m dup -d dup" fs with memleck
checking, all passed:
mkfs.btrfs -f --data dup --metadata dup /dev/sda6
btrfs filesystem show /dev/sda6
btrfs filesystem label /dev/sda6 btrfs_label_test
btrfs filesystem label /dev/sda6
btrfs device scan --all-devices
btrfs device scan /dev/sda6
btrfs device scan /dev/sda6
btrfs device ready /dev/sda6
btrfs check /dev/sda6
btrfs check -s 1 /dev/sda6
btrfs check --repair /dev/sda6
btrfs check --init-csum-tree /dev/sda6
btrfs check --init-extent-tree /dev/sda6
btrfs check --check-data-csum /dev/sda6
btrfs check --qgroup-report /dev/sda6
btrfs rescue super-recover -y /dev/sda6
btrfs rescue zero-log /dev/sda6
btrfs restore -l /dev/sda6
btrfs restore /dev/sda6 /
btrfs restore -s /dev/sda6 /
btrfs restore -x /dev/sda6 /
btrfs restore -m /dev/sda6 /
btrfs restore -S /dev/sda6 /
btrfs restore -v /dev/sda6 /
btrfs restore -i /dev/sda6 /
btrfs restore -o /dev/sda6 /
btrfs restore -u0 /dev/sda6 /
btrfs restore -u1 /dev/sda6 /
btrfs restore -D /dev/sda6 /
btrfs property list /dev/sda6
btrfs property get /dev/sda6 label
btrfs property set /dev/sda6 label test
btrfs property set /dev/sda6 label btrfs_label_test
btrfs help
btrfs help --full
btrfs version
btrfsck /dev/sda6
btrfs-find-root /dev/sda6
btrfs-find-root -a /dev/sda6
btrfs-map-logical -l1 /dev/sda6
btrfs-map-logical -l1 -c1 /dev/sda6
btrfs-map-logical -l1 -o /tmp/btrfs-map-logic-out /dev/sda6
btrfs-map-logical -l1 -b1 /dev/sda6
btrfs-select-super -s 0 /dev/sda6
btrfs-select-super -s 1 /dev/sda6
btrfstune -S 1 /dev/sda6
btrfstune -f -S 0 /dev/sda6
btrfstune -r /dev/sda6
btrfstune -x /dev/sda6
btrfstune -n /dev/sda6
btrfstune -f -U 00000000-0000-0000-0000-000000000000 /dev/sda6
btrfstune -f -u /dev/sda6
btrfs-calc-size /dev/sda6
btrfs-calc-size -v /dev/sda6
btrfs-calc-size -b /dev/sda6
btrfs-debug-tree /dev/sda6
btrfs-debug-tree -e /dev/sda6
btrfs-debug-tree -d /dev/sda6
btrfs-debug-tree -r /dev/sda6
btrfs-debug-tree -R /dev/sda6
btrfs-debug-tree -u /dev/sda6
btrfs-debug-tree -b 0 /dev/sda6
btrfs-debug-tree -t 0 /dev/sda6
btrfs-debug-tree -t 2 /dev/sda6
btrfs-show-super /dev/sda6
btrfs-show-super -i 0 /dev/sda6
btrfs-show-super -i 1 /dev/sda6
btrfs-show-super -i 2 /dev/sda6
btrfs-show-super -a /dev/sda6
btrfs-show-super -f /dev/sda6
btrfs-show-super -F /dev/sda6
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume create /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default 258 /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default /mnt/btrfs-progs-tests
btrfs subvolume snapshot /mnt/btrfs-progs-tests/mysubvol /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume show /mnt/btrfs-progs-tests
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs filesystem df /mnt/btrfs-progs-tests
btrfs filesystem show /mnt/btrfs-progs-tests
btrfs filesystem sync /mnt/btrfs-progs-tests
btrfs filesystem label /mnt/btrfs-progs-tests btrfs_label_test
btrfs filesystem label /mnt/btrfs-progs-tests
btrfs filesystem usage /mnt/btrfs-progs-tests
btrfs filesystem defragment -s 1024 -l 2048 /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_0
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_1
btrfs filesystem defragment -f /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_2
btrfs filesystem defragment -czlib /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_3
btrfs filesystem defragment -clzo /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_4
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment -r /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment /mnt/btrfs-progs-tests
btrfs filesystem resize 1:-10M /mnt/btrfs-progs-tests
btrfs filesystem resize 1:max /mnt/btrfs-progs-tests
btrfs balance start /mnt/btrfs-progs-tests
btrfs balance start -v /mnt/btrfs-progs-tests
btrfs balance start -f /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance pause /mnt/btrfs-progs-tests
btrfs balance status /mnt/btrfs-progs-tests
btrfs balance resume /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance cancel /mnt/btrfs-progs-tests
btrfs balance start -dprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -dconvert=single /mnt/btrfs-progs-tests
btrfs balance start -ddevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -mprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -mconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -mdevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -sprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -sconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -sdevid=1 /mnt/btrfs-progs-tests
btrfs device add -f /dev/sda10 /mnt/btrfs-progs-tests
btrfs device del /dev/sda10 /mnt/btrfs-progs-tests
btrfs device stats /dev/sda6
btrfs device stats -z /dev/sda6
btrfs device stats /mnt/btrfs-progs-tests
btrfs device stats -z /mnt/btrfs-progs-tests
btrfs device usage /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start -B /mnt/btrfs-progs-tests
btrfs scrub start -B -d /mnt/btrfs-progs-tests
btrfs scrub start -B -r /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status -d /mnt/btrfs-progs-tests
btrfs scrub status -R /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status -d /dev/sda6
btrfs scrub status -R /dev/sda6
btrfs scrub status /dev/sda6
btrfs subvolume snapshot -r /mnt/btrfs-progs-tests /mnt/btrfs-progs-tests/snap1
btrfs send -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send -e -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send --no-data -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs quota enable /mnt/btrfs-progs-tests
btrfs quota rescan /mnt/btrfs-progs-tests
btrfs quota rescan -s /mnt/btrfs-progs-tests
btrfs quota rescan -w /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs quota enable /mnt/btrfs-progs-tests
btrfs qgroup create 1/5 /mnt/btrfs-progs-tests
btrfs qgroup create 2/5 /mnt/btrfs-progs-tests
btrfs qgroup assign 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup limit 1G 1/5 /mnt/btrfs-progs-tests
btrfs qgroup show /mnt/btrfs-progs-tests
btrfs qgroup show -p -c -r -e -F -f /mnt/btrfs-progs-tests
btrfs qgroup remove 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 1/5 /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda6 /dev/sda10 /mnt/btrfs-progs-tests
btrfs replace status /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda10 /dev/sda6 /mnt/btrfs-progs-tests
btrfs-convert /dev/sda6
btrfs-convert -r /dev/sda6
btrfs-convert -d /dev/sda6
btrfs-convert -i /dev/sda6
btrfs-convert -n /dev/sda6
btrfs-convert -N 4096 /dev/sda6
btrfs-convert -l test /dev/sda6
btrfs-convert -L /dev/sda6
btrfs-convert --no-progress /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 9 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 1 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 32 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 0 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 1 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 32 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -o /tmp/btrfs_image.img /dev/sda6
3: Manual check relation source by:
grep DUP *.c
Confirmed that all source are modified.
4: Use this raid type manually, do some operations in fs,
no error found in command and dmesg.
5: Combination of dup conversion with fsck
Confirmed OK with relative kernel patch titled:
[PATCH] btrfs: Support convert to -d dup for btrfs-convert
export TEST_DEV='/dev/vdc'
export TEST_DIR='/var/ltf/tester/mnt'
do_dup_test()
{
local m_from="$1"
local d_from="$2"
local m_to="$3"
local d_to="$4"
echo "Convert from -m $m_from -d $d_from to -m $m_to -d $d_to"
umount "$TEST_DIR" &>/dev/null
./mkfs.btrfs -f -m "$m_from" -d "$d_from" "$TEST_DEV" >/dev/null || return 1
mount "$TEST_DEV" "$TEST_DIR" || return 1
cp -a /sbin/* "$TEST_DIR"
[[ "$m_from" != "$m_to" ]] && {
./btrfs balance start -f -mconvert="$m_to" "$TEST_DIR" || return 1
}
[[ "$d_from" != "$d_to" ]] && {
local opt=()
[[ "$d_to" == single ]] && opt+=("-f")
./btrfs balance start "${opt[@]}" -dconvert="$d_to" "$TEST_DIR" || return 1
}
umount "$TEST_DIR" || return 1
./btrfsck "$TEST_DEV" || return 1
echo
return 0
}
test_all()
{
for m_from in single dup; do
for d_from in single dup; do
for m_to in single dup; do
for d_to in single dup; do
do_dup_test "$m_from" "$d_from" "$m_to" "$d_to" || return 1
done
done
done
done
}
test_all
Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Tested-by: Austin S. Hemmelgarn <ahferroin7@gmail.com>
[ minor updates in the changelog ]
Signed-off-by: David Sterba <dsterba@suse.com>
2015-11-19 17:36:24 +08:00
|
|
|
warning_on(!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP) && ssd,
|
2015-11-27 00:56:45 +08:00
|
|
|
"DUP may not actually lead to 2 copies on the device, see manual page");
|
btrfs-progs: mkfs: allow --data DUP for single device
Current code don't support DUP profile on single device, except it
is in mixed mode, because of following reasons:
1: Some SSD do deduplication internally, so the duplication on
the filesystem side has no effect.
2: On a physical device, if the entire disk broken, --data DUP does not
help.
3: Half performance compared to single profile.
4: We have a workaround: create multi-partition on a single device,
and btffs will treat them as multi device.
Instead of refusing --data DUP, we give the user a choice and print
a wrning.
Test:
1: Tested by xfstests
Run with modified xfstests, I add test items of -d dup in single
device into btrfs/* and common/rc, run tests of btrfs/*,
with all mount option, no regression diffed with v4.3.
2: Tested by btrfs-progs
Checked following commands in "-m dup -d dup" fs with memleck
checking, all passed:
mkfs.btrfs -f --data dup --metadata dup /dev/sda6
btrfs filesystem show /dev/sda6
btrfs filesystem label /dev/sda6 btrfs_label_test
btrfs filesystem label /dev/sda6
btrfs device scan --all-devices
btrfs device scan /dev/sda6
btrfs device scan /dev/sda6
btrfs device ready /dev/sda6
btrfs check /dev/sda6
btrfs check -s 1 /dev/sda6
btrfs check --repair /dev/sda6
btrfs check --init-csum-tree /dev/sda6
btrfs check --init-extent-tree /dev/sda6
btrfs check --check-data-csum /dev/sda6
btrfs check --qgroup-report /dev/sda6
btrfs rescue super-recover -y /dev/sda6
btrfs rescue zero-log /dev/sda6
btrfs restore -l /dev/sda6
btrfs restore /dev/sda6 /
btrfs restore -s /dev/sda6 /
btrfs restore -x /dev/sda6 /
btrfs restore -m /dev/sda6 /
btrfs restore -S /dev/sda6 /
btrfs restore -v /dev/sda6 /
btrfs restore -i /dev/sda6 /
btrfs restore -o /dev/sda6 /
btrfs restore -u0 /dev/sda6 /
btrfs restore -u1 /dev/sda6 /
btrfs restore -D /dev/sda6 /
btrfs property list /dev/sda6
btrfs property get /dev/sda6 label
btrfs property set /dev/sda6 label test
btrfs property set /dev/sda6 label btrfs_label_test
btrfs help
btrfs help --full
btrfs version
btrfsck /dev/sda6
btrfs-find-root /dev/sda6
btrfs-find-root -a /dev/sda6
btrfs-map-logical -l1 /dev/sda6
btrfs-map-logical -l1 -c1 /dev/sda6
btrfs-map-logical -l1 -o /tmp/btrfs-map-logic-out /dev/sda6
btrfs-map-logical -l1 -b1 /dev/sda6
btrfs-select-super -s 0 /dev/sda6
btrfs-select-super -s 1 /dev/sda6
btrfstune -S 1 /dev/sda6
btrfstune -f -S 0 /dev/sda6
btrfstune -r /dev/sda6
btrfstune -x /dev/sda6
btrfstune -n /dev/sda6
btrfstune -f -U 00000000-0000-0000-0000-000000000000 /dev/sda6
btrfstune -f -u /dev/sda6
btrfs-calc-size /dev/sda6
btrfs-calc-size -v /dev/sda6
btrfs-calc-size -b /dev/sda6
btrfs-debug-tree /dev/sda6
btrfs-debug-tree -e /dev/sda6
btrfs-debug-tree -d /dev/sda6
btrfs-debug-tree -r /dev/sda6
btrfs-debug-tree -R /dev/sda6
btrfs-debug-tree -u /dev/sda6
btrfs-debug-tree -b 0 /dev/sda6
btrfs-debug-tree -t 0 /dev/sda6
btrfs-debug-tree -t 2 /dev/sda6
btrfs-show-super /dev/sda6
btrfs-show-super -i 0 /dev/sda6
btrfs-show-super -i 1 /dev/sda6
btrfs-show-super -i 2 /dev/sda6
btrfs-show-super -a /dev/sda6
btrfs-show-super -f /dev/sda6
btrfs-show-super -F /dev/sda6
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume create /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default 258 /mnt/btrfs-progs-tests
btrfs subvolume get-default /mnt/btrfs-progs-tests
btrfs subvolume set-default /mnt/btrfs-progs-tests
btrfs subvolume snapshot /mnt/btrfs-progs-tests/mysubvol /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume list /mnt/btrfs-progs-tests
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume find-new /mnt/btrfs-progs-tests/mysubvol 0
btrfs subvolume show /mnt/btrfs-progs-tests
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume show /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol_snap
btrfs subvolume delete /mnt/btrfs-progs-tests/mysubvol
btrfs subvolume sync /mnt/btrfs-progs-tests
btrfs filesystem df /mnt/btrfs-progs-tests
btrfs filesystem show /mnt/btrfs-progs-tests
btrfs filesystem sync /mnt/btrfs-progs-tests
btrfs filesystem label /mnt/btrfs-progs-tests btrfs_label_test
btrfs filesystem label /mnt/btrfs-progs-tests
btrfs filesystem usage /mnt/btrfs-progs-tests
btrfs filesystem defragment -s 1024 -l 2048 /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_0
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_1
btrfs filesystem defragment -f /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_2
btrfs filesystem defragment -czlib /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_3
btrfs filesystem defragment -clzo /mnt/btrfs-progs-tests/filesystem_test_dir/test_dir_0/test_file_4
btrfs filesystem defragment /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment -r /mnt/btrfs-progs-tests/filesystem_test_dir
btrfs filesystem defragment /mnt/btrfs-progs-tests
btrfs filesystem resize 1:-10M /mnt/btrfs-progs-tests
btrfs filesystem resize 1:max /mnt/btrfs-progs-tests
btrfs balance start /mnt/btrfs-progs-tests
btrfs balance start -v /mnt/btrfs-progs-tests
btrfs balance start -f /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance pause /mnt/btrfs-progs-tests
btrfs balance status /mnt/btrfs-progs-tests
btrfs balance resume /mnt/btrfs-progs-tests
btrfs balance status -v /mnt/btrfs-progs-tests
btrfs balance cancel /mnt/btrfs-progs-tests
btrfs balance start -dprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -dconvert=single /mnt/btrfs-progs-tests
btrfs balance start -ddevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -mprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -mconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -mdevid=1 /mnt/btrfs-progs-tests
btrfs balance start -f -sprofiles=single /mnt/btrfs-progs-tests
btrfs balance start -f -sconvert=single /mnt/btrfs-progs-tests
btrfs balance start -f -sdevid=1 /mnt/btrfs-progs-tests
btrfs device add -f /dev/sda10 /mnt/btrfs-progs-tests
btrfs device del /dev/sda10 /mnt/btrfs-progs-tests
btrfs device stats /dev/sda6
btrfs device stats -z /dev/sda6
btrfs device stats /mnt/btrfs-progs-tests
btrfs device stats -z /mnt/btrfs-progs-tests
btrfs device usage /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start -B /mnt/btrfs-progs-tests
btrfs scrub start -B -d /mnt/btrfs-progs-tests
btrfs scrub start -B -r /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub status -d /mnt/btrfs-progs-tests
btrfs scrub status -R /mnt/btrfs-progs-tests
btrfs scrub status /mnt/btrfs-progs-tests
btrfs scrub start /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status /dev/sda6
btrfs scrub status -d /dev/sda6
btrfs scrub status -R /dev/sda6
btrfs scrub status /dev/sda6
btrfs subvolume snapshot -r /mnt/btrfs-progs-tests /mnt/btrfs-progs-tests/snap1
btrfs send -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send -e -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs send --no-data -f /tmp/btrfs_snapshot_test /mnt/btrfs-progs-tests/snap1
btrfs quota enable /mnt/btrfs-progs-tests
btrfs quota rescan /mnt/btrfs-progs-tests
btrfs quota rescan -s /mnt/btrfs-progs-tests
btrfs quota rescan -w /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs quota enable /mnt/btrfs-progs-tests
btrfs qgroup create 1/5 /mnt/btrfs-progs-tests
btrfs qgroup create 2/5 /mnt/btrfs-progs-tests
btrfs qgroup assign 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup limit 1G 1/5 /mnt/btrfs-progs-tests
btrfs qgroup show /mnt/btrfs-progs-tests
btrfs qgroup show -p -c -r -e -F -f /mnt/btrfs-progs-tests
btrfs qgroup remove 1/5 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 2/5 /mnt/btrfs-progs-tests
btrfs qgroup destroy 1/5 /mnt/btrfs-progs-tests
btrfs quota disable /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda6 /dev/sda10 /mnt/btrfs-progs-tests
btrfs replace status /mnt/btrfs-progs-tests
btrfs replace start -f -B /dev/sda10 /dev/sda6 /mnt/btrfs-progs-tests
btrfs-convert /dev/sda6
btrfs-convert -r /dev/sda6
btrfs-convert -d /dev/sda6
btrfs-convert -i /dev/sda6
btrfs-convert -n /dev/sda6
btrfs-convert -N 4096 /dev/sda6
btrfs-convert -l test /dev/sda6
btrfs-convert -L /dev/sda6
btrfs-convert --no-progress /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -c 9 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 0 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 1 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -t 32 /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
mkfs.btrfs -f /dev/sda6
btrfs-image -w /dev/sda6 /tmp/btrfs_image.img
btrfs-image -r /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 0 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 1 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -t 32 /tmp/btrfs_image.img /dev/sda6
btrfs-image -r -o /tmp/btrfs_image.img /dev/sda6
3: Manual check relation source by:
grep DUP *.c
Confirmed that all source are modified.
4: Use this raid type manually, do some operations in fs,
no error found in command and dmesg.
5: Combination of dup conversion with fsck
Confirmed OK with relative kernel patch titled:
[PATCH] btrfs: Support convert to -d dup for btrfs-convert
export TEST_DEV='/dev/vdc'
export TEST_DIR='/var/ltf/tester/mnt'
do_dup_test()
{
local m_from="$1"
local d_from="$2"
local m_to="$3"
local d_to="$4"
echo "Convert from -m $m_from -d $d_from to -m $m_to -d $d_to"
umount "$TEST_DIR" &>/dev/null
./mkfs.btrfs -f -m "$m_from" -d "$d_from" "$TEST_DEV" >/dev/null || return 1
mount "$TEST_DEV" "$TEST_DIR" || return 1
cp -a /sbin/* "$TEST_DIR"
[[ "$m_from" != "$m_to" ]] && {
./btrfs balance start -f -mconvert="$m_to" "$TEST_DIR" || return 1
}
[[ "$d_from" != "$d_to" ]] && {
local opt=()
[[ "$d_to" == single ]] && opt+=("-f")
./btrfs balance start "${opt[@]}" -dconvert="$d_to" "$TEST_DIR" || return 1
}
umount "$TEST_DIR" || return 1
./btrfsck "$TEST_DEV" || return 1
echo
return 0
}
test_all()
{
for m_from in single dup; do
for d_from in single dup; do
for m_to in single dup; do
for d_to in single dup; do
do_dup_test "$m_from" "$d_from" "$m_to" "$d_to" || return 1
done
done
done
done
}
test_all
Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Tested-by: Austin S. Hemmelgarn <ahferroin7@gmail.com>
[ minor updates in the changelog ]
Signed-off-by: David Sterba <dsterba@suse.com>
2015-11-19 17:36:24 +08:00
|
|
|
|
2013-08-07 20:11:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:54:48 +08:00
|
|
|
int group_profile_max_safe_loss(u64 flags)
|
|
|
|
{
|
|
|
|
switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
|
|
|
|
case 0: /* single */
|
|
|
|
case BTRFS_BLOCK_GROUP_DUP:
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID0:
|
|
|
|
return 0;
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID1:
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID5:
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID10:
|
|
|
|
return 1;
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID6:
|
|
|
|
return 2;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-11 06:46:30 +08:00
|
|
|
/*
|
|
|
|
* Check if a device is suitable for btrfs
|
2013-04-15 14:38:09 +08:00
|
|
|
* returns:
|
2015-06-11 06:46:30 +08:00
|
|
|
* 1: something is wrong, an error is printed
|
2013-04-15 14:38:09 +08:00
|
|
|
* 0: all is fine
|
|
|
|
*/
|
2015-06-11 06:46:30 +08:00
|
|
|
int test_dev_for_mkfs(char *file, int force_overwrite)
|
2013-04-15 14:38:09 +08:00
|
|
|
{
|
|
|
|
int ret, fd;
|
2013-07-26 01:35:27 +08:00
|
|
|
struct stat st;
|
2013-04-15 14:38:09 +08:00
|
|
|
|
|
|
|
ret = is_swap_device(file);
|
|
|
|
if (ret < 0) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: checking status of %s: %s\n", file,
|
2013-04-15 14:38:09 +08:00
|
|
|
strerror(-ret));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (ret == 1) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: %s is a swap device\n", file);
|
2013-04-15 14:38:09 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!force_overwrite) {
|
|
|
|
if (check_overwrite(file)) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "Use the -f option to force overwrite.\n");
|
2013-04-15 14:38:09 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = check_mounted(file);
|
|
|
|
if (ret < 0) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: checking mount status of %s: %s\n",
|
|
|
|
file, strerror(-ret));
|
2013-04-15 14:38:09 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (ret == 1) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: %s is mounted\n", file);
|
2013-04-15 14:38:09 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* check if the device is busy */
|
|
|
|
fd = open(file, O_RDWR|O_EXCL);
|
|
|
|
if (fd < 0) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: unable to open %s: %s\n", file,
|
2013-04-15 14:38:09 +08:00
|
|
|
strerror(errno));
|
|
|
|
return 1;
|
|
|
|
}
|
2013-07-26 01:35:27 +08:00
|
|
|
if (fstat(fd, &st)) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: unable to stat %s: %s\n", file,
|
2013-07-26 01:35:27 +08:00
|
|
|
strerror(errno));
|
2013-11-07 07:15:44 +08:00
|
|
|
close(fd);
|
2013-07-26 01:35:27 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!S_ISBLK(st.st_mode)) {
|
2015-06-11 06:46:30 +08:00
|
|
|
fprintf(stderr, "ERROR: %s is not a block device\n", file);
|
2013-11-07 07:15:44 +08:00
|
|
|
close(fd);
|
2013-07-26 01:35:27 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2013-04-15 14:38:09 +08:00
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-15 13:30:50 +08:00
|
|
|
|
2015-09-15 05:42:29 +08:00
|
|
|
int btrfs_scan_lblkid(void)
|
2013-09-27 23:45:00 +08:00
|
|
|
{
|
|
|
|
int fd = -1;
|
2013-11-07 07:15:52 +08:00
|
|
|
int ret;
|
2013-09-27 23:45:00 +08:00
|
|
|
u64 num_devices;
|
|
|
|
struct btrfs_fs_devices *tmp_devices;
|
|
|
|
blkid_dev_iterate iter = NULL;
|
|
|
|
blkid_dev dev = NULL;
|
|
|
|
blkid_cache cache = NULL;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
2014-10-31 12:11:20 +08:00
|
|
|
if (btrfs_scan_done)
|
|
|
|
return 0;
|
|
|
|
|
2016-01-04 09:01:30 +08:00
|
|
|
if (blkid_get_cache(&cache, NULL) < 0) {
|
2013-09-27 23:45:00 +08:00
|
|
|
printf("ERROR: lblkid cache get failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
blkid_probe_all(cache);
|
|
|
|
iter = blkid_dev_iterate_begin(cache);
|
|
|
|
blkid_dev_set_search(iter, "TYPE", "btrfs");
|
|
|
|
while (blkid_dev_next(iter, &dev) == 0) {
|
|
|
|
dev = blkid_verify(cache, dev);
|
|
|
|
if (!dev)
|
|
|
|
continue;
|
2013-11-07 07:15:49 +08:00
|
|
|
/* if we are here its definitely a btrfs disk*/
|
2015-01-03 10:24:10 +08:00
|
|
|
strncpy_null(path, blkid_dev_devname(dev));
|
2013-09-27 23:45:00 +08:00
|
|
|
|
|
|
|
fd = open(path, O_RDONLY);
|
2013-11-07 07:15:49 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
printf("ERROR: could not open %s\n", path);
|
|
|
|
continue;
|
|
|
|
}
|
2013-11-07 07:15:52 +08:00
|
|
|
ret = btrfs_scan_one_device(fd, path, &tmp_devices,
|
2014-07-03 17:36:36 +08:00
|
|
|
&num_devices, BTRFS_SUPER_INFO_OFFSET, 0);
|
2013-11-07 07:15:52 +08:00
|
|
|
if (ret) {
|
|
|
|
printf("ERROR: could not scan %s\n", path);
|
|
|
|
close (fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-09-27 23:45:00 +08:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
blkid_dev_iterate_end(iter);
|
2014-03-14 11:28:11 +08:00
|
|
|
blkid_put_cache(cache);
|
2014-10-31 12:11:20 +08:00
|
|
|
|
|
|
|
btrfs_scan_done = 1;
|
|
|
|
|
2013-09-27 23:45:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-30 16:50:37 +08:00
|
|
|
int is_vol_small(char *file)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int e;
|
|
|
|
struct stat st;
|
|
|
|
u64 size;
|
|
|
|
|
|
|
|
fd = open(file, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
|
|
|
if (fstat(fd, &st) < 0) {
|
|
|
|
e = -errno;
|
|
|
|
close(fd);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
size = btrfs_device_size(fd, &st);
|
|
|
|
if (size == 0) {
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-07-15 16:02:10 +08:00
|
|
|
if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
|
2013-08-30 16:50:37 +08:00
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 16:27:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This reads a line from the stdin and only returns non-zero if the
|
|
|
|
* first whitespace delimited token is a case insensitive match with yes
|
|
|
|
* or y.
|
|
|
|
*/
|
|
|
|
int ask_user(char *question)
|
|
|
|
{
|
|
|
|
char buf[30] = {0,};
|
|
|
|
char *saveptr = NULL;
|
|
|
|
char *answer;
|
|
|
|
|
|
|
|
printf("%s [y/N]: ", question);
|
|
|
|
|
|
|
|
return fgets(buf, sizeof(buf) - 1, stdin) &&
|
|
|
|
(answer = strtok_r(buf, " \t\n\r", &saveptr)) &&
|
|
|
|
(!strcasecmp(answer, "yes") || !strcasecmp(answer, "y"));
|
|
|
|
}
|
2013-09-19 00:19:30 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For a given:
|
|
|
|
* - file or directory return the containing tree root id
|
2014-09-02 19:57:20 +08:00
|
|
|
* - subvolume return its own tree id
|
2013-09-19 00:19:30 +08:00
|
|
|
* - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is
|
|
|
|
* undefined and function returns -1
|
|
|
|
*/
|
|
|
|
int lookup_ino_rootid(int fd, u64 *rootid)
|
|
|
|
{
|
|
|
|
struct btrfs_ioctl_ino_lookup_args args;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
args.treeid = 0;
|
|
|
|
args.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
|
2016-01-12 20:35:50 +08:00
|
|
|
if (ret < 0) {
|
2013-09-19 00:19:30 +08:00
|
|
|
fprintf(stderr, "ERROR: Failed to lookup root id - %s\n",
|
2016-01-12 18:20:18 +08:00
|
|
|
strerror(errno));
|
2013-09-19 00:19:30 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*rootid = args.treeid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-02-10 15:28:28 +08:00
|
|
|
|
2014-07-23 13:47:35 +08:00
|
|
|
/*
|
|
|
|
* return 0 if a btrfs mount point is found
|
|
|
|
* return 1 if a mount point is found but not btrfs
|
|
|
|
* return <0 if something goes wrong
|
|
|
|
*/
|
2014-02-10 15:28:28 +08:00
|
|
|
int find_mount_root(const char *path, char **mount_root)
|
|
|
|
{
|
|
|
|
FILE *mnttab;
|
|
|
|
int fd;
|
|
|
|
struct mntent *ent;
|
|
|
|
int len;
|
|
|
|
int ret;
|
2014-07-23 13:47:35 +08:00
|
|
|
int not_btrfs = 1;
|
2014-02-10 15:28:28 +08:00
|
|
|
int longest_matchlen = 0;
|
|
|
|
char *longest_match = NULL;
|
|
|
|
|
|
|
|
fd = open(path, O_RDONLY | O_NOATIME);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
mnttab = setmntent("/proc/self/mounts", "r");
|
|
|
|
if (!mnttab)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
while ((ent = getmntent(mnttab))) {
|
|
|
|
len = strlen(ent->mnt_dir);
|
|
|
|
if (strncmp(ent->mnt_dir, path, len) == 0) {
|
2014-09-04 10:27:48 +08:00
|
|
|
/* match found and use the latest match */
|
|
|
|
if (longest_matchlen <= len) {
|
2014-02-10 15:28:28 +08:00
|
|
|
free(longest_match);
|
|
|
|
longest_matchlen = len;
|
|
|
|
longest_match = strdup(ent->mnt_dir);
|
2014-07-23 13:47:35 +08:00
|
|
|
not_btrfs = strcmp(ent->mnt_type, "btrfs");
|
2014-02-10 15:28:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endmntent(mnttab);
|
|
|
|
|
2014-07-23 13:47:34 +08:00
|
|
|
if (!longest_match)
|
2014-02-10 15:28:28 +08:00
|
|
|
return -ENOENT;
|
2014-07-23 13:47:35 +08:00
|
|
|
if (not_btrfs) {
|
|
|
|
free(longest_match);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-02-10 15:28:28 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
*mount_root = realpath(longest_match, NULL);
|
|
|
|
if (!*mount_root)
|
|
|
|
ret = -errno;
|
|
|
|
|
|
|
|
free(longest_match);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-07-04 15:29:17 +08:00
|
|
|
|
2015-02-02 22:51:15 +08:00
|
|
|
int test_minimum_size(const char *file, u32 nodesize)
|
2014-07-04 15:29:17 +08:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
fd = open(file, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
|
|
|
if (stat(file, &statbuf) < 0) {
|
|
|
|
close(fd);
|
|
|
|
return -errno;
|
|
|
|
}
|
2015-02-02 22:51:15 +08:00
|
|
|
if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
|
2014-07-04 15:29:17 +08:00
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-08-01 10:44:21 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* test if name is a correct subvolume name
|
|
|
|
* this function return
|
|
|
|
* 0-> name is not a correct subvolume name
|
|
|
|
* 1-> name is a correct subvolume name
|
|
|
|
*/
|
|
|
|
int test_issubvolname(const char *name)
|
|
|
|
{
|
|
|
|
return name[0] != '\0' && !strchr(name, '/') &&
|
|
|
|
strcmp(name, ".") && strcmp(name, "..");
|
|
|
|
}
|
2014-08-01 10:58:00 +08:00
|
|
|
|
|
|
|
/*
|
2016-01-14 00:45:39 +08:00
|
|
|
* Test if path is a directory
|
|
|
|
* Returns:
|
|
|
|
* 0 - path exists but it is not a directory
|
|
|
|
* 1 - path exists and it is a directory
|
|
|
|
* < 0 - error
|
2014-08-01 10:58:00 +08:00
|
|
|
*/
|
|
|
|
int test_isdir(const char *path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = stat(path, &st);
|
2016-01-14 00:45:39 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
2014-08-01 10:58:00 +08:00
|
|
|
|
2016-01-14 00:45:39 +08:00
|
|
|
return !!S_ISDIR(st.st_mode);
|
2014-08-01 10:58:00 +08:00
|
|
|
}
|
2014-08-31 02:27:00 +08:00
|
|
|
|
|
|
|
void units_set_mode(unsigned *units, unsigned mode)
|
|
|
|
{
|
|
|
|
unsigned base = *units & UNITS_MODE_MASK;
|
|
|
|
|
|
|
|
*units = base | mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void units_set_base(unsigned *units, unsigned base)
|
|
|
|
{
|
|
|
|
unsigned mode = *units & ~UNITS_MODE_MASK;
|
|
|
|
|
|
|
|
*units = base | mode;
|
|
|
|
}
|
Btrfs-progs: check, ability to detect and fix outdated snapshot root items
This change adds code to detect and fix the issue introduced in the kernel
release 3.17, where creation of read-only snapshots lead to a corrupted
filesystem if they were created at a moment when the source subvolume/snapshot
had orphan items. The issue was that the on-disk root items became incorrect,
referring to the pre orphan cleanup root node instead of the post orphan
cleanup root node.
A test filesystem can be generated with the test case recently submitted for
xfstests/fstests, which is essencially the following (bash script):
workout()
{
ops=$1
procs=$2
num_snapshots=$3
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
run_check $FSSTRESS_PROG -p $procs \
-x "$snapshot_cmd" -X $num_snapshots -d $SCRATCH_MNT -n $ops
}
ops=10000
procs=4
snapshots=500
workout $ops $procs $snapshots
Example of btrfsck's (btrfs check) behaviour against such filesystem:
$ btrfsck /dev/loop0
root item for root 311, current bytenr 44630016, current gen 60, current level 1, new bytenr 44957696, new gen 61, new level 1
root item for root 1480, current bytenr 1003569152, current gen 1271, current level 1, new bytenr 1004175360, new gen 1272, new level 1
root item for root 1509, current bytenr 1037434880, current gen 1300, current level 1, new bytenr 1038467072, new gen 1301, new level 1
root item for root 1562, current bytenr 33636352, current gen 1354, current level 1, new bytenr 34455552, new gen 1355, new level 1
root item for root 3094, current bytenr 1011712000, current gen 2935, current level 1, new bytenr 1008484352, new gen 2936, new level 1
root item for root 3716, current bytenr 80805888, current gen 3578, current level 1, new bytenr 73515008, new gen 3579, new level 1
root item for root 4085, current bytenr 714031104, current gen 3958, current level 1, new bytenr 716816384, new gen 3959, new level 1
Found 7 roots with an outdated root item.
Please run a filesystem check with the option --repair to fix them.
$ echo $?
1
$ btrfsck --repair /dev/loop0
enabling repair mode
fixing root item for root 311, current bytenr 44630016, current gen 60, current level 1, new bytenr 44957696, new gen 61, new level 1
fixing root item for root 1480, current bytenr 1003569152, current gen 1271, current level 1, new bytenr 1004175360, new gen 1272, new level 1
fixing root item for root 1509, current bytenr 1037434880, current gen 1300, current level 1, new bytenr 1038467072, new gen 1301, new level 1
fixing root item for root 1562, current bytenr 33636352, current gen 1354, current level 1, new bytenr 34455552, new gen 1355, new level 1
fixing root item for root 3094, current bytenr 1011712000, current gen 2935, current level 1, new bytenr 1008484352, new gen 2936, new level 1
fixing root item for root 3716, current bytenr 80805888, current gen 3578, current level 1, new bytenr 73515008, new gen 3579, new level 1
fixing root item for root 4085, current bytenr 714031104, current gen 3958, current level 1, new bytenr 716816384, new gen 3959, new level 1
Fixed 7 roots.
Checking filesystem on /dev/loop0
UUID: 2186e9b9-c977-4a35-9c7b-69c6609d4620
checking extents
checking free space cache
cache and super generation don't match, space cache will be invalidated
checking fs roots
checking csums
checking root refs
found 618537000 bytes used err is 0
total csum bytes: 130824
total tree bytes: 601620480
total fs tree bytes: 580288512
total extent tree bytes: 18464768
btree space waste bytes: 136939144
file data blocks allocated: 34150318080
referenced 27815415808
Btrfs v3.17-rc3-2-gbbe1dd8
$ echo $?
0
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
2014-10-18 01:20:08 +08:00
|
|
|
|
|
|
|
int find_next_key(struct btrfs_path *path, struct btrfs_key *key)
|
|
|
|
{
|
|
|
|
int level;
|
|
|
|
|
|
|
|
for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
|
|
|
|
if (!path->nodes[level])
|
|
|
|
break;
|
|
|
|
if (path->slots[level] + 1 >=
|
|
|
|
btrfs_header_nritems(path->nodes[level]))
|
|
|
|
continue;
|
|
|
|
if (level == 0)
|
|
|
|
btrfs_item_key_to_cpu(path->nodes[level], key,
|
|
|
|
path->slots[level] + 1);
|
|
|
|
else
|
|
|
|
btrfs_node_key_to_cpu(path->nodes[level], key,
|
|
|
|
path->slots[level] + 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-24 01:05:31 +08:00
|
|
|
|
|
|
|
char* btrfs_group_type_str(u64 flag)
|
|
|
|
{
|
|
|
|
u64 mask = BTRFS_BLOCK_GROUP_TYPE_MASK |
|
|
|
|
BTRFS_SPACE_INFO_GLOBAL_RSV;
|
|
|
|
|
|
|
|
switch (flag & mask) {
|
|
|
|
case BTRFS_BLOCK_GROUP_DATA:
|
|
|
|
return "Data";
|
|
|
|
case BTRFS_BLOCK_GROUP_SYSTEM:
|
|
|
|
return "System";
|
|
|
|
case BTRFS_BLOCK_GROUP_METADATA:
|
|
|
|
return "Metadata";
|
|
|
|
case BTRFS_BLOCK_GROUP_DATA|BTRFS_BLOCK_GROUP_METADATA:
|
|
|
|
return "Data+Metadata";
|
|
|
|
case BTRFS_SPACE_INFO_GLOBAL_RSV:
|
|
|
|
return "GlobalReserve";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char* btrfs_group_profile_str(u64 flag)
|
|
|
|
{
|
|
|
|
switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
|
|
|
|
case 0:
|
|
|
|
return "single";
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID0:
|
|
|
|
return "RAID0";
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID1:
|
|
|
|
return "RAID1";
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID5:
|
|
|
|
return "RAID5";
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID6:
|
|
|
|
return "RAID6";
|
|
|
|
case BTRFS_BLOCK_GROUP_DUP:
|
|
|
|
return "DUP";
|
|
|
|
case BTRFS_BLOCK_GROUP_RAID10:
|
|
|
|
return "RAID10";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-14 03:19:01 +08:00
|
|
|
u64 disk_size(char *path)
|
|
|
|
{
|
|
|
|
struct statfs sfs;
|
|
|
|
|
|
|
|
if (statfs(path, &sfs) < 0)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return sfs.f_bsize * sfs.f_blocks;
|
|
|
|
}
|
2014-02-14 03:19:50 +08:00
|
|
|
|
|
|
|
u64 get_partition_size(char *dev)
|
|
|
|
{
|
|
|
|
u64 result;
|
|
|
|
int fd = open(dev, O_RDONLY);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return 0;
|
|
|
|
if (ioctl(fd, BLKGETSIZE64, &result) < 0) {
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2014-11-26 01:46:16 +08:00
|
|
|
|
|
|
|
int btrfs_tree_search2_ioctl_supported(int fd)
|
|
|
|
{
|
|
|
|
struct btrfs_ioctl_search_args_v2 *args2;
|
|
|
|
struct btrfs_ioctl_search_key *sk;
|
|
|
|
int args2_size = 1024;
|
|
|
|
char args2_buf[args2_size];
|
|
|
|
int ret;
|
|
|
|
static int v2_supported = -1;
|
|
|
|
|
|
|
|
if (v2_supported != -1)
|
|
|
|
return v2_supported;
|
|
|
|
|
|
|
|
args2 = (struct btrfs_ioctl_search_args_v2 *)args2_buf;
|
|
|
|
sk = &(args2->key);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for the extent tree item in the root tree.
|
|
|
|
*/
|
|
|
|
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
|
|
|
|
sk->min_objectid = BTRFS_EXTENT_TREE_OBJECTID;
|
|
|
|
sk->max_objectid = BTRFS_EXTENT_TREE_OBJECTID;
|
|
|
|
sk->min_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->max_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->min_offset = 0;
|
|
|
|
sk->max_offset = (u64)-1;
|
|
|
|
sk->min_transid = 0;
|
|
|
|
sk->max_transid = (u64)-1;
|
|
|
|
sk->nr_items = 1;
|
|
|
|
args2->buf_size = args2_size - sizeof(struct btrfs_ioctl_search_args_v2);
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, args2);
|
|
|
|
if (ret == -EOPNOTSUPP)
|
|
|
|
v2_supported = 0;
|
|
|
|
else if (ret == 0)
|
|
|
|
v2_supported = 1;
|
|
|
|
else
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return v2_supported;
|
|
|
|
}
|
2015-03-20 09:10:15 +08:00
|
|
|
|
2015-09-26 00:15:44 +08:00
|
|
|
int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features)
|
2015-03-20 09:10:15 +08:00
|
|
|
{
|
2015-02-02 22:51:15 +08:00
|
|
|
if (nodesize < sectorsize) {
|
2015-03-20 09:10:15 +08:00
|
|
|
fprintf(stderr,
|
2015-02-02 22:51:15 +08:00
|
|
|
"ERROR: Illegal nodesize %u (smaller than %u)\n",
|
|
|
|
nodesize, sectorsize);
|
2015-03-20 09:10:15 +08:00
|
|
|
return -1;
|
2015-02-02 22:51:15 +08:00
|
|
|
} else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
|
2015-03-20 09:10:15 +08:00
|
|
|
fprintf(stderr,
|
2015-02-02 22:51:15 +08:00
|
|
|
"ERROR: Illegal nodesize %u (larger than %u)\n",
|
|
|
|
nodesize, BTRFS_MAX_METADATA_BLOCKSIZE);
|
2015-03-20 09:10:15 +08:00
|
|
|
return -1;
|
2015-02-02 22:51:15 +08:00
|
|
|
} else if (nodesize & (sectorsize - 1)) {
|
2015-03-20 09:10:15 +08:00
|
|
|
fprintf(stderr,
|
2015-02-02 22:51:15 +08:00
|
|
|
"ERROR: Illegal nodesize %u (not aligned to %u)\n",
|
|
|
|
nodesize, sectorsize);
|
2015-03-20 09:10:15 +08:00
|
|
|
return -1;
|
2015-09-26 00:15:44 +08:00
|
|
|
} else if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS &&
|
|
|
|
nodesize != sectorsize) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Illegal nodesize %u (not equal to %u for mixed block group)\n",
|
|
|
|
nodesize, sectorsize);
|
|
|
|
return -1;
|
2015-03-20 09:10:15 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-12 22:09:33 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy a path argument from SRC to DEST and check the SRC length if it's at
|
|
|
|
* most PATH_MAX and fits into DEST. DESTLEN is supposed to be exact size of
|
|
|
|
* the buffer.
|
|
|
|
* The destination buffer is zero terminated.
|
|
|
|
* Return < 0 for error, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int arg_copy_path(char *dest, const char *src, int destlen)
|
|
|
|
{
|
|
|
|
size_t len = strlen(src);
|
|
|
|
|
|
|
|
if (len >= PATH_MAX || len >= destlen)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
__strncpy__null(dest, src, destlen);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-08-31 19:03:41 +08:00
|
|
|
|
|
|
|
unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
|
|
|
|
{
|
|
|
|
unsigned int unit_mode = UNITS_DEFAULT;
|
|
|
|
int arg_i;
|
|
|
|
int arg_end;
|
|
|
|
|
|
|
|
for (arg_i = 0; arg_i < *argc; arg_i++) {
|
|
|
|
if (!strcmp(argv[arg_i], "--raw")) {
|
|
|
|
unit_mode = UNITS_RAW;
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "--human-readable")) {
|
|
|
|
unit_mode = UNITS_HUMAN_BINARY;
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(argv[arg_i], "--iec")) {
|
|
|
|
units_set_mode(&unit_mode, UNITS_BINARY);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "--si")) {
|
|
|
|
units_set_mode(&unit_mode, UNITS_DECIMAL);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(argv[arg_i], "--kbytes")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_KBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "--mbytes")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_MBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "--gbytes")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_GBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "--tbytes")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_TBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!df_mode)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!strcmp(argv[arg_i], "-b")) {
|
|
|
|
unit_mode = UNITS_RAW;
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-h")) {
|
|
|
|
unit_mode = UNITS_HUMAN_BINARY;
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-H")) {
|
|
|
|
unit_mode = UNITS_HUMAN_DECIMAL;
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-k")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_KBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-m")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_MBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-g")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_GBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[arg_i], "-t")) {
|
|
|
|
units_set_base(&unit_mode, UNITS_TBYTES);
|
|
|
|
argv[arg_i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (arg_i = 0, arg_end = 0; arg_i < *argc; arg_i++) {
|
|
|
|
if (!argv[arg_i])
|
|
|
|
continue;
|
|
|
|
argv[arg_end] = argv[arg_i];
|
|
|
|
arg_end++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*argc = arg_end;
|
|
|
|
|
|
|
|
return unit_mode;
|
|
|
|
}
|
2015-10-10 22:30:56 +08:00
|
|
|
|
|
|
|
int string_is_numerical(const char *str)
|
|
|
|
{
|
|
|
|
if (!(*str >= '0' && *str <= '9'))
|
|
|
|
return 0;
|
|
|
|
while (*str >= '0' && *str <= '9')
|
|
|
|
str++;
|
|
|
|
if (*str != '\0')
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2016-01-14 17:30:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Preprocess @argv with getopt_long to reorder options and consume the "--"
|
|
|
|
* option separator.
|
|
|
|
* Unknown short and long options are reported, optionally the @usage is printed
|
|
|
|
* before exit.
|
|
|
|
*/
|
|
|
|
void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
|
|
|
|
{
|
|
|
|
static const struct option long_options[] = {
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int c = getopt_long(argc, argv, "", long_options, NULL);
|
|
|
|
|
|
|
|
if (c < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
default:
|
|
|
|
if (usagestr)
|
|
|
|
usage(usagestr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|