2008-01-04 15:38:22 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Oracle. All rights reserved.
|
2013-01-30 08:32:47 +00:00
|
|
|
* Copyright (C) 2008 Morey Roof. All rights reserved.
|
2008-01-04 15:38:22 +00: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-20 21:04:07 +00:00
|
|
|
#include <string.h>
|
2008-03-24 19:04:49 +00:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mount.h>
|
2008-01-04 15:38:22 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2008-04-01 14:36:46 +00:00
|
|
|
#include <mntent.h>
|
2012-10-29 17:53:18 +00:00
|
|
|
#include <ctype.h>
|
2010-09-09 02:57:02 +00:00
|
|
|
#include <linux/loop.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/kdev_t.h>
|
|
|
|
#include <limits.h>
|
2013-04-15 06:38:09 +00:00
|
|
|
#include <blkid/blkid.h>
|
2014-02-13 19:19:01 +00:00
|
|
|
#include <sys/vfs.h>
|
2015-08-26 09:04:22 +00:00
|
|
|
#include <sys/statfs.h>
|
|
|
|
#include <linux/magic.h>
|
2016-01-14 09:30:35 +00:00
|
|
|
#include <getopt.h>
|
2016-02-09 23:23:29 +00:00
|
|
|
#include <sys/utsname.h>
|
2016-11-10 16:23:17 +00:00
|
|
|
#include <linux/version.h>
|
2014-02-13 19:19:01 +00:00
|
|
|
|
2008-01-04 15:38:22 +00: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-24 19:04:49 +00:00
|
|
|
#include "volumes.h"
|
2008-03-24 19:05:44 +00:00
|
|
|
#include "ioctl.h"
|
2016-01-14 09:30:35 +00:00
|
|
|
#include "commands.h"
|
2008-03-24 19:04:49 +00:00
|
|
|
|
2011-04-21 20:24:07 +00:00
|
|
|
#ifndef BLKDISCARD
|
|
|
|
#define BLKDISCARD _IO(0x12,119)
|
|
|
|
#endif
|
|
|
|
|
2014-10-31 04:11:20 +00:00
|
|
|
static int btrfs_scan_done = 0;
|
|
|
|
|
2014-06-30 03:54:11 +00:00
|
|
|
static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
|
|
|
|
|
2016-05-26 02:56:50 +00:00
|
|
|
static int rand_seed_initlized = 0;
|
|
|
|
static unsigned short rand_seed[3];
|
|
|
|
|
2015-06-08 21:30:21 +00:00
|
|
|
const char *get_argv0_buf(void)
|
|
|
|
{
|
|
|
|
return argv0_buf;
|
|
|
|
}
|
|
|
|
|
2014-06-30 03:54:11 +00: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-18 17:05:20 +00:00
|
|
|
strncpy(argv0_buf, argv[0], sizeof(argv0_buf));
|
|
|
|
argv0_buf[sizeof(argv0_buf) - 1] = 0;
|
2014-06-30 03:54:11 +00: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 15:19:37 +00:00
|
|
|
/*
|
|
|
|
* Discard the given range in one go
|
|
|
|
*/
|
|
|
|
static int discard_range(int fd, u64 start, u64 len)
|
2011-04-21 20:24:07 +00:00
|
|
|
{
|
|
|
|
u64 range[2] = { start, len };
|
|
|
|
|
|
|
|
if (ioctl(fd, BLKDISCARD, &range) < 0)
|
|
|
|
return errno;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-27 15:19:37 +00: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-08 22:00:31 +00:00
|
|
|
static u64 reference_root_table[] = {
|
2008-01-04 15:38:22 +00:00
|
|
|
[1] = BTRFS_ROOT_TREE_OBJECTID,
|
|
|
|
[2] = BTRFS_EXTENT_TREE_OBJECTID,
|
2008-03-24 19:03:18 +00: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-08 22:00:31 +00:00
|
|
|
[6] = BTRFS_CSUM_TREE_OBJECTID,
|
2008-01-04 15:38:22 +00:00
|
|
|
};
|
|
|
|
|
2014-05-14 17:39:07 +00: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 01:01:30 +00:00
|
|
|
if (blkid_get_cache(&cache, NULL) < 0) {
|
2014-05-14 17:39:07 +00: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;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:14 +00:00
|
|
|
/*
|
|
|
|
* Reserve space from free_tree.
|
|
|
|
* The algorithm is very simple, find the first cache_extent with enough space
|
|
|
|
* and allocate from its beginning.
|
|
|
|
*/
|
|
|
|
static int reserve_free_space(struct cache_tree *free_tree, u64 len,
|
|
|
|
u64 *ret_start)
|
|
|
|
{
|
|
|
|
struct cache_extent *cache;
|
|
|
|
int found = 0;
|
|
|
|
|
2016-08-31 17:31:57 +00:00
|
|
|
ASSERT(ret_start != NULL);
|
2016-01-29 05:03:14 +00:00
|
|
|
cache = first_cache_extent(free_tree);
|
|
|
|
while (cache) {
|
|
|
|
if (cache->size > len) {
|
|
|
|
found = 1;
|
|
|
|
*ret_start = cache->start;
|
|
|
|
|
|
|
|
cache->size -= len;
|
|
|
|
if (cache->size == 0) {
|
|
|
|
remove_cache_extent(free_tree, cache);
|
|
|
|
free(cache);
|
|
|
|
} else {
|
|
|
|
cache->start += len;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cache = next_cache_extent(cache);
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return -ENOSPC;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:15 +00:00
|
|
|
static inline int write_temp_super(int fd, struct btrfs_super_block *sb,
|
|
|
|
u64 sb_bytenr)
|
|
|
|
{
|
|
|
|
u32 crc = ~(u32)0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
|
|
|
|
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
2016-09-17 23:10:23 +00:00
|
|
|
btrfs_csum_final(crc, &sb->csum[0]);
|
2016-01-29 05:03:15 +00:00
|
|
|
ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
|
|
|
|
if (ret < BTRFS_SUPER_INFO_SIZE)
|
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup temporary superblock at cfg->super_bynter
|
|
|
|
* Needed info are extracted from cfg, and root_bytenr, chunk_bytenr
|
|
|
|
*
|
|
|
|
* For now sys chunk array will be empty and dev_item is empty too.
|
|
|
|
* They will be re-initialized at temp chunk tree setup.
|
2016-08-22 14:32:24 +00:00
|
|
|
*
|
|
|
|
* The superblock signature is not valid, denotes a partially created
|
|
|
|
* filesystem, needs to be finalized.
|
2016-01-29 05:03:15 +00:00
|
|
|
*/
|
|
|
|
static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 root_bytenr, u64 chunk_bytenr)
|
|
|
|
{
|
|
|
|
unsigned char chunk_uuid[BTRFS_UUID_SIZE];
|
|
|
|
char super_buf[BTRFS_SUPER_INFO_SIZE];
|
|
|
|
struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE);
|
|
|
|
cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize);
|
|
|
|
|
2016-08-31 17:38:31 +00:00
|
|
|
if (*cfg->fs_uuid) {
|
2016-01-29 05:03:15 +00:00
|
|
|
if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) {
|
|
|
|
error("cound not parse UUID: %s", cfg->fs_uuid);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!test_uuid_unique(cfg->fs_uuid)) {
|
|
|
|
error("non-unique UUID: %s", cfg->fs_uuid);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uuid_generate(super->fsid);
|
|
|
|
uuid_unparse(super->fsid, cfg->fs_uuid);
|
|
|
|
}
|
|
|
|
uuid_generate(chunk_uuid);
|
|
|
|
uuid_unparse(chunk_uuid, cfg->chunk_uuid);
|
|
|
|
|
|
|
|
btrfs_set_super_bytenr(super, cfg->super_bytenr);
|
|
|
|
btrfs_set_super_num_devices(super, 1);
|
2016-08-22 14:32:24 +00:00
|
|
|
btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL);
|
2016-01-29 05:03:15 +00:00
|
|
|
btrfs_set_super_generation(super, 1);
|
|
|
|
btrfs_set_super_root(super, root_bytenr);
|
|
|
|
btrfs_set_super_chunk_root(super, chunk_bytenr);
|
|
|
|
btrfs_set_super_total_bytes(super, cfg->num_bytes);
|
|
|
|
/*
|
|
|
|
* Temporary filesystem will only have 6 tree roots:
|
|
|
|
* chunk tree, root tree, extent_tree, device tree, fs tree
|
|
|
|
* and csum tree.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32);
|
|
|
|
btrfs_set_super_chunk_root(super, chunk_bytenr);
|
|
|
|
btrfs_set_super_cache_generation(super, -1);
|
|
|
|
btrfs_set_super_incompat_flags(super, cfg->features);
|
|
|
|
if (cfg->label)
|
|
|
|
__strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1);
|
|
|
|
|
|
|
|
/* Sys chunk array will be re-initialized at chunk tree init time */
|
|
|
|
super->sys_chunk_array_size = 0;
|
|
|
|
|
|
|
|
ret = write_temp_super(fd, super, cfg->super_bytenr);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:16 +00:00
|
|
|
/*
|
|
|
|
* Setup an extent buffer for tree block.
|
|
|
|
*/
|
|
|
|
static int setup_temp_extent_buffer(struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 bytenr, u64 owner)
|
|
|
|
{
|
|
|
|
unsigned char fsid[BTRFS_FSID_SIZE];
|
|
|
|
unsigned char chunk_uuid[BTRFS_UUID_SIZE];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = uuid_parse(cfg->fs_uuid, fsid);
|
|
|
|
if (ret)
|
|
|
|
return -EINVAL;
|
|
|
|
ret = uuid_parse(cfg->chunk_uuid, chunk_uuid);
|
|
|
|
if (ret)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(buf->data, 0, cfg->nodesize);
|
|
|
|
buf->len = cfg->nodesize;
|
|
|
|
btrfs_set_header_bytenr(buf, bytenr);
|
|
|
|
btrfs_set_header_generation(buf, 1);
|
|
|
|
btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
|
|
|
|
btrfs_set_header_owner(buf, owner);
|
|
|
|
btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN);
|
|
|
|
write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
|
|
|
|
u64 bytenr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
|
|
|
|
|
|
|
/* Temporary extent buffer is always mapped 1:1 on disk */
|
|
|
|
ret = pwrite(fd, buf->data, buf->len, bytenr);
|
|
|
|
if (ret < buf->len)
|
|
|
|
ret = (ret < 0 ? ret : -EIO);
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert a root item for temporary tree root
|
|
|
|
*
|
|
|
|
* Only used in make_btrfs_v2().
|
|
|
|
*/
|
|
|
|
static void insert_temp_root_item(struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
int *slot, u32 *itemoff, u64 objectid,
|
|
|
|
u64 bytenr)
|
|
|
|
{
|
|
|
|
struct btrfs_root_item root_item;
|
|
|
|
struct btrfs_inode_item *inode_item;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
(*itemoff) -= sizeof(root_item);
|
|
|
|
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);
|
|
|
|
btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
|
|
|
|
btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
|
|
|
|
btrfs_set_root_refs(&root_item, 1);
|
|
|
|
btrfs_set_root_used(&root_item, cfg->nodesize);
|
|
|
|
btrfs_set_root_generation(&root_item, 1);
|
|
|
|
btrfs_set_root_bytenr(&root_item, bytenr);
|
|
|
|
|
|
|
|
memset(&disk_key, 0, sizeof(disk_key));
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, objectid);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 0);
|
|
|
|
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item));
|
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, *slot),
|
|
|
|
sizeof(root_item));
|
|
|
|
(*slot)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 root_bytenr, u64 extent_bytenr,
|
|
|
|
u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
|
|
|
|
int slot = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Provided bytenr must in ascending order, or tree root will have a
|
|
|
|
* bad key order.
|
|
|
|
*/
|
2016-08-31 18:05:13 +00:00
|
|
|
if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
|
|
|
|
dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) {
|
|
|
|
error("bad tree bytenr order: "
|
|
|
|
"root < extent %llu < %llu, "
|
|
|
|
"extent < dev %llu < %llu, "
|
|
|
|
"dev < fs %llu < %llu, "
|
|
|
|
"fs < csum %llu < %llu",
|
|
|
|
(unsigned long long)root_bytenr,
|
|
|
|
(unsigned long long)extent_bytenr,
|
|
|
|
(unsigned long long)extent_bytenr,
|
|
|
|
(unsigned long long)dev_bytenr,
|
|
|
|
(unsigned long long)dev_bytenr,
|
|
|
|
(unsigned long long)fs_bytenr,
|
|
|
|
(unsigned long long)fs_bytenr,
|
|
|
|
(unsigned long long)csum_bytenr);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-01-29 05:03:16 +00:00
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, root_bytenr,
|
|
|
|
BTRFS_ROOT_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
insert_temp_root_item(buf, cfg, &slot, &itemoff,
|
|
|
|
BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr);
|
|
|
|
insert_temp_root_item(buf, cfg, &slot, &itemoff,
|
|
|
|
BTRFS_DEV_TREE_OBJECTID, dev_bytenr);
|
|
|
|
insert_temp_root_item(buf, cfg, &slot, &itemoff,
|
|
|
|
BTRFS_FS_TREE_OBJECTID, fs_bytenr);
|
|
|
|
insert_temp_root_item(buf, cfg, &slot, &itemoff,
|
|
|
|
BTRFS_CSUM_TREE_OBJECTID, csum_bytenr);
|
|
|
|
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, root_bytenr);
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:17 +00:00
|
|
|
static int insert_temp_dev_item(int fd, struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
int *slot, u32 *itemoff)
|
|
|
|
{
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
struct btrfs_dev_item *dev_item;
|
|
|
|
char super_buf[BTRFS_SUPER_INFO_SIZE];
|
|
|
|
unsigned char dev_uuid[BTRFS_UUID_SIZE];
|
|
|
|
unsigned char fsid[BTRFS_FSID_SIZE];
|
|
|
|
struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr);
|
|
|
|
if (ret < BTRFS_SUPER_INFO_SIZE) {
|
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
(*itemoff) -= sizeof(*dev_item);
|
|
|
|
/* setup device item 1, 0 is for replace case */
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, 1);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item));
|
|
|
|
|
|
|
|
dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item);
|
|
|
|
/* Generate device uuid */
|
|
|
|
uuid_generate(dev_uuid);
|
|
|
|
write_extent_buffer(buf, dev_uuid,
|
|
|
|
(unsigned long)btrfs_device_uuid(dev_item),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
uuid_parse(cfg->fs_uuid, fsid);
|
|
|
|
write_extent_buffer(buf, fsid,
|
|
|
|
(unsigned long)btrfs_device_fsid(dev_item),
|
|
|
|
BTRFS_FSID_SIZE);
|
|
|
|
btrfs_set_device_id(buf, dev_item, 1);
|
|
|
|
btrfs_set_device_generation(buf, dev_item, 0);
|
|
|
|
btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes);
|
|
|
|
/*
|
|
|
|
* The number must match the initial SYSTEM and META chunk size
|
|
|
|
*/
|
|
|
|
btrfs_set_device_bytes_used(buf, dev_item,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE +
|
|
|
|
BTRFS_CONVERT_META_GROUP_SIZE);
|
|
|
|
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);
|
|
|
|
btrfs_set_device_type(buf, dev_item, 0);
|
|
|
|
|
|
|
|
/* Super dev_item is not complete, copy the complete one to sb */
|
|
|
|
read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item,
|
|
|
|
sizeof(*dev_item));
|
|
|
|
ret = write_temp_super(fd, super, cfg->super_bytenr);
|
|
|
|
(*slot)++;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int insert_temp_chunk_item(int fd, struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
int *slot, u32 *itemoff, u64 start, u64 len,
|
|
|
|
u64 type)
|
|
|
|
{
|
|
|
|
struct btrfs_chunk *chunk;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
char super_buf[BTRFS_SUPER_INFO_SIZE];
|
|
|
|
struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE,
|
|
|
|
cfg->super_bytenr);
|
|
|
|
if (ret < BTRFS_SUPER_INFO_SIZE) {
|
|
|
|
ret = (ret < 0 ? ret : -EIO);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
(*itemoff) -= btrfs_chunk_item_size(1);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, start);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot),
|
|
|
|
btrfs_chunk_item_size(1));
|
|
|
|
|
|
|
|
chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk);
|
|
|
|
btrfs_set_chunk_length(buf, chunk, len);
|
|
|
|
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, type);
|
|
|
|
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);
|
|
|
|
btrfs_set_chunk_num_stripes(buf, chunk, 1);
|
|
|
|
/* TODO: Support DUP profile for system chunk */
|
|
|
|
btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
|
|
|
|
/* We are doing 1:1 mapping, so start is its dev offset */
|
|
|
|
btrfs_set_stripe_offset_nr(buf, chunk, 0, start);
|
|
|
|
write_extent_buffer(buf, &sb->dev_item.uuid,
|
|
|
|
(unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
(*slot)++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's system chunk, also copy it to super block.
|
|
|
|
*/
|
|
|
|
if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
|
|
|
|
char *cur;
|
2016-12-01 13:51:14 +00:00
|
|
|
u32 array_size;
|
2016-01-29 05:03:17 +00:00
|
|
|
|
2016-12-01 13:51:14 +00:00
|
|
|
cur = (char *)sb->sys_chunk_array
|
|
|
|
+ btrfs_super_sys_array_size(sb);
|
2016-01-29 05:03:17 +00:00
|
|
|
memcpy(cur, &disk_key, sizeof(disk_key));
|
|
|
|
cur += sizeof(disk_key);
|
|
|
|
read_extent_buffer(buf, cur, (unsigned long int)chunk,
|
|
|
|
btrfs_chunk_item_size(1));
|
2016-12-01 13:51:14 +00:00
|
|
|
array_size = btrfs_super_sys_array_size(sb);
|
|
|
|
array_size += btrfs_chunk_item_size(1) +
|
2016-01-29 05:03:17 +00:00
|
|
|
sizeof(disk_key);
|
2016-12-01 13:51:14 +00:00
|
|
|
btrfs_set_super_sys_array_size(sb, array_size);
|
2016-01-29 05:03:17 +00:00
|
|
|
|
|
|
|
ret = write_temp_super(fd, sb, cfg->super_bytenr);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 sys_chunk_start, u64 meta_chunk_start,
|
|
|
|
u64 chunk_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
|
|
|
|
int slot = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Must ensure SYS chunk starts before META chunk */
|
2016-08-31 18:05:13 +00:00
|
|
|
if (meta_chunk_start < sys_chunk_start) {
|
|
|
|
error("wrong chunk order: meta < system %llu < %llu",
|
|
|
|
(unsigned long long)meta_chunk_start,
|
|
|
|
(unsigned long long)sys_chunk_start);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-01-29 05:03:17 +00:00
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr,
|
|
|
|
BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
sys_chunk_start,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE,
|
|
|
|
BTRFS_BLOCK_GROUP_SYSTEM);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
meta_chunk_start,
|
|
|
|
BTRFS_CONVERT_META_GROUP_SIZE,
|
|
|
|
BTRFS_BLOCK_GROUP_METADATA);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, chunk_bytenr);
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:18 +00:00
|
|
|
static void insert_temp_dev_extent(struct extent_buffer *buf,
|
|
|
|
int *slot, u32 *itemoff, u64 start, u64 len)
|
|
|
|
{
|
|
|
|
struct btrfs_dev_extent *dev_extent;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
(*itemoff) -= sizeof(*dev_extent);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, 1);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, start);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent));
|
|
|
|
|
|
|
|
dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent);
|
|
|
|
btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
|
|
|
|
BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_dev_extent_length(buf, dev_extent, len);
|
|
|
|
btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start);
|
|
|
|
btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
|
|
|
|
BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
(*slot)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 sys_chunk_start, u64 meta_chunk_start,
|
|
|
|
u64 dev_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
|
|
|
|
int slot = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Must ensure SYS chunk starts before META chunk */
|
2016-08-31 18:05:13 +00:00
|
|
|
if (meta_chunk_start < sys_chunk_start) {
|
|
|
|
error("wrong chunk order: meta < system %llu < %llu",
|
|
|
|
(unsigned long long)meta_chunk_start,
|
|
|
|
(unsigned long long)sys_chunk_start);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-01-29 05:03:18 +00:00
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr,
|
|
|
|
BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
|
|
|
insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start,
|
|
|
|
BTRFS_CONVERT_META_GROUP_SIZE);
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, dev_bytenr);
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:19 +00:00
|
|
|
static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 fs_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr,
|
|
|
|
BTRFS_FS_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* Temporary fs tree is completely empty.
|
|
|
|
*/
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, fs_bytenr);
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:20 +00:00
|
|
|
static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 csum_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr,
|
|
|
|
BTRFS_CSUM_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* Temporary csum tree is completely empty.
|
|
|
|
*/
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, csum_bytenr);
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:21 +00:00
|
|
|
/*
|
|
|
|
* Insert one temporary extent item.
|
|
|
|
*
|
|
|
|
* NOTE: if skinny_metadata is not enabled, this function must be called
|
|
|
|
* after all other trees are initialized.
|
|
|
|
* Or fs without skinny-metadata will be screwed up.
|
|
|
|
*/
|
|
|
|
static int insert_temp_extent_item(int fd, struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
int *slot, u32 *itemoff, u64 bytenr,
|
|
|
|
u64 ref_root)
|
|
|
|
{
|
|
|
|
struct extent_buffer *tmp;
|
|
|
|
struct btrfs_extent_item *ei;
|
|
|
|
struct btrfs_extent_inline_ref *iref;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
struct btrfs_disk_key tree_info_key;
|
|
|
|
struct btrfs_tree_block_info *info;
|
|
|
|
int itemsize;
|
|
|
|
int skinny_metadata = cfg->features &
|
|
|
|
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (skinny_metadata)
|
|
|
|
itemsize = sizeof(*ei) + sizeof(*iref);
|
|
|
|
else
|
|
|
|
itemsize = sizeof(*ei) + sizeof(*iref) +
|
|
|
|
sizeof(struct btrfs_tree_block_info);
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
*(itemoff) -= itemsize;
|
|
|
|
|
|
|
|
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);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
|
|
|
|
}
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, bytenr);
|
|
|
|
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize);
|
|
|
|
|
|
|
|
ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item);
|
|
|
|
btrfs_set_extent_refs(buf, ei, 1);
|
|
|
|
btrfs_set_extent_generation(buf, ei, 1);
|
|
|
|
btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK);
|
|
|
|
|
|
|
|
if (skinny_metadata) {
|
|
|
|
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
|
|
|
|
} else {
|
|
|
|
info = (struct btrfs_tree_block_info *)(ei + 1);
|
|
|
|
iref = (struct btrfs_extent_inline_ref *)(info + 1);
|
|
|
|
}
|
|
|
|
btrfs_set_extent_inline_ref_type(buf, iref,
|
|
|
|
BTRFS_TREE_BLOCK_REF_KEY);
|
|
|
|
btrfs_set_extent_inline_ref_offset(buf, iref, ref_root);
|
|
|
|
|
|
|
|
(*slot)++;
|
|
|
|
if (skinny_metadata)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lastly, check the tree block key by read the tree block
|
|
|
|
* Since we do 1:1 mapping for convert case, we can directly
|
|
|
|
* read the bytenr from disk
|
|
|
|
*/
|
|
|
|
tmp = malloc(sizeof(*tmp) + cfg->nodesize);
|
|
|
|
if (!tmp)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = pread(fd, tmp->data, cfg->nodesize, bytenr);
|
|
|
|
if (ret < cfg->nodesize) {
|
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (btrfs_header_nritems(tmp) == 0) {
|
|
|
|
btrfs_set_disk_key_type(&tree_info_key, 0);
|
|
|
|
btrfs_set_disk_key_objectid(&tree_info_key, 0);
|
|
|
|
btrfs_set_disk_key_offset(&tree_info_key, 0);
|
|
|
|
} else {
|
|
|
|
btrfs_item_key(tmp, &tree_info_key, 0);
|
|
|
|
}
|
|
|
|
btrfs_set_tree_block_key(buf, info, &tree_info_key);
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(tmp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insert_temp_block_group(struct extent_buffer *buf,
|
|
|
|
struct btrfs_mkfs_config *cfg,
|
|
|
|
int *slot, u32 *itemoff,
|
|
|
|
u64 bytenr, u64 len, u64 used, u64 flag)
|
|
|
|
{
|
|
|
|
struct btrfs_block_group_item bgi;
|
|
|
|
struct btrfs_disk_key disk_key;
|
|
|
|
|
|
|
|
btrfs_set_header_nritems(buf, *slot + 1);
|
|
|
|
(*itemoff) -= sizeof(bgi);
|
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY);
|
|
|
|
btrfs_set_disk_key_objectid(&disk_key, bytenr);
|
|
|
|
btrfs_set_disk_key_offset(&disk_key, len);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, *slot);
|
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi));
|
|
|
|
|
|
|
|
btrfs_set_block_group_flags(&bgi, flag);
|
|
|
|
btrfs_set_block_group_used(&bgi, used);
|
|
|
|
btrfs_set_block_group_chunk_objectid(&bgi,
|
|
|
|
BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
|
|
write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot),
|
|
|
|
sizeof(bgi));
|
|
|
|
(*slot)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
u64 chunk_bytenr, u64 root_bytenr,
|
|
|
|
u64 extent_bytenr, u64 dev_bytenr,
|
|
|
|
u64 fs_bytenr, u64 csum_bytenr)
|
|
|
|
{
|
|
|
|
struct extent_buffer *buf = NULL;
|
|
|
|
u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
|
|
|
|
int slot = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We must ensure provided bytenr are in ascending order,
|
|
|
|
* or extent tree key order will be broken.
|
|
|
|
*/
|
2016-08-31 18:05:13 +00:00
|
|
|
if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
|
|
|
|
extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
|
|
|
|
fs_bytenr < csum_bytenr)) {
|
|
|
|
error("bad tree bytenr order: "
|
|
|
|
"chunk < root %llu < %llu, "
|
|
|
|
"root < extent %llu < %llu, "
|
|
|
|
"extent < dev %llu < %llu, "
|
|
|
|
"dev < fs %llu < %llu, "
|
|
|
|
"fs < csum %llu < %llu",
|
|
|
|
(unsigned long long)chunk_bytenr,
|
|
|
|
(unsigned long long)root_bytenr,
|
|
|
|
(unsigned long long)root_bytenr,
|
|
|
|
(unsigned long long)extent_bytenr,
|
|
|
|
(unsigned long long)extent_bytenr,
|
|
|
|
(unsigned long long)dev_bytenr,
|
|
|
|
(unsigned long long)dev_bytenr,
|
|
|
|
(unsigned long long)fs_bytenr,
|
|
|
|
(unsigned long long)fs_bytenr,
|
|
|
|
(unsigned long long)csum_bytenr);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-01-29 05:03:21 +00:00
|
|
|
buf = malloc(sizeof(*buf) + cfg->nodesize);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr,
|
|
|
|
BTRFS_EXTENT_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize,
|
|
|
|
BTRFS_BLOCK_GROUP_SYSTEM);
|
|
|
|
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
root_bytenr, BTRFS_ROOT_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* 5 tree block used, root, extent, dev, fs and csum*/
|
|
|
|
insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr,
|
|
|
|
BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5,
|
|
|
|
BTRFS_BLOCK_GROUP_METADATA);
|
|
|
|
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
dev_bytenr, BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
fs_bytenr, BTRFS_FS_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
|
|
|
|
csum_bytenr, BTRFS_CSUM_TREE_OBJECTID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = write_temp_extent_buffer(fd, buf, extent_bytenr);
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-01-29 05:03:14 +00:00
|
|
|
/*
|
|
|
|
* Improved version of make_btrfs().
|
|
|
|
*
|
|
|
|
* This one will
|
|
|
|
* 1) Do chunk allocation to avoid used data
|
|
|
|
* And after this function, extent type matches chunk type
|
|
|
|
* 2) Better structured code
|
|
|
|
* No super long hand written codes to initialized all tree blocks
|
|
|
|
* Split into small blocks and reuse codes.
|
|
|
|
* TODO: Reuse tree operation facilities by introducing new flags
|
|
|
|
*/
|
|
|
|
static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
struct btrfs_convert_context *cctx)
|
|
|
|
{
|
|
|
|
struct cache_tree *free = &cctx->free;
|
|
|
|
struct cache_tree *used = &cctx->used;
|
|
|
|
u64 sys_chunk_start;
|
|
|
|
u64 meta_chunk_start;
|
2016-01-29 05:03:15 +00:00
|
|
|
/* chunk tree bytenr, in system chunk */
|
|
|
|
u64 chunk_bytenr;
|
|
|
|
/* metadata trees bytenr, in metadata chunk */
|
|
|
|
u64 root_bytenr;
|
2016-01-29 05:03:16 +00:00
|
|
|
u64 extent_bytenr;
|
|
|
|
u64 dev_bytenr;
|
|
|
|
u64 fs_bytenr;
|
|
|
|
u64 csum_bytenr;
|
2016-01-29 05:03:14 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Shouldn't happen */
|
|
|
|
BUG_ON(cache_tree_empty(used));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reserve space for temporary superblock first
|
|
|
|
* Here we allocate a little larger space, to keep later
|
|
|
|
* free space will be STRIPE_LEN aligned
|
|
|
|
*/
|
|
|
|
ret = reserve_free_space(free, BTRFS_STRIPE_LEN,
|
|
|
|
&cfg->super_bytenr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Then reserve system chunk space
|
|
|
|
* TODO: Change system group size depending on cctx->total_bytes.
|
|
|
|
* If using current 4M, it can only handle less than one TB for
|
|
|
|
* worst case and then run out of sys space.
|
|
|
|
*/
|
|
|
|
ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
|
|
|
|
&sys_chunk_start);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE,
|
|
|
|
&meta_chunk_start);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2016-01-29 05:03:15 +00:00
|
|
|
/*
|
2016-01-29 05:03:16 +00:00
|
|
|
* Allocated meta/sys chunks will be mapped 1:1 with device offset.
|
|
|
|
*
|
2016-01-29 05:03:15 +00:00
|
|
|
* Inside the allocated metadata chunk, the layout will be:
|
|
|
|
* | offset | contents |
|
|
|
|
* -------------------------------------
|
|
|
|
* | +0 | tree root |
|
|
|
|
* | +nodesize | extent root |
|
|
|
|
* | +nodesize * 2 | device root |
|
|
|
|
* | +nodesize * 3 | fs tree |
|
|
|
|
* | +nodesize * 4 | csum tree |
|
|
|
|
* -------------------------------------
|
|
|
|
* Inside the allocated system chunk, the layout will be:
|
|
|
|
* | offset | contents |
|
|
|
|
* -------------------------------------
|
|
|
|
* | +0 | chunk root |
|
|
|
|
* -------------------------------------
|
|
|
|
*/
|
|
|
|
chunk_bytenr = sys_chunk_start;
|
|
|
|
root_bytenr = meta_chunk_start;
|
2016-01-29 05:03:16 +00:00
|
|
|
extent_bytenr = meta_chunk_start + cfg->nodesize;
|
|
|
|
dev_bytenr = meta_chunk_start + cfg->nodesize * 2;
|
|
|
|
fs_bytenr = meta_chunk_start + cfg->nodesize * 3;
|
|
|
|
csum_bytenr = meta_chunk_start + cfg->nodesize * 4;
|
|
|
|
|
2016-01-29 05:03:15 +00:00
|
|
|
ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2016-01-29 05:03:16 +00:00
|
|
|
|
|
|
|
ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr,
|
|
|
|
dev_bytenr, fs_bytenr, csum_bytenr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2016-01-29 05:03:17 +00:00
|
|
|
ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
|
|
|
|
chunk_bytenr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2016-01-29 05:03:18 +00:00
|
|
|
ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
|
|
|
|
dev_bytenr);
|
2016-01-29 05:03:19 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = setup_temp_fs_tree(fd, cfg, fs_bytenr);
|
2016-01-29 05:03:20 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = setup_temp_csum_tree(fd, cfg, csum_bytenr);
|
2016-01-29 05:03:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* Setup extent tree last, since it may need to read tree block key
|
|
|
|
* for non-skinny metadata case.
|
|
|
|
*/
|
|
|
|
ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr,
|
|
|
|
extent_bytenr, dev_bytenr, fs_bytenr,
|
|
|
|
csum_bytenr);
|
2016-01-29 05:03:14 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-17 20:14:09 +00:00
|
|
|
/*
|
|
|
|
* @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
|
2016-08-22 14:32:24 +00:00
|
|
|
*
|
|
|
|
* The superblock signature is not valid, denotes a partially created
|
|
|
|
* filesystem, needs to be finalized.
|
2014-12-17 20:14:09 +00:00
|
|
|
*/
|
2016-01-29 05:03:14 +00:00
|
|
|
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
|
|
|
|
struct btrfs_convert_context *cctx)
|
2008-01-04 15:38:22 +00:00
|
|
|
{
|
|
|
|
struct btrfs_super_block super;
|
2015-11-06 17:52:09 +00:00
|
|
|
struct extent_buffer *buf;
|
2008-01-04 15:38:22 +00: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-24 19:03:18 +00:00
|
|
|
struct btrfs_chunk *chunk;
|
|
|
|
struct btrfs_dev_item *dev_item;
|
|
|
|
struct btrfs_dev_extent *dev_extent;
|
2008-04-15 19:42:08 +00:00
|
|
|
u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
|
2008-03-24 19:03:18 +00:00
|
|
|
u8 *ptr;
|
2008-01-04 15:38:22 +00:00
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
u32 itemoff;
|
|
|
|
u32 nritems = 0;
|
|
|
|
u64 first_free;
|
|
|
|
u64 ref_root;
|
2008-03-24 19:03:18 +00:00
|
|
|
u32 array_size;
|
|
|
|
u32 item_size;
|
2015-07-01 15:49:21 +00:00
|
|
|
int skinny_metadata = !!(cfg->features &
|
2013-08-17 13:48:29 +00:00
|
|
|
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
|
2015-07-01 15:49:21 +00:00
|
|
|
u64 num_bytes;
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2016-01-29 05:03:14 +00:00
|
|
|
if (cctx)
|
|
|
|
return make_convert_btrfs(fd, cfg, cctx);
|
2015-11-06 17:52:09 +00:00
|
|
|
buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
|
|
|
|
first_free &= ~((u64)cfg->sectorsize - 1);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2008-04-18 14:31:42 +00:00
|
|
|
memset(&super, 0, sizeof(super));
|
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize;
|
2016-08-31 17:38:31 +00:00
|
|
|
if (*cfg->fs_uuid) {
|
2015-07-01 15:49:21 +00:00
|
|
|
if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("cannot not parse UUID: %s", cfg->fs_uuid);
|
2014-05-14 17:39:07 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-07-01 15:49:21 +00:00
|
|
|
if (!test_uuid_unique(cfg->fs_uuid)) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("non-unique UUID: %s", cfg->fs_uuid);
|
2014-05-14 17:39:07 +00:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uuid_generate(super.fsid);
|
2016-11-03 15:27:04 +00:00
|
|
|
uuid_unparse(super.fsid, cfg->fs_uuid);
|
2014-05-14 17:39:07 +00:00
|
|
|
}
|
2008-04-15 19:42:08 +00:00
|
|
|
uuid_generate(super.dev_item.uuid);
|
|
|
|
uuid_generate(chunk_tree_uuid);
|
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_super_bytenr(&super, cfg->blocks[0]);
|
2008-03-24 19:04:49 +00:00
|
|
|
btrfs_set_super_num_devices(&super, 1);
|
2016-08-22 14:32:24 +00:00
|
|
|
btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_super_generation(&super, 1);
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_super_root(&super, cfg->blocks[1]);
|
|
|
|
btrfs_set_super_chunk_root(&super, cfg->blocks[3]);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_super_total_bytes(&super, num_bytes);
|
2015-07-01 15:49:21 +00: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 14:58:23 +00:00
|
|
|
btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
|
2008-10-29 18:07:47 +00:00
|
|
|
btrfs_set_super_chunk_root_generation(&super, 1);
|
2010-12-09 18:27:03 +00:00
|
|
|
btrfs_set_super_cache_generation(&super, -1);
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_super_incompat_flags(&super, cfg->features);
|
|
|
|
if (cfg->label)
|
2016-03-24 18:19:31 +00:00
|
|
|
__strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
/* create the tree of root objects */
|
2015-07-01 15:49:21 +00: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-08 22:00:31 +00:00
|
|
|
btrfs_set_header_nritems(buf, 4);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_header_generation(buf, 1);
|
2009-05-29 20:35:30 +00:00
|
|
|
btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
|
2013-10-01 09:59:22 +00:00
|
|
|
write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
|
|
|
|
BTRFS_FSID_SIZE);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2013-10-02 12:28:27 +00:00
|
|
|
write_extent_buffer(buf, chunk_tree_uuid,
|
2008-04-15 19:42:08 +00:00
|
|
|
btrfs_header_chunk_tree_uuid(buf),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
|
2008-01-04 15:38:22 +00: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 15:49:21 +00:00
|
|
|
btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
|
|
|
|
btrfs_set_root_refs(&root_item, 1);
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_root_used(&root_item, cfg->nodesize);
|
2008-10-29 18:07:47 +00:00
|
|
|
btrfs_set_root_generation(&root_item, 1);
|
2008-01-04 15:38:22 +00: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-24 19:04:37 +00:00
|
|
|
nritems = 0;
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item);
|
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[2]);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
|
2008-03-24 19:04:37 +00:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-01-04 15:38:22 +00:00
|
|
|
sizeof(root_item));
|
2008-03-24 19:04:37 +00:00
|
|
|
write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
|
|
|
|
nritems), sizeof(root_item));
|
|
|
|
nritems++;
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[4]);
|
2008-03-24 19:04:37 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-01-04 15:38:22 +00:00
|
|
|
sizeof(root_item));
|
2008-03-24 19:04:37 +00:00
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
|
|
|
sizeof(root_item));
|
|
|
|
nritems++;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_root_bytenr(&root_item, cfg->blocks[5]);
|
2008-03-24 19:04:37 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-03-24 19:04:37 +00:00
|
|
|
sizeof(root_item));
|
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
2008-03-24 19:03:18 +00:00
|
|
|
sizeof(root_item));
|
2008-03-24 19:04:37 +00: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-08 22:00:31 +00:00
|
|
|
itemoff = itemoff - sizeof(root_item);
|
2015-07-01 15:49:21 +00: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-08 22:00:31 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00: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-08 22:00:31 +00:00
|
|
|
sizeof(root_item));
|
|
|
|
write_extent_buffer(buf, &root_item,
|
|
|
|
btrfs_item_ptr_offset(buf, nritems),
|
|
|
|
sizeof(root_item));
|
|
|
|
nritems++;
|
|
|
|
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
/* create the items for the extent tree */
|
2015-02-02 14:51:15 +00:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 15:49:21 +00:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-24 19:04:37 +00:00
|
|
|
nritems = 0;
|
2015-07-01 15:49:21 +00: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-08 22:00:31 +00:00
|
|
|
for (i = 1; i < 7; i++) {
|
2013-08-17 13:48:29 +00:00
|
|
|
item_size = sizeof(struct btrfs_extent_item);
|
|
|
|
if (!skinny_metadata)
|
|
|
|
item_size += sizeof(struct btrfs_tree_block_info);
|
|
|
|
|
2016-09-06 11:52:09 +00:00
|
|
|
if (cfg->blocks[i] < first_free) {
|
|
|
|
error("block[%d] below first free: %llu < %llu",
|
|
|
|
i, (unsigned long long)cfg->blocks[i],
|
|
|
|
(unsigned long long)first_free);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (cfg->blocks[i] < cfg->blocks[i - 1]) {
|
|
|
|
error("blocks %d and %d in reverse order: %llu < %llu",
|
|
|
|
i, i - 1,
|
|
|
|
(unsigned long long)cfg->blocks[i],
|
|
|
|
(unsigned long long)cfg->blocks[i - 1]);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
/* create extent item */
|
2013-08-17 13:48:29 +00:00
|
|
|
itemoff -= item_size;
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
|
2013-08-17 13:48:29 +00: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 15:49:21 +00:00
|
|
|
btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
|
2013-08-17 13:48:29 +00:00
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
|
2008-01-04 15:38:22 +00:00
|
|
|
itemoff);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2013-08-17 13:48:29 +00:00
|
|
|
item_size);
|
2008-01-04 15:38:22 +00:00
|
|
|
extent_item = btrfs_item_ptr(buf, nritems,
|
|
|
|
struct btrfs_extent_item);
|
|
|
|
btrfs_set_extent_refs(buf, extent_item, 1);
|
2009-05-29 20:35:30 +00:00
|
|
|
btrfs_set_extent_generation(buf, extent_item, 1);
|
|
|
|
btrfs_set_extent_flags(buf, extent_item,
|
|
|
|
BTRFS_EXTENT_FLAG_TREE_BLOCK);
|
2008-01-04 15:38:22 +00:00
|
|
|
nritems++;
|
|
|
|
|
|
|
|
/* create extent ref */
|
|
|
|
ref_root = reference_root_table[i];
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]);
|
2009-05-29 20:35:30 +00: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 15:38:22 +00:00
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
|
2008-01-04 15:38:22 +00:00
|
|
|
itemoff);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
|
2008-01-04 15:38:22 +00:00
|
|
|
nritems++;
|
|
|
|
}
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[2]);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2008-03-24 19:03:18 +00:00
|
|
|
/* create the chunk tree */
|
2015-02-02 14:51:15 +00:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 15:49:21 +00:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-24 19:03:18 +00:00
|
|
|
nritems = 0;
|
2008-04-15 19:42:08 +00:00
|
|
|
item_size = sizeof(*dev_item);
|
2015-07-01 15:49:21 +00:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2008-04-15 19:42:08 +00: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 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
|
2008-04-15 19:42:08 +00:00
|
|
|
|
|
|
|
dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
|
|
|
|
btrfs_set_device_id(buf, dev_item, 1);
|
2008-11-18 15:40:06 +00:00
|
|
|
btrfs_set_device_generation(buf, dev_item, 0);
|
2008-04-15 19:42:08 +00: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 15:49:21 +00: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-15 19:42:08 +00: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 15:40:06 +00:00
|
|
|
write_extent_buffer(buf, super.fsid,
|
|
|
|
(unsigned long)btrfs_device_fsid(dev_item),
|
|
|
|
BTRFS_UUID_SIZE);
|
2008-04-15 19:42:08 +00: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-24 19:03:18 +00:00
|
|
|
btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
|
|
|
|
btrfs_set_item_key(buf, &disk_key, nritems);
|
2013-09-20 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
|
2008-03-24 19:03:18 +00:00
|
|
|
|
|
|
|
chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
|
2008-04-15 19:42:08 +00:00
|
|
|
btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
2008-03-24 19:03:18 +00: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 15:49:21 +00: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-24 19:03:18 +00: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-15 19:42:08 +00:00
|
|
|
nritems++;
|
|
|
|
|
|
|
|
write_extent_buffer(buf, super.dev_item.uuid,
|
|
|
|
(unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe),
|
|
|
|
BTRFS_UUID_SIZE);
|
2008-03-24 19:03:18 +00: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-24 19:04:37 +00:00
|
|
|
btrfs_set_super_sys_array_size(&super, array_size);
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[3]);
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-03-24 19:03:18 +00:00
|
|
|
|
|
|
|
/* create the device tree */
|
2015-02-02 14:51:15 +00:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 15:49:21 +00:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
2008-03-24 19:03:18 +00:00
|
|
|
nritems = 0;
|
2015-07-01 15:49:21 +00:00
|
|
|
itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) -
|
2008-03-24 19:03:18 +00: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 09:55:26 +00:00
|
|
|
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
|
|
|
|
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
|
2008-03-24 19:03:18 +00:00
|
|
|
sizeof(struct btrfs_dev_extent));
|
|
|
|
dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
|
2008-04-15 19:42:08 +00: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-24 19:03:18 +00:00
|
|
|
btrfs_set_dev_extent_length(buf, dev_extent,
|
|
|
|
BTRFS_MKFS_SYSTEM_GROUP_SIZE);
|
|
|
|
nritems++;
|
|
|
|
|
2015-07-01 15:49:21 +00:00
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[4]);
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, nritems);
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-03-24 19:03:18 +00: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-08 22:00:31 +00:00
|
|
|
/* create the FS root */
|
2015-02-02 14:51:15 +00:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 15:49:21 +00:00
|
|
|
cfg->nodesize - sizeof(struct btrfs_header));
|
|
|
|
btrfs_set_header_bytenr(buf, cfg->blocks[5]);
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
|
|
|
|
btrfs_set_header_nritems(buf, 0);
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-07-01 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00: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-08 22:00:31 +00:00
|
|
|
/* finally create the csum root */
|
2015-02-02 14:51:15 +00:00
|
|
|
memset(buf->data + sizeof(struct btrfs_header), 0,
|
2015-07-01 15:49:21 +00: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-08 22:00:31 +00: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 15:49:21 +00:00
|
|
|
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]);
|
|
|
|
if (ret != cfg->nodesize) {
|
2013-11-06 23:15:45 +00: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-08 22:00:31 +00:00
|
|
|
|
2008-03-24 19:03:18 +00:00
|
|
|
/* and write out the super block */
|
2015-11-19 14:55:05 +00:00
|
|
|
memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
|
2008-03-24 19:03:18 +00:00
|
|
|
memcpy(buf->data, &super, sizeof(super));
|
2015-11-19 14:55:05 +00:00
|
|
|
buf->len = BTRFS_SUPER_INFO_SIZE;
|
2008-12-02 14:58:23 +00:00
|
|
|
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
|
2015-11-19 14:55:05 +00:00
|
|
|
ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
|
|
|
|
if (ret != BTRFS_SUPER_INFO_SIZE) {
|
2013-11-06 23:15:45 +00:00
|
|
|
ret = (ret < 0 ? -errno : -EIO);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2013-11-06 23:15:45 +00:00
|
|
|
out:
|
2008-01-04 15:38:22 +00:00
|
|
|
free(buf);
|
2013-11-06 23:15:45 +00:00
|
|
|
return ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-10 16:23:17 +00:00
|
|
|
#define VERSION_TO_STRING3(a,b,c) #a "." #b "." #c, KERNEL_VERSION(a,b,c)
|
|
|
|
#define VERSION_TO_STRING2(a,b) #a "." #b, KERNEL_VERSION(a,b,0)
|
|
|
|
|
2016-02-09 18:36:42 +00:00
|
|
|
/*
|
|
|
|
* Feature stability status and versions: compat <= safe <= default
|
|
|
|
*/
|
2015-03-23 18:20:37 +00:00
|
|
|
static const struct btrfs_fs_feature {
|
|
|
|
const char *name;
|
|
|
|
u64 flag;
|
2016-02-09 22:45:38 +00:00
|
|
|
const char *sysfs_name;
|
2016-02-09 18:36:42 +00:00
|
|
|
/*
|
|
|
|
* Compatibility with kernel of given version. Filesystem can be
|
|
|
|
* mounted.
|
|
|
|
*/
|
2016-11-10 16:23:17 +00:00
|
|
|
const char *compat_str;
|
|
|
|
u32 compat_ver;
|
2016-02-09 18:36:42 +00:00
|
|
|
/*
|
|
|
|
* Considered safe for use, but is not on by default, even if the
|
|
|
|
* kernel supports the feature.
|
|
|
|
*/
|
2016-11-10 16:23:17 +00:00
|
|
|
const char *safe_str;
|
|
|
|
u32 safe_ver;
|
2016-02-09 18:36:42 +00:00
|
|
|
/*
|
|
|
|
* Considered safe for use and will be turned on by default if
|
|
|
|
* supported by the running kernel.
|
|
|
|
*/
|
2016-11-10 16:23:17 +00:00
|
|
|
const char *default_str;
|
|
|
|
u32 default_ver;
|
2015-03-23 18:20:37 +00:00
|
|
|
const char *desc;
|
|
|
|
} mkfs_features[] = {
|
|
|
|
{ "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS,
|
2016-02-09 22:45:38 +00:00
|
|
|
"mixed_groups",
|
2016-11-10 16:23:17 +00:00
|
|
|
VERSION_TO_STRING3(2,6,37),
|
|
|
|
VERSION_TO_STRING3(2,6,37),
|
|
|
|
NULL, 0,
|
2015-03-23 18:20:37 +00:00
|
|
|
"mixed data and metadata block groups" },
|
|
|
|
{ "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,
|
2016-02-09 22:45:38 +00:00
|
|
|
"extended_iref",
|
2016-11-10 16:23:17 +00:00
|
|
|
VERSION_TO_STRING2(3,7),
|
|
|
|
VERSION_TO_STRING2(3,12),
|
|
|
|
VERSION_TO_STRING2(3,12),
|
2015-03-23 18:20:37 +00:00
|
|
|
"increased hardlink limit per file to 65536" },
|
|
|
|
{ "raid56", BTRFS_FEATURE_INCOMPAT_RAID56,
|
2016-02-09 22:45:38 +00:00
|
|
|
"raid56",
|
2016-11-10 16:23:17 +00:00
|
|
|
VERSION_TO_STRING2(3,9),
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
2015-03-23 18:20:37 +00:00
|
|
|
"raid56 extended format" },
|
|
|
|
{ "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA,
|
2016-02-09 22:45:38 +00:00
|
|
|
"skinny_metadata",
|
2016-11-10 16:23:17 +00:00
|
|
|
VERSION_TO_STRING2(3,10),
|
|
|
|
VERSION_TO_STRING2(3,18),
|
|
|
|
VERSION_TO_STRING2(3,18),
|
2015-03-23 18:20:37 +00:00
|
|
|
"reduced-size metadata extent refs" },
|
|
|
|
{ "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES,
|
2016-02-09 22:45:38 +00:00
|
|
|
"no_holes",
|
2016-11-10 16:23:17 +00:00
|
|
|
VERSION_TO_STRING2(3,14),
|
|
|
|
VERSION_TO_STRING2(4,0),
|
|
|
|
NULL, 0,
|
2015-03-23 18:20:37 +00:00
|
|
|
"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-04 17:08:15 +00: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-23 18:20:37 +00: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-23 18:49:51 +00:00
|
|
|
void btrfs_list_all_fs_features(u64 mask_disallowed)
|
2015-03-23 18:20:37 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2015-03-23 18:49:51 +00:00
|
|
|
fprintf(stderr, "Filesystem features available:\n");
|
2015-03-23 18:20:37 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) {
|
2016-11-10 16:11:09 +00:00
|
|
|
const struct btrfs_fs_feature *feat = &mkfs_features[i];
|
2015-03-23 18:20:37 +00:00
|
|
|
|
2016-11-10 16:11:09 +00:00
|
|
|
if (feat->flag & mask_disallowed)
|
2015-03-23 18:49:51 +00:00
|
|
|
continue;
|
2016-02-09 18:36:42 +00:00
|
|
|
fprintf(stderr, "%-20s- %s (0x%llx", feat->name, feat->desc,
|
|
|
|
feat->flag);
|
|
|
|
if (feat->compat_ver)
|
2016-11-10 16:23:17 +00:00
|
|
|
fprintf(stderr, ", compat=%s", feat->compat_str);
|
2016-02-09 18:36:42 +00:00
|
|
|
if (feat->safe_ver)
|
2016-11-10 16:23:17 +00:00
|
|
|
fprintf(stderr, ", safe=%s", feat->safe_str);
|
2016-02-09 18:36:42 +00:00
|
|
|
if (feat->default_ver)
|
2016-11-10 16:23:17 +00:00
|
|
|
fprintf(stderr, ", default=%s", feat->default_str);
|
2016-02-09 18:36:42 +00:00
|
|
|
fprintf(stderr, ")\n");
|
2015-03-23 18:20:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2016-02-09 23:23:29 +00:00
|
|
|
void print_kernel_version(FILE *stream, u32 version)
|
|
|
|
{
|
|
|
|
u32 v[3];
|
|
|
|
|
|
|
|
v[0] = version & 0xFF;
|
|
|
|
v[1] = (version >> 8) & 0xFF;
|
|
|
|
v[2] = version >> 16;
|
|
|
|
fprintf(stream, "%u.%u", v[2], v[1]);
|
|
|
|
if (v[0])
|
|
|
|
fprintf(stream, ".%u", v[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_running_kernel_version(void)
|
|
|
|
{
|
|
|
|
struct utsname utsbuf;
|
|
|
|
char *tmp;
|
|
|
|
char *saveptr = NULL;
|
|
|
|
u32 version;
|
|
|
|
|
|
|
|
uname(&utsbuf);
|
|
|
|
if (strcmp(utsbuf.sysname, "Linux") != 0) {
|
|
|
|
error("unsupported system: %s", utsbuf.sysname);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* 1.2.3-4-name */
|
|
|
|
tmp = strchr(utsbuf.release, '-');
|
|
|
|
if (tmp)
|
|
|
|
*tmp = 0;
|
|
|
|
|
|
|
|
tmp = strtok_r(utsbuf.release, ".", &saveptr);
|
|
|
|
if (!string_is_numerical(tmp))
|
|
|
|
return (u32)-1;
|
|
|
|
version = atoi(tmp) << 16;
|
|
|
|
tmp = strtok_r(NULL, ".", &saveptr);
|
|
|
|
if (!string_is_numerical(tmp))
|
|
|
|
return (u32)-1;
|
|
|
|
version |= atoi(tmp) << 8;
|
|
|
|
tmp = strtok_r(NULL, ".", &saveptr);
|
|
|
|
if (tmp) {
|
|
|
|
if (!string_is_numerical(tmp))
|
|
|
|
return (u32)-1;
|
|
|
|
version |= atoi(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
2013-03-27 20:55:41 +00:00
|
|
|
u64 btrfs_device_size(int fd, struct stat *st)
|
2008-03-24 19:04:49 +00: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-16 21:02:37 +00: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-24 19:04:49 +00:00
|
|
|
{
|
2014-07-16 21:02:37 +00:00
|
|
|
off_t end = max(start, start + len);
|
2008-03-24 19:04:49 +00:00
|
|
|
|
|
|
|
#ifdef __sparc__
|
2014-07-16 21:02:37 +00:00
|
|
|
/* and don't overwrite the disk labels on sparc */
|
|
|
|
start = max(start, 1024);
|
|
|
|
end = max(end, 1024);
|
2008-03-24 19:04:49 +00:00
|
|
|
#endif
|
|
|
|
|
2014-07-16 21:02:37 +00:00
|
|
|
start = min_t(u64, start, dev_size);
|
|
|
|
end = min_t(u64, end, dev_size);
|
2008-03-24 19:04:49 +00:00
|
|
|
|
2014-07-16 21:02:37 +00:00
|
|
|
return zero_blocks(fd, start, end - start);
|
2008-03-24 19:04:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
|
2016-09-29 13:12:35 +00:00
|
|
|
struct btrfs_root *root, int fd, const char *path,
|
2015-11-03 03:23:37 +00:00
|
|
|
u64 device_total_bytes, u32 io_width, u32 io_align,
|
2008-04-18 14:31:42 +00:00
|
|
|
u32 sectorsize)
|
2008-03-24 19:04:49 +00:00
|
|
|
{
|
|
|
|
struct btrfs_super_block *disk_super;
|
2013-03-06 16:32:51 +00:00
|
|
|
struct btrfs_super_block *super = root->fs_info->super_copy;
|
2008-04-03 20:35:48 +00:00
|
|
|
struct btrfs_device *device;
|
2008-03-24 19:04:49 +00:00
|
|
|
struct btrfs_dev_item *dev_item;
|
2015-08-27 15:38:18 +00:00
|
|
|
char *buf = NULL;
|
2015-11-03 03:23:37 +00:00
|
|
|
u64 fs_total_bytes;
|
2008-03-24 19:04:49 +00:00
|
|
|
u64 num_devs;
|
|
|
|
int ret;
|
|
|
|
|
2015-11-03 03:23:36 +00:00
|
|
|
device_total_bytes = (device_total_bytes / sectorsize) * sectorsize;
|
|
|
|
|
2016-09-07 14:22:33 +00:00
|
|
|
device = calloc(1, sizeof(*device));
|
2016-09-07 14:18:31 +00:00
|
|
|
if (!device) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-09-07 14:22:33 +00:00
|
|
|
buf = calloc(1, sectorsize);
|
2016-09-07 14:18:31 +00:00
|
|
|
if (!buf) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-03-24 19:04:49 +00:00
|
|
|
|
|
|
|
disk_super = (struct btrfs_super_block *)buf;
|
|
|
|
dev_item = &disk_super->dev_item;
|
|
|
|
|
2008-04-03 20:35:48 +00: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-04 19:42:17 +00:00
|
|
|
device->fd = fd;
|
2008-11-18 15:40:06 +00:00
|
|
|
device->writeable = 1;
|
2015-11-03 03:23:37 +00:00
|
|
|
device->total_bytes = device_total_bytes;
|
2008-04-03 20:35:48 +00:00
|
|
|
device->bytes_used = 0;
|
|
|
|
device->total_ios = 0;
|
|
|
|
device->dev_root = root->fs_info->dev_root;
|
2014-12-17 20:14:08 +00:00
|
|
|
device->name = strdup(path);
|
2016-09-07 14:18:31 +00:00
|
|
|
if (!device->name) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-04-03 20:35:48 +00:00
|
|
|
|
2015-10-22 03:58:05 +00:00
|
|
|
INIT_LIST_HEAD(&device->dev_list);
|
2008-04-03 20:35:48 +00:00
|
|
|
ret = btrfs_add_device(trans, root, device);
|
2016-09-07 14:18:31 +00:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2008-03-24 19:04:49 +00:00
|
|
|
|
2015-11-03 03:23:37 +00:00
|
|
|
fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes;
|
|
|
|
btrfs_set_super_total_bytes(super, fs_total_bytes);
|
2008-03-24 19:04:49 +00: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-17 21:10:07 +00:00
|
|
|
btrfs_set_super_bytenr(disk_super, BTRFS_SUPER_INFO_OFFSET);
|
2008-04-03 20:35:48 +00: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-15 19:42:08 +00:00
|
|
|
memcpy(&dev_item->uuid, device->uuid, BTRFS_UUID_SIZE);
|
2008-03-24 19:04:49 +00:00
|
|
|
|
|
|
|
ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
|
|
|
|
BUG_ON(ret != sectorsize);
|
|
|
|
|
2016-09-07 14:22:33 +00:00
|
|
|
free(buf);
|
2008-04-03 20:35:48 +00:00
|
|
|
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
|
2008-11-18 15:40:06 +00:00
|
|
|
device->fs_devices = root->fs_info->fs_devices;
|
2008-03-24 19:04:49 +00:00
|
|
|
return 0;
|
2015-08-27 15:38:18 +00:00
|
|
|
|
2016-09-07 14:18:31 +00:00
|
|
|
out:
|
2016-09-07 14:22:33 +00:00
|
|
|
free(device);
|
|
|
|
free(buf);
|
2016-09-07 14:18:31 +00:00
|
|
|
return ret;
|
2008-03-24 19:04:49 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 14:00:12 +00:00
|
|
|
static int btrfs_wipe_existing_sb(int fd)
|
2014-10-01 23:22:09 +00:00
|
|
|
{
|
|
|
|
const char *off = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
loff_t offset;
|
|
|
|
char buf[BUFSIZ];
|
2015-09-01 14:00:12 +00:00
|
|
|
int ret = 0;
|
2014-10-01 23:22:09 +00:00
|
|
|
blkid_probe pr = NULL;
|
|
|
|
|
|
|
|
pr = blkid_new_probe();
|
|
|
|
if (!pr)
|
2015-09-01 14:00:12 +00:00
|
|
|
return -1;
|
2014-10-01 23:22:09 +00:00
|
|
|
|
2015-09-01 14:00:12 +00:00
|
|
|
if (blkid_probe_set_device(pr, fd, 0, 0)) {
|
|
|
|
ret = -1;
|
2014-10-01 23:22:09 +00:00
|
|
|
goto out;
|
2015-09-01 14:00:12 +00:00
|
|
|
}
|
2014-10-01 23:22:09 +00:00
|
|
|
|
2015-09-01 14:00:12 +00:00
|
|
|
ret = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
|
|
|
|
if (!ret)
|
|
|
|
ret = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
|
2014-10-01 23:22:09 +00:00
|
|
|
|
2015-09-01 14:00:12 +00: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-01 23:22:09 +00:00
|
|
|
goto out;
|
2015-09-01 14:00:12 +00:00
|
|
|
}
|
2014-10-01 23:22:09 +00:00
|
|
|
|
|
|
|
offset = strtoll(off, NULL, 10);
|
|
|
|
if (len > sizeof(buf))
|
|
|
|
len = sizeof(buf);
|
|
|
|
|
|
|
|
memset(buf, 0, len);
|
2015-09-01 14:00:12 +00:00
|
|
|
ret = pwrite(fd, buf, len, offset);
|
2016-03-14 13:18:32 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
error("cannot wipe existing superblock: %s", strerror(errno));
|
|
|
|
ret = -1;
|
|
|
|
} else if (ret != len) {
|
|
|
|
error("cannot wipe existing superblock: wrote %d of %zd", ret, len);
|
2015-09-01 14:00:12 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
2014-10-01 23:22:09 +00:00
|
|
|
fsync(fd);
|
|
|
|
|
|
|
|
out:
|
|
|
|
blkid_free_probe(pr);
|
2015-09-01 14:00:12 +00:00
|
|
|
return ret;
|
2014-10-01 23:22:09 +00:00
|
|
|
}
|
|
|
|
|
2016-07-27 23:47:40 +00:00
|
|
|
int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
|
|
|
|
u64 max_block_count, unsigned opflags)
|
2008-03-24 19:04:49 +00:00
|
|
|
{
|
|
|
|
u64 block_count;
|
|
|
|
struct stat st;
|
2008-12-05 17:21:31 +00:00
|
|
|
int i, ret;
|
2008-03-24 19:04:49 +00:00
|
|
|
|
|
|
|
ret = fstat(fd, &st);
|
|
|
|
if (ret < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("unable to stat %s: %s", file, strerror(errno));
|
2013-12-18 04:07:55 +00:00
|
|
|
return 1;
|
2008-03-24 19:04:49 +00:00
|
|
|
}
|
|
|
|
|
2013-03-27 20:55:41 +00:00
|
|
|
block_count = btrfs_device_size(fd, &st);
|
2008-03-24 19:04:49 +00:00
|
|
|
if (block_count == 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("unable to determine size of %s", file);
|
2013-12-18 04:07:55 +00:00
|
|
|
return 1;
|
2008-03-24 19:04:49 +00:00
|
|
|
}
|
2012-07-27 12:37:55 +00:00
|
|
|
if (max_block_count)
|
|
|
|
block_count = min(block_count, max_block_count);
|
2008-03-24 19:04:49 +00:00
|
|
|
|
2016-07-27 23:47:40 +00:00
|
|
|
if (opflags & PREP_DEVICE_DISCARD) {
|
2012-07-06 14:11:10 +00:00
|
|
|
/*
|
2013-09-20 16:42:07 +00:00
|
|
|
* We intentionally ignore errors from the discard ioctl. It
|
|
|
|
* is not necessary for the mkfs functionality but just an
|
|
|
|
* optimization.
|
2012-07-06 14:11:10 +00:00
|
|
|
*/
|
2014-04-22 13:30:27 +00:00
|
|
|
if (discard_range(fd, 0, 0) == 0) {
|
2016-07-27 23:47:40 +00:00
|
|
|
if (opflags & PREP_DEVICE_VERBOSE)
|
|
|
|
printf("Performing full device TRIM (%s) ...\n",
|
|
|
|
pretty_size(block_count));
|
2013-09-20 16:42:07 +00:00
|
|
|
discard_blocks(fd, 0, block_count);
|
|
|
|
}
|
2012-07-06 14:11:10 +00:00
|
|
|
}
|
2011-04-21 20:24:07 +00:00
|
|
|
|
2014-07-16 21:02:37 +00: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);
|
2016-07-27 23:47:40 +00:00
|
|
|
if (!ret && (opflags & PREP_DEVICE_ZERO_END))
|
2014-07-16 21:02:37 +00:00
|
|
|
ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES,
|
|
|
|
ZERO_DEV_BYTES, block_count);
|
2013-12-18 04:07:55 +00:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("failed to zero device '%s': %s", file, strerror(-ret));
|
2013-12-18 04:07:55 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2014-07-16 21:02:37 +00:00
|
|
|
|
2015-09-01 14:00:12 +00:00
|
|
|
ret = btrfs_wipe_existing_sb(fd);
|
|
|
|
if (ret < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("cannot wipe superblocks on %s", file);
|
2015-09-01 14:00:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2014-10-01 23:22:09 +00:00
|
|
|
|
2014-07-16 21:02:37 +00:00
|
|
|
*block_count_ret = block_count;
|
2008-03-24 19:04:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00: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 01:07:52 +00:00
|
|
|
time_t now = time(NULL);
|
2008-01-04 15:38:22 +00: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 14:51:15 +00:00
|
|
|
btrfs_set_stack_inode_nbytes(&inode_item, root->nodesize);
|
2012-07-17 10:18:10 +00:00
|
|
|
btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
|
2010-11-17 01:07:52 +00: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);
|
2016-10-31 16:06:15 +00:00
|
|
|
btrfs_set_stack_timespec_sec(&inode_item.otime, now);
|
2010-11-17 01:07:52 +00:00
|
|
|
btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
if (root->fs_info->tree_root == root)
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_root_dir(root->fs_info->super_copy, objectid);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
2008-07-24 16:13:32 +00:00
|
|
|
ret = btrfs_insert_inode_ref(trans, root, "..", 2, objectid, objectid, 0);
|
2008-01-04 15:38:22 +00:00
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
btrfs_set_root_dirid(&root->root_item, objectid);
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
|
|
return ret;
|
|
|
|
}
|
2008-03-24 19:05:44 +00:00
|
|
|
|
2013-03-11 23:12:59 +00: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 05:53:22 +00:00
|
|
|
int is_block_device(const char *path)
|
|
|
|
{
|
2013-03-11 23:12:59 +00:00
|
|
|
struct stat statbuf;
|
|
|
|
|
|
|
|
if (stat(path, &statbuf) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2015-09-25 15:59:43 +00:00
|
|
|
return !!S_ISBLK(statbuf.st_mode);
|
2013-03-11 23:12:59 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 05:53:22 +00: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 06:24:27 +00: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 01:16:34 +00:00
|
|
|
/*
|
|
|
|
* This function checks if the given input parameter is
|
|
|
|
* an uuid or a path
|
2015-01-05 06:24:27 +00: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 08:07:34 +00:00
|
|
|
* return BTRFS_ARG_BLKDEV: given input is block device
|
2014-12-25 01:16:34 +00: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 06:24:27 +00:00
|
|
|
if (is_reg_file(path))
|
|
|
|
return BTRFS_ARG_REG;
|
|
|
|
|
2014-12-25 01:16:34 +00: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-11 23:12:59 +00: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 05:53:21 +00:00
|
|
|
int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
|
|
|
|
{
|
2013-03-11 23:12:59 +00:00
|
|
|
int ret;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
ret = is_block_device(dev);
|
|
|
|
if (ret <= 0) {
|
|
|
|
if (!ret) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("not a block device: %s", dev);
|
2013-03-11 23:12:59 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
} else {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("cannot check %s: %s", dev, strerror(-ret));
|
2013-03-11 23:12:59 +00:00
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(dev, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
ret = -errno;
|
2016-03-14 13:36:30 +00:00
|
|
|
error("cannot open %s: %s", dev, strerror(errno));
|
2013-03-11 23:12:59 +00:00
|
|
|
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 13:23:02 +00:00
|
|
|
int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
|
2013-03-11 23:12:59 +00:00
|
|
|
{
|
2015-06-12 11:18:44 +00:00
|
|
|
char mp[PATH_MAX];
|
2015-10-12 13:23:02 +00:00
|
|
|
int ret;
|
2013-03-11 23:12:59 +00:00
|
|
|
|
2015-10-12 13:23:02 +00:00
|
|
|
if (is_block_device(path)) {
|
2013-03-11 23:12:59 +00:00
|
|
|
ret = get_btrfs_mount(path, mp, sizeof(mp));
|
|
|
|
if (ret < 0) {
|
|
|
|
/* not a mounted btrfs dev */
|
2015-10-12 13:23:02 +00:00
|
|
|
error_on(verbose, "'%s' is not a mounted btrfs device",
|
|
|
|
path);
|
2013-03-11 23:12:59 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-12 13:23:02 +00: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-11 23:12:59 +00:00
|
|
|
}
|
|
|
|
|
2015-10-12 13:23:02 +00:00
|
|
|
return ret;
|
2013-03-11 23:12:59 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 09:04:22 +00: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) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error_on(verbose, "cannot access '%s': %s", path,
|
|
|
|
strerror(errno));
|
2015-08-26 09:04:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stfs.f_type != BTRFS_SUPER_MAGIC) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error_on(verbose, "not a btrfs filesystem: %s", path);
|
2015-08-26 09:04:22 +00:00
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(path, &st) != 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error_on(verbose, "cannot access '%s': %s", path,
|
|
|
|
strerror(errno));
|
2015-08-26 09:04:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error_on(verbose, "not a directory: %s", path);
|
2015-08-26 09:04:22 +00:00
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = open_file_or_dir(path, dirstream);
|
|
|
|
if (ret < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error_on(verbose, "cannot access '%s': %s", path,
|
|
|
|
strerror(errno));
|
2015-08-26 09:04:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-09-09 02:57:02 +00:00
|
|
|
/* checks if a device is a loop device */
|
2013-08-09 20:20:47 +00:00
|
|
|
static int is_loop_device (const char* device) {
|
2010-09-09 02:57:02 +00: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-13 17:09:05 +00: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-13 18:15:27 +00:00
|
|
|
int ret;
|
2015-11-13 17:09:05 +00:00
|
|
|
struct loop_info64 lo64;
|
|
|
|
|
|
|
|
fd = open(loop_dev, O_RDONLY | O_NONBLOCK);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
2015-11-13 18:15:27 +00:00
|
|
|
ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-11-13 17:09:05 +00:00
|
|
|
|
2015-11-13 17:20:38 +00:00
|
|
|
memcpy(loop_file, lo64.lo_file_name, sizeof(lo64.lo_file_name));
|
|
|
|
loop_file[sizeof(lo64.lo_file_name)] = 0;
|
2015-11-13 17:09:05 +00:00
|
|
|
|
2015-11-13 18:15:27 +00:00
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return ret;
|
2015-11-13 17:09:05 +00:00
|
|
|
}
|
2010-09-09 02:57:02 +00:00
|
|
|
|
|
|
|
/* Takes a loop device path (e.g. /dev/loop0) and returns
|
|
|
|
* the associated file (e.g. /images/my_btrfs.img) */
|
2013-08-09 20:20:47 +00:00
|
|
|
static int resolve_loop_device(const char* loop_dev, char* loop_file,
|
|
|
|
int max_len)
|
2010-09-09 02:57:02 +00:00
|
|
|
{
|
2013-01-20 21:04:07 +00:00
|
|
|
int ret;
|
|
|
|
FILE *f;
|
|
|
|
char fmt[20];
|
|
|
|
char p[PATH_MAX];
|
|
|
|
char real_loop_dev[PATH_MAX];
|
2010-09-09 02:57:02 +00:00
|
|
|
|
2013-01-20 21:04:07 +00: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-13 17:09:05 +00: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 02:57:02 +00:00
|
|
|
return -errno;
|
2015-11-13 17:09:05 +00:00
|
|
|
}
|
2010-09-09 02:57:02 +00:00
|
|
|
|
2013-01-20 21:04:07 +00:00
|
|
|
snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
|
|
|
|
ret = fscanf(f, fmt, loop_file);
|
|
|
|
fclose(f);
|
|
|
|
if (ret == EOF)
|
2010-09-09 02:57:02 +00:00
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-03 02:13:11 +00:00
|
|
|
/*
|
|
|
|
* Checks whether a and b are identical or device
|
2010-09-09 02:57:02 +00:00
|
|
|
* files associated with the same block device
|
|
|
|
*/
|
2013-08-09 20:20:47 +00:00
|
|
|
static int is_same_blk_file(const char* a, const char* b)
|
2010-09-09 02:57:02 +00:00
|
|
|
{
|
|
|
|
struct stat st_buf_a, st_buf_b;
|
|
|
|
char real_a[PATH_MAX];
|
|
|
|
char real_b[PATH_MAX];
|
|
|
|
|
2015-01-03 02:10:55 +00:00
|
|
|
if (!realpath(a, real_a))
|
|
|
|
strncpy_null(real_a, a);
|
2013-09-20 16:52:18 +00:00
|
|
|
|
|
|
|
if (!realpath(b, real_b))
|
2015-01-03 02:10:55 +00:00
|
|
|
strncpy_null(real_b, b);
|
2010-09-09 02:57:02 +00:00
|
|
|
|
|
|
|
/* Identical path? */
|
2015-01-03 02:13:11 +00:00
|
|
|
if (strcmp(real_a, real_b) == 0)
|
2010-09-09 02:57:02 +00:00
|
|
|
return 1;
|
|
|
|
|
2015-01-03 02:13:11 +00:00
|
|
|
if (stat(a, &st_buf_a) < 0 || stat(b, &st_buf_b) < 0) {
|
2011-10-27 20:23:14 +00:00
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
2010-09-09 02:57:02 +00:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Same blockdevice? */
|
2015-01-03 02:13:11 +00: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 02:57:02 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hardlink? */
|
|
|
|
if (st_buf_a.st_dev == st_buf_b.st_dev &&
|
2015-01-03 02:13:11 +00:00
|
|
|
st_buf_a.st_ino == st_buf_b.st_ino) {
|
2010-09-09 02:57:02 +00: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-09 20:20:47 +00:00
|
|
|
static int is_same_loop_file(const char* a, const char* b)
|
2010-09-09 02:57:02 +00:00
|
|
|
{
|
|
|
|
char res_a[PATH_MAX];
|
|
|
|
char res_b[PATH_MAX];
|
2013-09-20 16:52:18 +00:00
|
|
|
const char* final_a = NULL;
|
|
|
|
const char* final_b = NULL;
|
2010-09-09 02:57:02 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Resolve a if it is a loop device */
|
|
|
|
if((ret = is_loop_device(a)) < 0) {
|
2011-10-27 20:23:14 +00:00
|
|
|
if (ret == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
return ret;
|
|
|
|
} else if (ret) {
|
2013-09-20 16:52:18 +00: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 02:57:02 +00:00
|
|
|
} else {
|
|
|
|
final_a = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resolve b if it is a loop device */
|
2011-10-27 20:23:14 +00:00
|
|
|
if ((ret = is_loop_device(b)) < 0) {
|
|
|
|
if (ret == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
return ret;
|
|
|
|
} else if (ret) {
|
2013-09-20 16:52:18 +00: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 02:57:02 +00: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-09 20:20:47 +00:00
|
|
|
static int is_existing_blk_or_reg_file(const char* filename)
|
2010-09-09 02:57:02 +00: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-09 20:20:47 +00:00
|
|
|
static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
|
|
|
|
const char* file)
|
2010-09-09 02:57:02 +00: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-04 20:43:11 +00: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 14:36:46 +00:00
|
|
|
/*
|
|
|
|
* returns 1 if the device was mounted, < 0 on error or 0 if everything
|
2010-09-09 02:57:02 +00:00
|
|
|
* is safe to continue.
|
2008-04-01 14:36:46 +00:00
|
|
|
*/
|
2010-09-09 02:57:02 +00:00
|
|
|
int check_mounted(const char* file)
|
2008-04-01 14:36:46 +00:00
|
|
|
{
|
2010-09-09 02:57:02 +00:00
|
|
|
int fd;
|
2011-07-18 12:37:23 +00:00
|
|
|
int ret;
|
2008-04-01 14:36:46 +00:00
|
|
|
|
2010-09-09 02:57:02 +00:00
|
|
|
fd = open(file, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("mount check: cannot open %s: %s", file,
|
|
|
|
strerror(errno));
|
2008-04-01 14:36:46 +00:00
|
|
|
return -errno;
|
2010-09-09 02:57:02 +00:00
|
|
|
}
|
2008-04-01 14:36:46 +00:00
|
|
|
|
2011-07-18 12:37:23 +00: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 02:57:02 +00:00
|
|
|
/* scan the initial device */
|
|
|
|
ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt,
|
2016-08-19 14:36:40 +00:00
|
|
|
&total_devs, BTRFS_SUPER_INFO_OFFSET, SBREAD_DEFAULT);
|
2010-09-09 02:57:02 +00:00
|
|
|
is_btrfs = (ret >= 0);
|
|
|
|
|
|
|
|
/* scan other devices */
|
|
|
|
if (is_btrfs && total_devs > 1) {
|
2014-11-11 11:35:58 +00:00
|
|
|
ret = btrfs_scan_devices();
|
2014-09-13 01:21:21 +00:00
|
|
|
if (ret)
|
2010-09-09 02:57:02 +00:00
|
|
|
return ret;
|
2008-04-01 14:36:46 +00:00
|
|
|
}
|
|
|
|
|
2016-05-11 23:50:36 +00:00
|
|
|
/* iterate over the list of currently mounted filesystems */
|
2013-11-07 02:53:43 +00:00
|
|
|
if ((f = setmntent ("/proc/self/mounts", "r")) == NULL)
|
2010-09-09 02:57:02 +00:00
|
|
|
return -errno;
|
|
|
|
|
2008-04-01 14:36:46 +00:00
|
|
|
while ((mnt = getmntent (f)) != NULL) {
|
2010-09-09 02:57:02 +00:00
|
|
|
if(is_btrfs) {
|
|
|
|
if(strcmp(mnt->mnt_type, "btrfs") != 0)
|
|
|
|
continue;
|
2008-04-01 14:36:46 +00:00
|
|
|
|
2010-09-09 02:57:02 +00: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 14:36:46 +00:00
|
|
|
}
|
|
|
|
|
2010-09-09 02:57:02 +00:00
|
|
|
if(ret < 0)
|
|
|
|
goto out_mntloop_err;
|
|
|
|
else if(ret)
|
|
|
|
break;
|
2008-04-01 14:36:46 +00:00
|
|
|
}
|
|
|
|
|
2010-09-09 02:57:02 +00:00
|
|
|
/* Did we find an entry in mnt table? */
|
2012-04-20 19:27:25 +00:00
|
|
|
if (mnt && size && where) {
|
2011-07-18 12:37:23 +00:00
|
|
|
strncpy(where, mnt->mnt_dir, size);
|
2012-04-20 19:27:25 +00:00
|
|
|
where[size-1] = 0;
|
|
|
|
}
|
2011-07-18 12:37:23 +00:00
|
|
|
if (fs_dev_ret)
|
|
|
|
*fs_dev_ret = fs_devices_mnt;
|
|
|
|
|
2010-09-09 02:57:02 +00:00
|
|
|
ret = (mnt != NULL);
|
|
|
|
|
|
|
|
out_mntloop_err:
|
2008-04-01 14:36:46 +00:00
|
|
|
endmntent (f);
|
2010-09-09 02:57:02 +00:00
|
|
|
|
2008-04-01 14:36:46 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-24 19:05:44 +00:00
|
|
|
struct pending_dir {
|
|
|
|
struct list_head list;
|
2013-01-23 19:31:24 +00:00
|
|
|
char name[PATH_MAX];
|
2008-03-24 19:05:44 +00:00
|
|
|
};
|
|
|
|
|
2014-10-15 00:50:38 +00:00
|
|
|
int btrfs_register_one_device(const char *fname)
|
2008-03-24 19:05:44 +00:00
|
|
|
{
|
|
|
|
struct btrfs_ioctl_vol_args args;
|
|
|
|
int fd;
|
|
|
|
int ret;
|
|
|
|
|
2014-10-15 00:46:54 +00:00
|
|
|
fd = open("/dev/btrfs-control", O_RDWR);
|
2009-01-07 19:57:11 +00:00
|
|
|
if (fd < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
warning(
|
|
|
|
"failed to open /dev/btrfs-control, skipping device registration: %s",
|
2013-03-14 03:17:19 +00:00
|
|
|
strerror(errno));
|
2014-10-15 00:50:38 +00:00
|
|
|
return -errno;
|
2009-01-07 19:57:11 +00:00
|
|
|
}
|
2015-06-12 12:36:51 +00:00
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
strncpy_null(args.name, fname);
|
2008-03-24 19:05:44 +00:00
|
|
|
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
|
2014-10-15 00:50:38 +00:00
|
|
|
if (ret < 0) {
|
2016-03-14 13:36:30 +00:00
|
|
|
error("device scan failed on '%s': %s", fname,
|
|
|
|
strerror(errno));
|
2016-01-12 10:20:18 +00:00
|
|
|
ret = -errno;
|
2010-12-20 20:06:19 +00:00
|
|
|
}
|
2008-03-24 19:05:44 +00:00
|
|
|
close(fd);
|
2014-10-15 00:50:38 +00:00
|
|
|
return ret;
|
2008-03-24 19:05:44 +00:00
|
|
|
}
|
|
|
|
|
2014-10-31 04:11:19 +00:00
|
|
|
/*
|
|
|
|
* Register all devices in the fs_uuid list created in the user
|
2014-11-11 11:35:58 +00:00
|
|
|
* space. Ensure btrfs_scan_devices() is called before this func.
|
2014-10-31 04:11:19 +00:00
|
|
|
*/
|
|
|
|
int btrfs_register_all_devices(void)
|
|
|
|
{
|
2016-03-11 00:04:35 +00:00
|
|
|
int err = 0;
|
|
|
|
int ret = 0;
|
2014-10-31 04:11:19 +00:00
|
|
|
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) {
|
2016-03-11 00:04:35 +00:00
|
|
|
if (*device->name)
|
2014-10-31 04:11:19 +00:00
|
|
|
err = btrfs_register_one_device(device->name);
|
2016-03-11 00:04:35 +00:00
|
|
|
|
|
|
|
if (err)
|
|
|
|
ret++;
|
2014-10-31 04:11:19 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-11 00:04:35 +00:00
|
|
|
|
|
|
|
return ret;
|
2014-10-31 04:11:19 +00:00
|
|
|
}
|
|
|
|
|
2008-04-18 14:45:17 +00: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;
|
2016-08-19 14:40:14 +00:00
|
|
|
/*
|
|
|
|
* Accept devices from the same filesystem, allow partially created
|
|
|
|
* structures.
|
|
|
|
*/
|
|
|
|
if (btrfs_super_magic(disk_super) != BTRFS_MAGIC &&
|
|
|
|
btrfs_super_magic(disk_super) != BTRFS_MAGIC_PARTIAL)
|
2008-04-18 14:45:17 +00:00
|
|
|
goto brelse;
|
|
|
|
|
2013-03-06 16:32:51 +00:00
|
|
|
if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid,
|
2008-04-18 14:45:17 +00:00
|
|
|
BTRFS_FSID_SIZE))
|
|
|
|
ret = 1;
|
|
|
|
brelse:
|
|
|
|
free(buf);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2008-04-22 18:06:31 +00:00
|
|
|
|
2015-02-27 18:37:24 +00:00
|
|
|
/*
|
|
|
|
* Note: this function uses a static per-thread buffer. Do not call this
|
|
|
|
* function more than 10 times within one argument list!
|
|
|
|
*/
|
2017-01-03 17:03:44 +00:00
|
|
|
const char *pretty_size_mode(u64 size, unsigned mode)
|
2015-02-27 18:37:24 +00:00
|
|
|
{
|
|
|
|
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-13 21:34:35 +00:00
|
|
|
static const char* unit_suffix_binary[] =
|
2014-04-28 16:04:48 +00:00
|
|
|
{ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
|
2014-06-13 21:34:35 +00:00
|
|
|
static const char* unit_suffix_decimal[] =
|
2014-11-04 17:22:51 +00:00
|
|
|
{ "B", "kB", "MB", "GB", "TB", "PB", "EB"};
|
2014-04-28 16:04:48 +00:00
|
|
|
|
2017-01-03 17:03:44 +00:00
|
|
|
int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mode)
|
2008-04-22 18:06:31 +00:00
|
|
|
{
|
2014-04-28 16:04:48 +00:00
|
|
|
int num_divs;
|
2008-04-22 18:06:31 +00:00
|
|
|
float fraction;
|
2017-01-03 17:03:44 +00:00
|
|
|
u64 base = 0;
|
2014-08-30 18:27:00 +00:00
|
|
|
int mult = 0;
|
2014-06-13 21:34:35 +00:00
|
|
|
const char** suffix = NULL;
|
2017-01-03 17:03:44 +00:00
|
|
|
u64 last_size;
|
|
|
|
int negative;
|
2013-07-09 20:24:43 +00:00
|
|
|
|
2014-04-28 16:04:48 +00:00
|
|
|
if (str_size == 0)
|
2013-10-07 07:21:47 +00:00
|
|
|
return 0;
|
2008-04-22 18:06:31 +00:00
|
|
|
|
2017-01-03 17:03:44 +00:00
|
|
|
negative = !!(unit_mode & UNITS_NEGATIVE);
|
|
|
|
unit_mode &= ~UNITS_NEGATIVE;
|
|
|
|
|
2014-08-30 18:27:00 +00:00
|
|
|
if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) {
|
2017-01-03 17:03:44 +00:00
|
|
|
if (negative)
|
|
|
|
snprintf(str, str_size, "%lld", size);
|
|
|
|
else
|
|
|
|
snprintf(str, str_size, "%llu", size);
|
2014-04-28 16:04:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2008-04-22 18:06:31 +00:00
|
|
|
|
2014-08-30 18:27:00 +00:00
|
|
|
if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_BINARY) {
|
2014-04-28 16:04:48 +00:00
|
|
|
base = 1024;
|
2014-08-30 18:27:00 +00:00
|
|
|
mult = 1024;
|
2014-04-28 16:04:48 +00:00
|
|
|
suffix = unit_suffix_binary;
|
2014-08-30 18:27:00 +00:00
|
|
|
} else if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_DECIMAL) {
|
2014-04-28 16:04:48 +00:00
|
|
|
base = 1000;
|
2014-08-30 18:27:00 +00:00
|
|
|
mult = 1000;
|
2014-04-28 16:04:48 +00:00
|
|
|
suffix = unit_suffix_decimal;
|
2012-10-01 20:11:27 +00:00
|
|
|
}
|
2014-04-28 16:04:48 +00:00
|
|
|
|
|
|
|
/* Unknown mode */
|
|
|
|
if (!base) {
|
2014-08-30 18:27:00 +00:00
|
|
|
fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d\n",
|
2014-04-28 16:04:48 +00:00
|
|
|
unit_mode);
|
|
|
|
assert(0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_divs = 0;
|
|
|
|
last_size = size;
|
2014-08-30 18:27:00 +00: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:
|
2017-01-03 17:03:44 +00:00
|
|
|
if (negative) {
|
|
|
|
s64 ssize = (s64)size;
|
|
|
|
s64 last_ssize = ssize;
|
|
|
|
|
|
|
|
while ((ssize < 0 ? -ssize : ssize) >= mult) {
|
|
|
|
last_ssize = ssize;
|
|
|
|
ssize /= mult;
|
|
|
|
num_divs++;
|
|
|
|
}
|
|
|
|
last_size = (u64)last_ssize;
|
|
|
|
} else {
|
|
|
|
while (size >= mult) {
|
|
|
|
last_size = size;
|
|
|
|
size /= mult;
|
|
|
|
num_divs++;
|
|
|
|
}
|
2014-08-30 18:27:00 +00:00
|
|
|
}
|
2016-03-09 02:10:55 +00: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-28 16:04:48 +00: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;
|
|
|
|
}
|
2017-01-03 17:03:44 +00:00
|
|
|
|
|
|
|
if (negative) {
|
|
|
|
fraction = (float)(s64)last_size / base;
|
|
|
|
} else {
|
|
|
|
fraction = (float)last_size / base;
|
|
|
|
}
|
2014-04-28 16:04:48 +00:00
|
|
|
|
|
|
|
return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]);
|
2008-04-22 18:06:31 +00:00
|
|
|
}
|
|
|
|
|
2013-01-25 19:27:47 +00:00
|
|
|
/*
|
2016-03-24 18:17:53 +00:00
|
|
|
* __strncpy_null - strncpy with null termination
|
2013-01-25 19:27:47 +00:00
|
|
|
* @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.
|
|
|
|
*/
|
2016-03-24 18:17:53 +00:00
|
|
|
char *__strncpy_null(char *dest, const char *src, size_t n)
|
2013-01-25 19:27:47 +00:00
|
|
|
{
|
|
|
|
strncpy(dest, src, n);
|
|
|
|
if (n > 0)
|
|
|
|
dest[n - 1] = '\0';
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2010-12-05 17:46:44 +00: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 08:32:43 +00:00
|
|
|
static int check_label(const char *input)
|
2010-12-05 17:46:44 +00:00
|
|
|
{
|
|
|
|
int len = strlen(input);
|
|
|
|
|
2013-01-30 08:32:43 +00:00
|
|
|
if (len > BTRFS_LABEL_SIZE - 1) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("label %s is too long (max %d)", input,
|
|
|
|
BTRFS_LABEL_SIZE - 1);
|
2010-12-05 17:46:44 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-06-15 19:55:25 +00:00
|
|
|
|
2013-01-30 08:32:47 +00: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) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("checking mount status of %s failed: %d", dev, ret);
|
2013-01-30 08:32:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ret > 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("device %s is mounted, use mount point", dev);
|
2013-01-30 08:32:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the super_block at the default location
|
|
|
|
* and as read-write.
|
|
|
|
*/
|
2013-10-28 18:28:43 +00:00
|
|
|
root = open_ctree(dev, 0, OPEN_CTREE_WRITES);
|
2013-01-30 08:32:47 +00:00
|
|
|
if (!root) /* errors are printed by open_ctree() */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2016-03-24 18:19:31 +00:00
|
|
|
__strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1);
|
|
|
|
|
2013-01-30 08:32:47 +00:00
|
|
|
btrfs_commit_transaction(trans, root);
|
|
|
|
|
|
|
|
/* Now we close it since we are done. */
|
|
|
|
close_ctree(root);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-22 10:40:29 +00:00
|
|
|
static int set_label_mounted(const char *mount_path, const char *labelp)
|
2013-01-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
int fd;
|
2016-03-22 10:40:29 +00:00
|
|
|
char label[BTRFS_LABEL_SIZE];
|
2013-01-30 08:32:47 +00:00
|
|
|
|
|
|
|
fd = open(mount_path, O_RDONLY | O_NOATIME);
|
|
|
|
if (fd < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to access %s: %s", mount_path, strerror(errno));
|
2013-01-30 08:32:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-03-22 10:40:29 +00:00
|
|
|
memset(label, 0, sizeof(label));
|
2016-03-24 18:19:31 +00:00
|
|
|
__strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
|
2013-01-30 08:32:47 +00:00
|
|
|
if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to set label of %s: %s", mount_path,
|
|
|
|
strerror(errno));
|
2013-01-30 08:32:47 +00:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-03-11 23:12:58 +00:00
|
|
|
close(fd);
|
2013-01-30 08:32:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-15 08:46:22 +00:00
|
|
|
int get_label_unmounted(const char *dev, char *label)
|
2013-01-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = check_mounted(dev);
|
|
|
|
if (ret < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("checking mount status of %s failed: %d", dev, ret);
|
2013-01-30 08:32:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the super_block at the default location
|
|
|
|
* and as read-only.
|
|
|
|
*/
|
|
|
|
root = open_ctree(dev, 0, 0);
|
|
|
|
if(!root)
|
|
|
|
return -1;
|
|
|
|
|
2016-03-24 18:19:31 +00:00
|
|
|
__strncpy_null(label, root->fs_info->super_copy->label,
|
|
|
|
BTRFS_LABEL_SIZE - 1);
|
2013-01-30 08:32:47 +00: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 05:30:54 +00:00
|
|
|
int get_label_mounted(const char *mount_path, char *labelp)
|
2013-01-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
char label[BTRFS_LABEL_SIZE];
|
|
|
|
int fd;
|
2015-09-15 08:46:22 +00:00
|
|
|
int ret;
|
2013-01-30 08:32:47 +00:00
|
|
|
|
|
|
|
fd = open(mount_path, O_RDONLY | O_NOATIME);
|
|
|
|
if (fd < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to access %s: %s", mount_path, strerror(errno));
|
2013-01-30 08:32:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(label, '\0', sizeof(label));
|
2015-09-15 08:46:22 +00:00
|
|
|
ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno != ENOTTY)
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to get label of %s: %s", mount_path,
|
2015-09-15 08:46:22 +00:00
|
|
|
strerror(errno));
|
|
|
|
ret = -errno;
|
2013-01-30 08:32:47 +00:00
|
|
|
close(fd);
|
2015-09-15 08:46:22 +00:00
|
|
|
return ret;
|
2013-01-30 08:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-03-24 18:19:31 +00:00
|
|
|
__strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1);
|
2013-03-11 23:12:58 +00:00
|
|
|
close(fd);
|
2013-01-30 08:32:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-12 13:41:42 +00:00
|
|
|
int get_label(const char *btrfs_dev, char *label)
|
2013-01-30 08:32:47 +00:00
|
|
|
{
|
2013-07-15 05:30:54 +00:00
|
|
|
int ret;
|
|
|
|
|
2014-07-31 03:23:44 +00:00
|
|
|
ret = is_existing_blk_or_reg_file(btrfs_dev);
|
|
|
|
if (!ret)
|
2013-07-15 05:30:54 +00:00
|
|
|
ret = get_label_mounted(btrfs_dev, label);
|
2014-07-31 03:23:44 +00:00
|
|
|
else if (ret > 0)
|
|
|
|
ret = get_label_unmounted(btrfs_dev, label);
|
2013-11-12 13:41:42 +00:00
|
|
|
|
2013-07-15 05:30:54 +00:00
|
|
|
return ret;
|
2013-01-30 08:32:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int set_label(const char *btrfs_dev, const char *label)
|
|
|
|
{
|
2014-07-31 03:23:44 +00:00
|
|
|
int ret;
|
|
|
|
|
2013-01-30 08:32:47 +00:00
|
|
|
if (check_label(label))
|
|
|
|
return -1;
|
|
|
|
|
2014-07-31 03:23:44 +00: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 08:32:47 +00:00
|
|
|
}
|
|
|
|
|
2014-06-13 02:55:57 +00: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-29 17:53:17 +00:00
|
|
|
{
|
2012-10-29 17:53:18 +00:00
|
|
|
int i;
|
2014-06-13 02:55:57 +00:00
|
|
|
|
|
|
|
for (i = 0; i <64; i++)
|
2014-07-26 16:49:55 +00:00
|
|
|
if (x << i & (1ULL << 63))
|
2014-06-13 02:55:57 +00:00
|
|
|
return 64 - i;
|
|
|
|
return 64 - i;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 parse_size(char *s)
|
|
|
|
{
|
2012-10-29 17:53:17 +00:00
|
|
|
char c;
|
2014-06-13 02:55:57 +00:00
|
|
|
char *endptr;
|
2012-10-29 17:53:17 +00:00
|
|
|
u64 mult = 1;
|
2014-06-13 02:55:57 +00:00
|
|
|
u64 ret;
|
|
|
|
|
|
|
|
if (!s) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("size value is empty");
|
2014-06-13 02:55:57 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (s[0] == '-') {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("size value '%s' is less equal than 0", s);
|
2014-06-13 02:55:57 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ret = strtoull(s, &endptr, 10);
|
|
|
|
if (endptr == s) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("size value '%s' is invalid", s);
|
2014-06-13 02:55:57 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (endptr[0] && endptr[1]) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("illegal suffix contains character '%c' in wrong position",
|
2014-06-13 02:55:57 +00:00
|
|
|
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) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("size value '%s' is too large for u64", s);
|
2014-06-13 02:55:57 +00:00
|
|
|
exit(1);
|
2012-10-29 17:53:18 +00:00
|
|
|
}
|
2014-06-13 02:55:57 +00:00
|
|
|
if (endptr[0]) {
|
|
|
|
c = tolower(endptr[0]);
|
2012-10-29 17:53:17 +00:00
|
|
|
switch (c) {
|
2012-10-29 17:53:19 +00:00
|
|
|
case 'e':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:19 +00:00
|
|
|
case 'p':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:19 +00:00
|
|
|
case 't':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:17 +00:00
|
|
|
case 'g':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:17 +00:00
|
|
|
case 'm':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:17 +00:00
|
|
|
case 'k':
|
|
|
|
mult *= 1024;
|
2013-11-06 23:15:54 +00:00
|
|
|
/* fallthrough */
|
2012-10-29 17:53:17 +00:00
|
|
|
case 'b':
|
|
|
|
break;
|
|
|
|
default:
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unknown size descriptor '%c'", c);
|
2012-10-29 17:53:17 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2014-06-13 02:55:57 +00:00
|
|
|
/* Check whether ret * mult overflow */
|
|
|
|
if (fls64(ret) + fls64(mult) - 1 > 64) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("size value '%s' is too large for u64", s);
|
2014-06-13 02:55:57 +00:00
|
|
|
exit(1);
|
2012-10-29 17:53:18 +00:00
|
|
|
}
|
2014-06-13 02:55:57 +00:00
|
|
|
ret *= mult;
|
|
|
|
return ret;
|
2012-10-29 17:53:17 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 08:26:35 +00: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 08:26:36 +00:00
|
|
|
int fd;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (p[0] == '/')
|
|
|
|
goto path;
|
2015-02-27 08:26:35 +00:00
|
|
|
|
2015-02-27 08:26:36 +00:00
|
|
|
/* Numeric format like '0/257' is the primary case */
|
2015-02-27 08:26:35 +00:00
|
|
|
if (!s) {
|
|
|
|
id = strtoull(p, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != ptr_src_end)
|
2015-02-27 08:26:36 +00:00
|
|
|
goto path;
|
2015-02-27 08:26:35 +00:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
level = strtoull(p, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != s)
|
2015-02-27 08:26:36 +00:00
|
|
|
goto path;
|
2015-02-27 08:26:35 +00:00
|
|
|
|
|
|
|
id = strtoull(s + 1, &ptr_parse_end, 10);
|
|
|
|
if (ptr_parse_end != ptr_src_end)
|
2015-02-27 08:26:36 +00:00
|
|
|
goto path;
|
2015-02-27 08:26:35 +00:00
|
|
|
|
|
|
|
return (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
|
2015-02-27 08:26:36 +00:00
|
|
|
|
|
|
|
path:
|
|
|
|
/* Path format like subv at 'my_subvol' is the fallback case */
|
2016-03-21 07:21:02 +00:00
|
|
|
ret = test_issubvolume(p);
|
2015-02-27 08:26:36 +00:00
|
|
|
if (ret < 0 || !ret)
|
|
|
|
goto err;
|
|
|
|
fd = open(p, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
goto err;
|
2016-10-31 09:34:37 +00:00
|
|
|
ret = lookup_path_rootid(fd, &id);
|
2016-07-07 09:43:38 +00:00
|
|
|
if (ret)
|
|
|
|
error("failed to lookup root id: %s", strerror(-ret));
|
2015-02-27 08:26:36 +00:00
|
|
|
close(fd);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
return id;
|
|
|
|
|
2015-02-27 08:26:35 +00:00
|
|
|
err:
|
2016-05-20 11:39:02 +00:00
|
|
|
error("invalid qgroupid or subvolume path: %s", p);
|
2015-02-27 08:26:35 +00:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2014-04-15 19:47:27 +00:00
|
|
|
int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags)
|
2013-01-28 05:22:30 +00: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 11:36:50 +00:00
|
|
|
*dirstream = opendir(fname);
|
|
|
|
if (!*dirstream)
|
2013-12-12 07:46:18 +00:00
|
|
|
return -1;
|
2013-07-15 11:36:50 +00:00
|
|
|
fd = dirfd(*dirstream);
|
2013-12-12 07:46:18 +00:00
|
|
|
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
|
2014-04-15 19:47:27 +00:00
|
|
|
fd = open(fname, open_flags);
|
2013-12-12 07:46:18 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* we set this on purpose, in case the caller output
|
|
|
|
* strerror(errno) as success
|
|
|
|
*/
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
2013-01-28 05:22:30 +00:00
|
|
|
}
|
|
|
|
if (fd < 0) {
|
2013-12-12 07:46:18 +00:00
|
|
|
fd = -1;
|
2015-06-09 13:24:51 +00:00
|
|
|
if (*dirstream) {
|
2013-07-15 11:36:50 +00:00
|
|
|
closedir(*dirstream);
|
2015-06-09 13:24:51 +00:00
|
|
|
*dirstream = NULL;
|
|
|
|
}
|
2013-01-28 05:22:30 +00:00
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
2012-05-15 10:05:44 +00:00
|
|
|
|
2014-04-15 19:47:27 +00:00
|
|
|
int open_file_or_dir(const char *fname, DIR **dirstream)
|
|
|
|
{
|
|
|
|
return open_file_or_dir3(fname, dirstream, O_RDWR);
|
|
|
|
}
|
|
|
|
|
2013-07-15 11:36:50 +00:00
|
|
|
void close_file_or_dir(int fd, DIR *dirstream)
|
|
|
|
{
|
|
|
|
if (dirstream)
|
|
|
|
closedir(dirstream);
|
|
|
|
else if (fd >= 0)
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2014-02-13 19:19:44 +00:00
|
|
|
int get_device_info(int fd, u64 devid,
|
2013-08-09 20:20:47 +00:00
|
|
|
struct btrfs_ioctl_dev_info_args *di_args)
|
2012-05-15 10:05:44 +00: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 12:35:50 +00:00
|
|
|
return ret < 0 ? -errno : 0;
|
2012-05-15 10:05:44 +00:00
|
|
|
}
|
|
|
|
|
2014-11-13 01:19:29 +00: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 04:17:40 +00: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.
|
|
|
|
*/
|
2016-09-29 13:12:35 +00:00
|
|
|
int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
|
2012-05-15 10:05:44 +00:00
|
|
|
struct btrfs_ioctl_dev_info_args **di_ret)
|
|
|
|
{
|
2013-03-12 04:17:40 +00:00
|
|
|
int fd = -1;
|
2012-05-15 10:05:44 +00:00
|
|
|
int ret = 0;
|
|
|
|
int ndevs = 0;
|
2016-11-07 14:38:24 +00:00
|
|
|
u64 last_devid = 0;
|
2015-01-06 09:30:44 +00:00
|
|
|
int replacing = 0;
|
2012-05-15 10:05:44 +00:00
|
|
|
struct btrfs_fs_devices *fs_devices_mnt = NULL;
|
|
|
|
struct btrfs_ioctl_dev_info_args *di_args;
|
2015-01-06 09:30:44 +00:00
|
|
|
struct btrfs_ioctl_dev_info_args tmp;
|
2015-06-12 11:18:44 +00:00
|
|
|
char mp[PATH_MAX];
|
2013-07-15 11:36:50 +00:00
|
|
|
DIR *dirstream = NULL;
|
2012-05-15 10:05:44 +00:00
|
|
|
|
|
|
|
memset(fi_args, 0, sizeof(*fi_args));
|
|
|
|
|
2015-08-28 14:11:30 +00:00
|
|
|
if (is_block_device(path) == 1) {
|
2014-02-24 11:43:38 +00:00
|
|
|
struct btrfs_super_block *disk_super;
|
|
|
|
char buf[BTRFS_SUPER_INFO_SIZE];
|
|
|
|
|
2013-03-12 04:17:40 +00:00
|
|
|
/* Ensure it's mounted, then set path to the mountpoint */
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
ret = -errno;
|
2016-03-14 13:36:30 +00:00
|
|
|
error("cannot open %s: %s", path, strerror(errno));
|
2013-03-12 04:17:40 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2012-05-15 10:05:44 +00:00
|
|
|
ret = check_mounted_where(fd, path, mp, sizeof(mp),
|
|
|
|
&fs_devices_mnt);
|
2013-03-12 04:17:40 +00:00
|
|
|
if (!ret) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-15 10:05:44 +00:00
|
|
|
if (ret < 0)
|
2013-03-12 04:17:40 +00:00
|
|
|
goto out;
|
|
|
|
path = mp;
|
|
|
|
/* Only fill in this one device */
|
2012-05-15 10:05:44 +00:00
|
|
|
fi_args->num_devices = 1;
|
2014-02-24 11:43:38 +00:00
|
|
|
|
|
|
|
disk_super = (struct btrfs_super_block *)buf;
|
2014-07-03 09:36:36 +00:00
|
|
|
ret = btrfs_read_dev_super(fd, disk_super,
|
|
|
|
BTRFS_SUPER_INFO_OFFSET, 0);
|
2014-02-24 11:43:38 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-11-07 14:38:24 +00:00
|
|
|
last_devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
|
|
fi_args->max_id = last_devid;
|
2014-02-24 11:43:38 +00:00
|
|
|
|
2012-05-15 10:05:44 +00:00
|
|
|
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
|
|
|
|
close(fd);
|
2013-03-12 04:17:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* at this point path must not be for a block device */
|
2013-07-15 11:36:50 +00:00
|
|
|
fd = open_file_or_dir(path, &dirstream);
|
2013-03-12 04:17:40 +00: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 01:19:29 +00: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 09:30:44 +00: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;
|
2016-11-07 14:38:24 +00:00
|
|
|
if (last_devid == 0)
|
|
|
|
last_devid++;
|
2015-01-06 09:30:44 +00:00
|
|
|
}
|
2012-05-15 10:05:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!fi_args->num_devices)
|
2013-03-12 04:17:40 +00:00
|
|
|
goto out;
|
2012-05-15 10:05:44 +00:00
|
|
|
|
2014-10-30 04:43:03 +00:00
|
|
|
di_args = *di_ret = malloc((fi_args->num_devices) * sizeof(*di_args));
|
2013-03-12 04:17:40 +00:00
|
|
|
if (!di_args) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-15 10:05:44 +00:00
|
|
|
|
2015-01-06 09:30:44 +00:00
|
|
|
if (replacing)
|
|
|
|
memcpy(di_args, &tmp, sizeof(tmp));
|
2016-11-07 14:38:24 +00:00
|
|
|
for (; last_devid <= fi_args->max_id; last_devid++) {
|
|
|
|
ret = get_device_info(fd, last_devid, &di_args[ndevs]);
|
2012-05-15 10:05:44 +00:00
|
|
|
if (ret == -ENODEV)
|
|
|
|
continue;
|
|
|
|
if (ret)
|
2013-03-12 04:17:40 +00:00
|
|
|
goto out;
|
2012-05-15 10:05:44 +00:00
|
|
|
ndevs++;
|
|
|
|
}
|
|
|
|
|
2014-02-24 11:43:39 +00: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 04:17:40 +00:00
|
|
|
out:
|
2013-07-15 11:36:50 +00:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-03-12 04:17:40 +00:00
|
|
|
return ret;
|
2012-05-15 10:05:44 +00:00
|
|
|
}
|
2013-02-14 07:53:04 +00: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-09 20:20:47 +00:00
|
|
|
static int is_swap_device(const char *file)
|
2013-02-14 07:53:04 +00: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 06:38:09 +00: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
|
|
|
|
*/
|
2016-03-22 15:56:33 +00:00
|
|
|
static int check_overwrite(const char *device)
|
2013-04-15 06:38:09 +00:00
|
|
|
{
|
|
|
|
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 08:20:56 +00: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 12:11:25 +00: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 09:36:24 +00:00
|
|
|
u64 dev_cnt, int mixed, int ssd)
|
2013-08-07 12:11:25 +00:00
|
|
|
{
|
|
|
|
u64 allowed = 0;
|
2016-09-06 15:35:06 +00:00
|
|
|
u64 profile = metadata_profile | data_profile;
|
2013-08-07 12:11:25 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
case 1:
|
|
|
|
allowed |= BTRFS_BLOCK_GROUP_DUP;
|
|
|
|
}
|
|
|
|
|
2016-09-06 15:35:06 +00:00
|
|
|
if (dev_cnt > 1 && profile & BTRFS_BLOCK_GROUP_DUP) {
|
2016-03-16 17:08:33 +00:00
|
|
|
warning("DUP is not recommended on filesystem with multiple devices");
|
2014-05-16 08:20:56 +00:00
|
|
|
}
|
2013-08-07 12:11:25 +00:00
|
|
|
if (metadata_profile & ~allowed) {
|
2015-06-10 22:51:15 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: unable to create FS with metadata profile %s "
|
2014-05-16 08:20:56 +00: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 12:11:25 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (data_profile & ~allowed) {
|
2015-06-10 22:51:15 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: unable to create FS with data profile %s "
|
2014-05-16 08:20:56 +00: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 12:11:25 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-09-06 15:35:06 +00:00
|
|
|
if (dev_cnt == 3 && profile & BTRFS_BLOCK_GROUP_RAID6) {
|
|
|
|
warning("RAID6 is not recommended on filesystem with 3 devices only");
|
|
|
|
}
|
|
|
|
if (dev_cnt == 2 && profile & BTRFS_BLOCK_GROUP_RAID5) {
|
|
|
|
warning("RAID5 is not recommended on filesystem with 2 devices only");
|
|
|
|
}
|
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 09:36:24 +00:00
|
|
|
warning_on(!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP) && ssd,
|
2015-11-26 16:56:45 +00: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 09:36:24 +00:00
|
|
|
|
2013-08-07 12:11:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-30 14:54:48 +00: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-10 22:46:30 +00:00
|
|
|
/*
|
|
|
|
* Check if a device is suitable for btrfs
|
2013-04-15 06:38:09 +00:00
|
|
|
* returns:
|
2015-06-10 22:46:30 +00:00
|
|
|
* 1: something is wrong, an error is printed
|
2013-04-15 06:38:09 +00:00
|
|
|
* 0: all is fine
|
|
|
|
*/
|
2016-03-22 15:56:33 +00:00
|
|
|
int test_dev_for_mkfs(const char *file, int force_overwrite)
|
2013-04-15 06:38:09 +00:00
|
|
|
{
|
|
|
|
int ret, fd;
|
2013-07-25 17:35:27 +00:00
|
|
|
struct stat st;
|
2013-04-15 06:38:09 +00:00
|
|
|
|
|
|
|
ret = is_swap_device(file);
|
|
|
|
if (ret < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("checking status of %s: %s", file, strerror(-ret));
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (ret == 1) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("%s is a swap device", file);
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!force_overwrite) {
|
|
|
|
if (check_overwrite(file)) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("use the -f option to force overwrite of %s",
|
|
|
|
file);
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = check_mounted(file);
|
|
|
|
if (ret < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("cannot check mount status of %s: %s", file,
|
|
|
|
strerror(-ret));
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (ret == 1) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("%s is mounted", file);
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* check if the device is busy */
|
|
|
|
fd = open(file, O_RDWR|O_EXCL);
|
|
|
|
if (fd < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to open %s: %s", file, strerror(errno));
|
2013-04-15 06:38:09 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2013-07-25 17:35:27 +00:00
|
|
|
if (fstat(fd, &st)) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("unable to stat %s: %s", file, strerror(errno));
|
2013-11-06 23:15:44 +00:00
|
|
|
close(fd);
|
2013-07-25 17:35:27 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!S_ISBLK(st.st_mode)) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("%s is not a block device", file);
|
2013-11-06 23:15:44 +00:00
|
|
|
close(fd);
|
2013-07-25 17:35:27 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2013-04-15 06:38:09 +00:00
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-15 05:30:50 +00:00
|
|
|
|
2014-11-11 11:35:58 +00:00
|
|
|
int btrfs_scan_devices(void)
|
2013-09-27 15:45:00 +00:00
|
|
|
{
|
|
|
|
int fd = -1;
|
2013-11-06 23:15:52 +00:00
|
|
|
int ret;
|
2013-09-27 15:45:00 +00: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 04:11:20 +00:00
|
|
|
if (btrfs_scan_done)
|
|
|
|
return 0;
|
|
|
|
|
2016-01-04 01:01:30 +00:00
|
|
|
if (blkid_get_cache(&cache, NULL) < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("blkid cache get failed");
|
2013-09-27 15:45:00 +00:00
|
|
|
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-06 23:15:49 +00:00
|
|
|
/* if we are here its definitely a btrfs disk*/
|
2015-01-03 02:24:10 +00:00
|
|
|
strncpy_null(path, blkid_dev_devname(dev));
|
2013-09-27 15:45:00 +00:00
|
|
|
|
|
|
|
fd = open(path, O_RDONLY);
|
2013-11-06 23:15:49 +00:00
|
|
|
if (fd < 0) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("cannot open %s: %s", path, strerror(errno));
|
2013-11-06 23:15:49 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-11-06 23:15:52 +00:00
|
|
|
ret = btrfs_scan_one_device(fd, path, &tmp_devices,
|
2016-08-19 14:36:40 +00:00
|
|
|
&num_devices, BTRFS_SUPER_INFO_OFFSET,
|
|
|
|
SBREAD_DEFAULT);
|
2013-11-06 23:15:52 +00:00
|
|
|
if (ret) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("cannot scan %s: %s", path, strerror(-ret));
|
2013-11-06 23:15:52 +00:00
|
|
|
close (fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-09-27 15:45:00 +00:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
blkid_dev_iterate_end(iter);
|
2014-03-14 03:28:11 +00:00
|
|
|
blkid_put_cache(cache);
|
2014-10-31 04:11:20 +00:00
|
|
|
|
|
|
|
btrfs_scan_done = 1;
|
|
|
|
|
2013-09-27 15:45:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-22 15:56:33 +00:00
|
|
|
int is_vol_small(const char *file)
|
2013-08-30 08:50:37 +00:00
|
|
|
{
|
|
|
|
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 08:02:10 +00:00
|
|
|
if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
|
2013-08-30 08:50:37 +00:00
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 08:27:35 +00: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.
|
|
|
|
*/
|
2016-03-22 15:56:33 +00:00
|
|
|
int ask_user(const char *question)
|
2013-09-18 08:27:35 +00:00
|
|
|
{
|
|
|
|
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-18 16:19:30 +00:00
|
|
|
|
2014-07-23 05:47:35 +00: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 07:28:28 +00: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 05:47:35 +00:00
|
|
|
int not_btrfs = 1;
|
2014-02-10 07:28:28 +00: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 02:27:48 +00:00
|
|
|
/* match found and use the latest match */
|
|
|
|
if (longest_matchlen <= len) {
|
2014-02-10 07:28:28 +00:00
|
|
|
free(longest_match);
|
|
|
|
longest_matchlen = len;
|
|
|
|
longest_match = strdup(ent->mnt_dir);
|
2014-07-23 05:47:35 +00:00
|
|
|
not_btrfs = strcmp(ent->mnt_type, "btrfs");
|
2014-02-10 07:28:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endmntent(mnttab);
|
|
|
|
|
2014-07-23 05:47:34 +00:00
|
|
|
if (!longest_match)
|
2014-02-10 07:28:28 +00:00
|
|
|
return -ENOENT;
|
2014-07-23 05:47:35 +00:00
|
|
|
if (not_btrfs) {
|
|
|
|
free(longest_match);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-02-10 07:28:28 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
*mount_root = realpath(longest_match, NULL);
|
|
|
|
if (!*mount_root)
|
|
|
|
ret = -errno;
|
|
|
|
|
|
|
|
free(longest_match);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-07-04 07:29:17 +00:00
|
|
|
|
2015-02-02 14:51:15 +00:00
|
|
|
int test_minimum_size(const char *file, u32 nodesize)
|
2014-07-04 07:29:17 +00: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 14:51:15 +00:00
|
|
|
if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
|
2014-07-04 07:29:17 +00:00
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-08-01 02:44:21 +00:00
|
|
|
|
2014-08-01 02:58:00 +00:00
|
|
|
|
|
|
|
/*
|
2016-01-13 16:45:39 +00: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 02:58:00 +00:00
|
|
|
*/
|
|
|
|
int test_isdir(const char *path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = stat(path, &st);
|
2016-01-13 16:45:39 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
2014-08-01 02:58:00 +00:00
|
|
|
|
2016-01-13 16:45:39 +00:00
|
|
|
return !!S_ISDIR(st.st_mode);
|
2014-08-01 02:58:00 +00:00
|
|
|
}
|
2014-08-30 18:27:00 +00: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-17 17:20:08 +00: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-23 17:05:31 +00:00
|
|
|
|
2016-03-22 16:01:20 +00:00
|
|
|
const char* btrfs_group_type_str(u64 flag)
|
2014-10-23 17:05:31 +00:00
|
|
|
{
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 16:01:20 +00:00
|
|
|
const char* btrfs_group_profile_str(u64 flag)
|
2014-10-23 17:05:31 +00:00
|
|
|
{
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 15:56:33 +00:00
|
|
|
u64 disk_size(const char *path)
|
2014-02-13 19:19:01 +00:00
|
|
|
{
|
|
|
|
struct statfs sfs;
|
|
|
|
|
|
|
|
if (statfs(path, &sfs) < 0)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return sfs.f_bsize * sfs.f_blocks;
|
|
|
|
}
|
2014-02-13 19:19:50 +00:00
|
|
|
|
2016-03-22 15:56:33 +00:00
|
|
|
u64 get_partition_size(const char *dev)
|
2014-02-13 19:19:50 +00:00
|
|
|
{
|
|
|
|
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-25 17:46:16 +00:00
|
|
|
|
2016-11-07 12:02:00 +00:00
|
|
|
/*
|
|
|
|
* Check if the BTRFS_IOC_TREE_SEARCH_V2 ioctl is supported on a given
|
|
|
|
* filesystem, opened at fd
|
|
|
|
*/
|
2014-11-25 17:46:16 +00: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;
|
|
|
|
|
|
|
|
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)
|
2016-11-07 12:02:00 +00:00
|
|
|
return 0;
|
2014-11-25 17:46:16 +00:00
|
|
|
else if (ret == 0)
|
2016-11-07 12:02:00 +00:00
|
|
|
return 1;
|
|
|
|
return ret;
|
2014-11-25 17:46:16 +00:00
|
|
|
}
|
2015-03-20 01:10:15 +00:00
|
|
|
|
2015-09-25 16:15:44 +00:00
|
|
|
int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features)
|
2015-03-20 01:10:15 +00:00
|
|
|
{
|
2015-02-02 14:51:15 +00:00
|
|
|
if (nodesize < sectorsize) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("illegal nodesize %u (smaller than %u)",
|
|
|
|
nodesize, sectorsize);
|
2015-03-20 01:10:15 +00:00
|
|
|
return -1;
|
2015-02-02 14:51:15 +00:00
|
|
|
} else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("illegal nodesize %u (larger than %u)",
|
2015-02-02 14:51:15 +00:00
|
|
|
nodesize, BTRFS_MAX_METADATA_BLOCKSIZE);
|
2015-03-20 01:10:15 +00:00
|
|
|
return -1;
|
2015-02-02 14:51:15 +00:00
|
|
|
} else if (nodesize & (sectorsize - 1)) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("illegal nodesize %u (not aligned to %u)",
|
2015-02-02 14:51:15 +00:00
|
|
|
nodesize, sectorsize);
|
2015-03-20 01:10:15 +00:00
|
|
|
return -1;
|
2015-09-25 16:15:44 +00:00
|
|
|
} else if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS &&
|
|
|
|
nodesize != sectorsize) {
|
2016-05-20 11:39:02 +00:00
|
|
|
error("illegal nodesize %u (not equal to %u for mixed block group)",
|
2015-09-25 16:15:44 +00:00
|
|
|
nodesize, sectorsize);
|
|
|
|
return -1;
|
2015-03-20 01:10:15 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-12 14:09:33 +00: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;
|
|
|
|
|
2016-03-24 18:17:53 +00:00
|
|
|
__strncpy_null(dest, src, destlen);
|
2015-06-12 14:09:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-08-31 11:03:41 +00: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++) {
|
2016-03-14 00:02:34 +00:00
|
|
|
if (!strcmp(argv[arg_i], "--"))
|
|
|
|
break;
|
|
|
|
|
2015-08-31 11:03:41 +00:00
|
|
|
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 14:30:56 +00:00
|
|
|
|
|
|
|
int string_is_numerical(const char *str)
|
|
|
|
{
|
2016-10-24 02:43:34 +00:00
|
|
|
if (!str)
|
|
|
|
return 0;
|
2015-10-10 14:30:56 +00:00
|
|
|
if (!(*str >= '0' && *str <= '9'))
|
|
|
|
return 0;
|
|
|
|
while (*str >= '0' && *str <= '9')
|
|
|
|
str++;
|
|
|
|
if (*str != '\0')
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2016-01-14 09:30:35 +00: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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-21 07:21:00 +00:00
|
|
|
|
2016-06-16 11:27:18 +00:00
|
|
|
/*
|
|
|
|
* Same as clean_args_no_options but pass through arguments that could look
|
|
|
|
* like short options. Eg. reisze which takes a negative resize argument like
|
|
|
|
* '-123M' .
|
|
|
|
*
|
|
|
|
* This accepts only two forms:
|
|
|
|
* - "-- option1 option2 ..."
|
|
|
|
* - "option1 option2 ..."
|
|
|
|
*/
|
|
|
|
void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
|
|
|
|
{
|
|
|
|
if (argc <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (strcmp(argv[1], "--") == 0)
|
|
|
|
optind = 2;
|
|
|
|
}
|
|
|
|
|
2016-03-21 07:21:00 +00:00
|
|
|
/* Subvolume helper functions */
|
|
|
|
/*
|
|
|
|
* 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, "..");
|
|
|
|
}
|
2016-03-21 07:21:01 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Test if path is a subvolume
|
|
|
|
* Returns:
|
|
|
|
* 0 - path exists but it is not a subvolume
|
|
|
|
* 1 - path exists and it is a subvolume
|
|
|
|
* < 0 - error
|
|
|
|
*/
|
|
|
|
int test_issubvolume(const char *path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
struct statfs stfs;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = stat(path, &st);
|
|
|
|
if (res < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
res = statfs(path, &stfs);
|
|
|
|
if (res < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
|
|
|
|
}
|
2016-03-21 07:21:03 +00:00
|
|
|
|
2016-03-22 15:56:33 +00:00
|
|
|
const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
|
2016-03-21 07:21:03 +00:00
|
|
|
{
|
|
|
|
int len = strlen(mnt);
|
|
|
|
if (!len)
|
|
|
|
return full_path;
|
|
|
|
|
|
|
|
if (mnt[len - 1] != '/')
|
|
|
|
len += 1;
|
|
|
|
|
|
|
|
return full_path + len;
|
|
|
|
}
|
2016-03-21 07:21:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns
|
|
|
|
* <0: Std error
|
|
|
|
* 0: All fine
|
|
|
|
* 1: Error; and error info printed to the terminal. Fixme.
|
|
|
|
* 2: If the fullpath is root tree instead of subvol tree
|
|
|
|
*/
|
2016-03-22 15:56:33 +00:00
|
|
|
int get_subvol_info(const char *fullpath, struct root_info *get_ri)
|
2016-03-21 07:21:04 +00:00
|
|
|
{
|
|
|
|
u64 sv_id;
|
|
|
|
int ret = 1;
|
|
|
|
int fd = -1;
|
|
|
|
int mntfd = -1;
|
|
|
|
char *mnt = NULL;
|
2016-03-22 15:56:33 +00:00
|
|
|
const char *svpath = NULL;
|
2016-03-21 07:21:04 +00:00
|
|
|
DIR *dirstream1 = NULL;
|
|
|
|
DIR *dirstream2 = NULL;
|
|
|
|
|
|
|
|
ret = test_issubvolume(fullpath);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (!ret) {
|
|
|
|
error("not a subvolume: %s", fullpath);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = find_mount_root(fullpath, &mnt);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (ret > 0) {
|
|
|
|
error("%s doesn't belong to btrfs mount point", fullpath);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ret = 1;
|
2016-03-21 07:21:05 +00:00
|
|
|
svpath = subvol_strip_mountpoint(mnt, fullpath);
|
2016-03-21 07:21:04 +00:00
|
|
|
|
|
|
|
fd = btrfs_open_dir(fullpath, &dirstream1, 1);
|
|
|
|
if (fd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = btrfs_list_get_path_rootid(fd, &sv_id);
|
2016-10-31 09:38:31 +00:00
|
|
|
if (ret)
|
2016-03-21 07:21:04 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
|
|
|
|
if (mntfd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
memset(get_ri, 0, sizeof(*get_ri));
|
|
|
|
get_ri->root_id = sv_id;
|
|
|
|
|
2016-10-31 12:56:28 +00:00
|
|
|
if (sv_id == BTRFS_FS_TREE_OBJECTID)
|
|
|
|
ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
|
|
|
|
else
|
|
|
|
ret = btrfs_get_subvol(mntfd, get_ri);
|
2016-03-21 07:21:04 +00:00
|
|
|
if (ret)
|
|
|
|
error("can't find '%s': %d", svpath, ret);
|
|
|
|
|
|
|
|
out:
|
|
|
|
close_file_or_dir(mntfd, dirstream2);
|
2016-03-24 18:31:33 +00:00
|
|
|
close_file_or_dir(fd, dirstream1);
|
2016-03-21 07:21:04 +00:00
|
|
|
free(mnt);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2016-05-26 02:56:50 +00:00
|
|
|
|
|
|
|
void init_rand_seed(u64 seed)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* only use the last 48 bits */
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
rand_seed[i] = (unsigned short)(seed ^ (unsigned short)(-1));
|
|
|
|
seed >>= 16;
|
|
|
|
}
|
|
|
|
rand_seed_initlized = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init_seed(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
int ret;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if(rand_seed_initlized)
|
|
|
|
return;
|
|
|
|
/* Use urandom as primary seed source. */
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
ret = read(fd, rand_seed, sizeof(rand_seed));
|
|
|
|
close(fd);
|
|
|
|
if (ret < sizeof(rand_seed))
|
|
|
|
goto fallback;
|
|
|
|
} else {
|
|
|
|
fallback:
|
|
|
|
/* Use time and pid as fallback seed */
|
|
|
|
warning("failed to read /dev/urandom, use time and pid as random seed");
|
|
|
|
gettimeofday(&tv, 0);
|
|
|
|
rand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
|
|
|
|
rand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
|
|
|
|
rand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
|
|
|
|
}
|
|
|
|
rand_seed_initlized = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 rand_u32(void)
|
|
|
|
{
|
|
|
|
__init_seed();
|
|
|
|
/*
|
|
|
|
* Don't use nrand48, its range is [0,2^31) The highest bit will alwasy
|
|
|
|
* be 0. Use jrand48 to include the highest bit.
|
|
|
|
*/
|
|
|
|
return (u32)jrand48(rand_seed);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int rand_range(unsigned int upper)
|
|
|
|
{
|
|
|
|
__init_seed();
|
|
|
|
/*
|
|
|
|
* Use the full 48bits to mod, which would be more uniformly
|
|
|
|
* distributed
|
|
|
|
*/
|
|
|
|
return (unsigned int)(jrand48(rand_seed) % upper);
|
|
|
|
}
|