#include <stdlib.h>
/* #include <sys/dir.h> included via androidcompat.h */
#include <fcntl.h>
+#include <limits.h>
#include <unistd.h>
#include <getopt.h>
#include <uuid/uuid.h>
bytes_used = btrfs_super_bytes_used(fs_info->super_copy);
root->fs_info->system_allocs = 1;
+ /*
+ * First temporary system chunk must match the chunk layout
+ * created in make_btrfs().
+ */
ret = btrfs_make_block_group(trans, fs_info, bytes_used,
BTRFS_BLOCK_GROUP_SYSTEM,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ BTRFS_BLOCK_RESERVED_1M_FOR_SUPER,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
allocation->system += BTRFS_MKFS_SYSTEM_GROUP_SIZE;
if (ret)
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA |
BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
if (ret)
return ret;
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_METADATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
allocation->metadata += chunk_size;
if (ret)
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
chunk_start, chunk_size);
allocation->data += chunk_size;
if (ret)
return ret;
ret = btrfs_make_block_group(trans, fs_info, 0,
- type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- chunk_start, chunk_size);
+ type, chunk_start, chunk_size);
type &= BTRFS_BLOCK_GROUP_TYPE_MASK;
if (type == BTRFS_BLOCK_GROUP_DATA) {
struct btrfs_key location;
struct btrfs_root_item root_item;
struct extent_buffer *tmp;
+ u8 uuid[BTRFS_UUID_SIZE] = {0};
int ret;
ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid);
btrfs_set_root_bytenr(&root_item, tmp->start);
btrfs_set_root_level(&root_item, btrfs_header_level(tmp));
btrfs_set_root_generation(&root_item, trans->transid);
+ /* clear uuid and o/ctime of source tree */
+ memcpy(root_item.uuid, uuid, BTRFS_UUID_SIZE);
+ btrfs_set_stack_timespec_sec(&root_item.otime, 0);
+ btrfs_set_stack_timespec_sec(&root_item.ctime, 0);
free_extent_buffer(tmp);
location.objectid = objectid;
printf(" creation:\n");
printf("\t-b|--byte-count SIZE set filesystem size to SIZE (on the first device)\n");
printf("\t-r|--rootdir DIR copy files from DIR to the image root directory\n");
+ printf("\t--shrink (with --rootdir) shrink the filled filesystem to minimal size\n");
printf("\t-K|--nodiscard do not perform whole device TRIM\n");
printf("\t-f|--force force overwrite of existing filesystem\n");
printf(" general:\n");
u32 stripesize = 4096;
int zero_end = 1;
int fd = -1;
- int ret;
+ int ret = 0;
int close_ret;
int i;
int mixed = 0;
int ssd = 0;
int force_overwrite = 0;
char *source_dir = NULL;
- int source_dir_set = 0;
+ bool source_dir_set = false;
+ bool shrink_rootdir = false;
u64 source_dir_size = 0;
u64 min_dev_size;
+ u64 shrink_size;
int dev_cnt = 0;
int saved_optind;
char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = { 0 };
while(1) {
int c;
+ enum { GETOPT_VAL_SHRINK = 257 };
static const struct option long_options[] = {
{ "alloc-start", required_argument, NULL, 'A'},
{ "byte-count", required_argument, NULL, 'b' },
{ "features", required_argument, NULL, 'O' },
{ "uuid", required_argument, NULL, 'U' },
{ "quiet", 0, NULL, 'q' },
+ { "shrink", no_argument, NULL, GETOPT_VAL_SHRINK },
{ "help", no_argument, NULL, GETOPT_VAL_HELP },
{ NULL, 0, NULL, 0}
};
goto success;
case 'r':
source_dir = optarg;
- source_dir_set = 1;
+ source_dir_set = true;
break;
case 'U':
strncpy(fs_uuid, optarg,
case 'q':
verbose = 0;
break;
+ case GETOPT_VAL_SHRINK:
+ shrink_rootdir = true;
+ break;
case GETOPT_VAL_HELP:
default:
print_usage(c != GETOPT_VAL_HELP);
error("the option -r is limited to a single device");
goto error;
}
+ if (shrink_rootdir && !source_dir_set) {
+ error("the option --shrink must be used with --rootdir");
+ goto error;
+ }
if (*fs_uuid) {
uuid_t dummy_uuid;
while (dev_cnt-- > 0) {
file = argv[optind++];
- if (is_block_device(file) == 1)
+ if (source_dir_set && is_path_exist(file) == 0)
+ ret = 0;
+ else if (is_block_device(file) == 1)
ret = test_dev_for_mkfs(file, force_overwrite);
else
ret = test_status_for_mkfs(file, force_overwrite);
* This must be done before minimal device size checks.
*/
if (source_dir_set) {
- fd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP |
- S_IWGRP | S_IROTH);
+ int oflags = O_RDWR;
+ struct stat statbuf;
+
+ if (is_path_exist(file) == 0)
+ oflags |= O_CREAT;
+
+ fd = open(file, oflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH);
if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
+ error("unable to open %s: %m", file);
goto error;
}
+ ret = fstat(fd, &statbuf);
+ if (ret < 0) {
+ error("unable to stat %s: %m", file);
+ ret = -errno;
+ goto error;
+ }
+
+ /*
+ * Block_count not specified, use file/device size first.
+ * Or we will always use source_dir_size calculated for mkfs.
+ */
+ if (!block_count)
+ block_count = btrfs_device_size(fd, &statbuf);
source_dir_size = btrfs_mkfs_size_dir(source_dir, sectorsize,
min_dev_size, metadata_profile, data_profile);
if (block_count < source_dir_size)
path = argv[i];
ret = test_minimum_size(path, min_dev_size);
if (ret < 0) {
- error("failed to check size for %s: %s",
- path, strerror(-ret));
+ error("failed to check size for %s: %m", path);
goto error;
}
if (ret > 0) {
*/
fd = open(file, O_RDWR);
if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
+ error("unable to open %s: %m", file);
goto error;
}
ret = btrfs_prepare_device(fd, file, &dev_block_count, block_count,
*/
fd = open(file, O_RDWR);
if (fd < 0) {
- error("unable to open %s: %s", file, strerror(errno));
+ error("unable to open %s: %m", file);
goto error;
}
ret = btrfs_device_already_in_root(root, fd,
error("error wihle filling filesystem: %d", ret);
goto out;
}
+ if (shrink_rootdir) {
+ ret = btrfs_mkfs_shrink_fs(fs_info, &shrink_size,
+ shrink_rootdir);
+ if (ret < 0) {
+ error("error while shrinking filesystem: %d",
+ ret);
+ goto out;
+ }
+ }
}
if (verbose) {