diff options
author | Michael Olbrich <m.olbrich@pengutronix.de> | 2019-09-22 14:24:14 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-22 14:24:14 +0000 |
commit | f69e15de765e48869fa26f9a9920557b6a9d2732 (patch) | |
tree | 007529e5ed970a560d3ad9945e4af8f03579ea3f | |
parent | 1b98d6fadfca76cc0202a2df3debc67a8fe0ee1a (diff) | |
parent | 3300b46aeb61d8eb0a723b7a3947ae4d95ba3666 (diff) | |
download | genimage-f69e15de765e48869fa26f9a9920557b6a9d2732.tar.gz genimage-f69e15de765e48869fa26f9a9920557b6a9d2732.tar.xz |
Merge branch 'master' into sparse
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | README.rst | 27 | ||||
-rw-r--r-- | config.c | 17 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | genimage.c | 18 | ||||
-rw-r--r-- | genimage.h | 9 | ||||
-rw-r--r-- | image-hd.c | 113 | ||||
-rw-r--r-- | image-rauc.c | 31 | ||||
-rwxr-xr-x | test/basic-images.test | 17 | ||||
-rw-r--r-- | test/ext2percent.config | 7 | ||||
-rw-r--r-- | test/ext2test-percent.dump | 40 | ||||
-rw-r--r-- | test/hdimage.config | 4 | ||||
-rw-r--r-- | util.c | 109 |
14 files changed, 335 insertions, 63 deletions
diff --git a/.travis.yml b/.travis.yml index 395937e..187a7f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,4 +23,4 @@ script: - ./configure - make distcheck after_failure: - - cat genimage-*/_build/test-suite.log + - find -name test-suite.log -print0 | xargs -0 cat diff --git a/Makefile.am b/Makefile.am index 3c96229..d283212 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,8 @@ EXTRA_DIST += \ test/cramfs.config \ test/ext2.config \ test/ext2test.dump \ + test/ext2percent.config \ + test/ext2test-percent.dump \ test/ext3.config \ test/ext3test.dump \ test/ext4.config \ @@ -68,7 +68,11 @@ Here are all options for images: :name: The name of this image. This is used for some image types to set the name of the image. -:size: Size of this image in bytes +:size: Size of this image in bytes. 'k', 'M' or 'G' can be used as suffix to + specify the size in multiple of 1024 etc. If the image if filled from + a mountpoint then '%' as suffix indicates a percentage. '200%' means + the resulting filesystem should be about 50% filled. Note that is is + only a rough estimate based on the original size of the content. :mountpoint: mountpoint if image refers to a filesystem image. The default is "/". The content of "${rootpath}${mountpoint}" will be used used fill the filesystem. @@ -89,12 +93,16 @@ Partition options: :offset: The offset of this partition as a total offset to the beginning of the device. -:size: The size of this partition in bytes. The last partition may have - size 0 to make this partition use the rest of the available space - on the device. +:size: The size of this partition in bytes. If the size and + autoresize are both not set then the size of the partition + image is used. :partition-type: Used by dos partition tables to specify the partition type. :image: The image file this partition shall be filled with -:autoresize: used by ubi (FIXME: do we need this? Isn't size = 0 enough) +:autoresize: Boolean specifying that the partition should be resized + automatically. For UBI volumes this means that the + ``autoresize`` flag is set. Only one volume can have this flag. + For hd images this can be used for the last partition. If set + the partition will fill the remaining space of the image. :bootable: Boolean specifying whether to set the bootable flag. :in-partition-table: Boolean specifying whether to include this partition in the partition table. @@ -181,9 +189,16 @@ Options: :extended-partition: Number of the extended partition. Contains the number of the extended partition between 1 and 4 or 0 for automatic. Defaults to 0. -:disk-signature: 32 bit integer used as disk signature (offset 440 in the MBR) +:disk-signature: 32 bit integer used as disk signature (offset 440 in the + MBR). Using a special value ``random`` will result in + using random 32 bit number. :gpt: Boolean. If true, a GPT type partion table is written. If false a DOS type partition table is written. Defaults to false. +:gpt-location: Location of the GPT table. Occasionally useful for moving the GPT + table away from where a bootloader is placed due to hardware + requirements. All partitions in the table must begin after this + table. Regardless of this setting, the GPT header will still be + placed at 512 bytes (sector 1). Defaults to 1024 bytes (sector 2). :disk-uuid: UUID string used as disk id in GPT partitioning. Defaults to a random value. @@ -152,7 +152,22 @@ unsigned long long cfg_getint_suffix(cfg_t *sec, const char *name) unsigned long long val = 0; if (str) - val = strtoul_suffix(str, NULL, 0); + val = strtoul_suffix(str, NULL, NULL); + + return val; +} + +/* + * Like cfg_getint_suffix() but allow '%' as suffix as well + */ +unsigned long long cfg_getint_suffix_percent(cfg_t *sec, const char *name, + cfg_bool_t *percent) +{ + const char *str = cfg_getstr(sec, name); + unsigned long long val = 0; + + if (str) + val = strtoul_suffix(str, NULL, percent); return val; } diff --git a/configure.ac b/configure.ac index 563b969..95e902d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ(2.60) AC_INIT([genimage], - [10], + [11], [oss-tools@pengutronix.de], [genimage], [http://www.pengutronix.de/genimage/]) @@ -157,6 +157,11 @@ static int image_setup(struct image *image) image->seen = -1; + if (image->size_is_percent) { + image->size = image_dir_size(image) * image->size / 100; + image->size_is_percent = cfg_false; + } + list_for_each_entry(part, &image->partitions, list) { struct image *child; if (!part->image) @@ -387,7 +392,7 @@ static struct mountpoint *add_mountpoint(const char *path) path_sanitized = sanitize_path(path); mp = xzalloc(sizeof(*mp)); mp->path = strdup(path); - xasprintf(&mp->mountpath, "%s/%s", tmppath(), path_sanitized); + xasprintf(&mp->mountpath, "%s/mp-%s", tmppath(), path_sanitized); list_add_tail(&mp->list, &mountpoints); free(path_sanitized); @@ -664,7 +669,8 @@ int main(int argc, char *argv[]) list_add_tail(&image->list, &images); image->file = cfg_title(imagesec); image->name = cfg_getstr(imagesec, "name"); - image->size = cfg_getint_suffix(imagesec, "size"); + image->size = cfg_getint_suffix_percent(imagesec, "size", + &image->size_is_percent); image->mountpoint = cfg_getstr(imagesec, "mountpoint"); image->exec_pre = cfg_getstr(imagesec, "exec-pre"); image->exec_post = cfg_getstr(imagesec, "exec-post"); @@ -717,6 +723,10 @@ int main(int argc, char *argv[]) if (ret) goto cleanup; + ret = collect_mountpoints(); + if (ret) + goto cleanup; + list_for_each_entry(image, &images, list) { ret = image_setup(image); if (ret) @@ -731,10 +741,6 @@ int main(int argc, char *argv[]) if (ret) goto cleanup; - ret = collect_mountpoints(); - if (ret) - goto cleanup; - list_for_each_entry(image, &images, list) { ret = setenv_image(image); if (ret) @@ -51,6 +51,7 @@ struct image { const char *name; const char *file; unsigned long long size; + cfg_bool_t size_is_percent; const char *mountpoint; const char *exec_pre; const char *exec_post; @@ -124,7 +125,8 @@ extern struct image_handler fit_handler; (type *)( (char *)__mptr - offsetof(type,member) );}) void *xzalloc(size_t n); -unsigned long long strtoul_suffix(const char *str, char **endp, int base); +unsigned long long strtoul_suffix(const char *str, char **endp, + cfg_bool_t *percent); int init_config(void); cfg_opt_t *get_confuse_opts(void); @@ -141,9 +143,12 @@ int pad_file(struct image *image, const char *infile, size_t size, unsigned char fillpattern, enum pad_mode mode); int insert_data(struct image *image, const char *data, const char *outfile, size_t size, long offset); +int extend_file(struct image *image, size_t size); int reload_partitions(struct image *image); unsigned long long cfg_getint_suffix(cfg_t *sec, const char *name); +unsigned long long cfg_getint_suffix_percent(cfg_t *sec, const char *name, + cfg_bool_t *percent); static inline const char *imageoutfile(const struct image *image) { @@ -154,6 +159,8 @@ int uuid_validate(const char *str); void uuid_parse(const char *str, unsigned char *uuid); char *uuid_random(void); +unsigned long long image_dir_size(struct image *image); + uint32_t crc32(const void *data, size_t len); #endif /* __PTX_IMAGE_H */ @@ -33,6 +33,8 @@ struct hdimage { uint32_t disksig; const char *disk_uuid; cfg_bool_t gpt; + unsigned long long gpt_location; + cfg_bool_t fill; }; struct mbr_partition_entry { @@ -78,6 +80,8 @@ struct gpt_partition_entry { #define GPT_SECTORS (1 + GPT_ENTRIES * sizeof(struct gpt_partition_entry) / 512) #define GPT_REVISION_1_0 0x00010000 +#define GPT_PE_FLAG_BOOTABLE (1 << 2) + static void hdimage_setup_chs(unsigned int lba, unsigned char *chs) { const unsigned int hpc = 255; @@ -258,14 +262,14 @@ static int hdimage_insert_gpt(struct image *image, struct list_head *partitions) header.backup_lba = htole64(image->size/512 - 1); header.last_usable_lba = htole64(image->size/512 - 1 - GPT_SECTORS); uuid_parse(hd->disk_uuid, header.disk_uuid); - header.starting_lba = htole64(2); + header.starting_lba = htole64(hd->gpt_location/512); header.number_entries = htole32(GPT_ENTRIES); header.entry_size = htole32(sizeof(struct gpt_partition_entry)); i = 0; memset(&table, 0, sizeof(table)); list_for_each_entry(part, partitions, list) { - if (header.first_usable_lba == 0) + if (header.first_usable_lba == 0 && part->in_partition_table) header.first_usable_lba = htole64(part->offset / 512); if (!part->in_partition_table) @@ -275,6 +279,7 @@ static int hdimage_insert_gpt(struct image *image, struct list_head *partitions) uuid_parse(part->partition_uuid, table[i].uuid); table[i].first_lba = htole64(part->offset/512); table[i].last_lba = htole64((part->offset + part->size)/512 - 1); + table[i].flags = part->bootable ? GPT_PE_FLAG_BOOTABLE : 0; for (j = 0; j < strlen(part->name) && j < 36; j++) table[i].name[j] = htole16(part->name[j]); i++; @@ -287,7 +292,7 @@ static int hdimage_insert_gpt(struct image *image, struct list_head *partitions) image_error(image, "failed to write GPT\n"); return ret; } - ret = insert_data(image, (char *)&table, outfile, sizeof(table), 2*512); + ret = insert_data(image, (char *)&table, outfile, sizeof(table), hd->gpt_location); if (ret) { image_error(image, "failed to write GPT table\n"); return ret; @@ -303,6 +308,7 @@ static int hdimage_insert_gpt(struct image *image, struct list_head *partitions) header.header_crc = 0; header.current_lba = htole64(image->size/512 - 1); header.backup_lba = htole64(1); + header.starting_lba = htole64(image->size/512 - GPT_SECTORS); header.header_crc = htole32(crc32(&header, sizeof(header))); ret = insert_data(image, (char *)&table, outfile, sizeof(table), image->size - GPT_SECTORS*512); @@ -376,6 +382,14 @@ static int hdimage_generate(struct image *image) } } + if (hd->fill) { + ret = extend_file(image, image->size); + if (ret) { + image_error(image, "failed to fill the image.\n"); + return ret; + } + } + if (hd->partition_table) { if (hd->gpt) { ret = hdimage_insert_gpt(image, &image->partitions); @@ -401,16 +415,19 @@ static unsigned long long roundup(unsigned long long value, unsigned long long a static int hdimage_setup(struct image *image, cfg_t *cfg) { struct partition *part; - int has_extended; + int has_extended, autoresize = 0; unsigned int partition_table_entries = 0; unsigned long long now = 0; + const char *disk_signature; struct hdimage *hd = xzalloc(sizeof(*hd)); hd->align = cfg_getint_suffix(cfg, "align"); hd->partition_table = cfg_getbool(cfg, "partition-table"); hd->extended_partition = cfg_getint(cfg, "extended-partition"); - hd->disksig = strtoul(cfg_getstr(cfg, "disk-signature"), NULL, 0); + disk_signature = cfg_getstr(cfg, "disk-signature"); hd->gpt = cfg_getbool(cfg, "gpt"); + hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location"); + hd->fill = cfg_getbool(cfg, "fill"); hd->disk_uuid = cfg_getstr(cfg, "disk-uuid"); if (hd->extended_partition > 4) { @@ -443,35 +460,30 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) hd->disk_uuid = uuid_random(); } + if (!strcmp(disk_signature, "random")) + hd->disksig = random(); + else + hd->disksig = strtoul(disk_signature, NULL, 0); + + if (hd->gpt_location == 0) { + hd->gpt_location = 2*512; + } + else if (hd->gpt_location % 512) { + image_error(image, "GPT table location (%lld) must be a " + "multiple of 1 sector (512 bytes)", hd->gpt_location); + } + partition_table_entries = 0; list_for_each_entry(part, &image->partitions, list) { - if (part->image) { - struct image *child = image_get(part->image); - if (!child) { - image_error(image, "could not find %s\n", - part->image); - return -EINVAL; - } - if (!part->size) { - if (part->in_partition_table) - part->size = roundup(child->size, hd->align); - else - part->size = child->size; - } else if (child->size > part->size) { - image_error(image, "part %s size (%lld) too small for %s (%lld)\n", - part->name, part->size, child->file, child->size); - return -EINVAL; - } - } - if (!part->size) { - image_error(image, "part %s size must not be zero\n", - part->name); + if (autoresize) { + image_error(image, "'autoresize' is only supported " + "for the last partition\n"); return -EINVAL; } - if (part->in_partition_table && (part->size % 512)) { - image_error(image, "part %s size (%lld) must be a " - "multiple of 1 sector (512 bytes)\n", - part->name, part->size); + autoresize = part->autoresize; + if (autoresize && image->size == 0) { + image_error(image, "the images size must be specified " + "when using a 'autoresize' partition\n"); return -EINVAL; } if (hd->gpt) { @@ -535,6 +547,45 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) } part->offset = roundup(now, hd->align); } + if (autoresize) { + long long partsize = image->size - now; + if (hd->gpt) + partsize -= GPT_SECTORS * 512; + if (partsize < 0) { + image_error(image, "partitions exceed device size\n"); + return -EINVAL; + } + part->size = partsize; + } + if (part->image) { + struct image *child = image_get(part->image); + if (!child) { + image_error(image, "could not find %s\n", + part->image); + return -EINVAL; + } + if (!part->size) { + if (part->in_partition_table) + part->size = roundup(child->size, hd->align); + else + part->size = child->size; + } else if (child->size > part->size) { + image_error(image, "part %s size (%lld) too small for %s (%lld)\n", + part->name, part->size, child->file, child->size); + return -EINVAL; + } + } + if (!part->size) { + image_error(image, "part %s size must not be zero\n", + part->name); + return -EINVAL; + } + if (part->in_partition_table && (part->size % 512)) { + image_error(image, "part %s size (%lld) must be a " + "multiple of 1 sector (512 bytes)\n", + part->name, part->size); + return -EINVAL; + } now = part->offset + part->size; } @@ -561,6 +612,8 @@ cfg_opt_t hdimage_opts[] = { CFG_BOOL("partition-table", cfg_true, CFGF_NONE), CFG_INT("extended-partition", 0, CFGF_NONE), CFG_BOOL("gpt", cfg_false, CFGF_NONE), + CFG_STR("gpt-location", NULL, CFGF_NONE), + CFG_BOOL("fill", cfg_false, CFGF_NONE), CFG_END() }; diff --git a/image-rauc.c b/image-rauc.c index 79aab10..c4195a3 100644 --- a/image-rauc.c +++ b/image-rauc.c @@ -91,29 +91,36 @@ static int rauc_generate(struct image *image) static int rauc_parse(struct image *image, cfg_t *cfg) { + const char *pkcs11_prefix = "pkcs11:"; unsigned int i; unsigned int num_files; struct partition *part; + char *part_image_key; + char *part_image_cert; - part = xzalloc(sizeof *part); - part->image = cfg_getstr(image->imagesec, "key"); - if (!part->image) { + part_image_key = cfg_getstr(image->imagesec, "key"); + if (!part_image_key) { image_error(image, "Mandatory 'key' option is missing!\n"); - free(part); return -EINVAL; } - part->partition_type = RAUC_KEY; - list_add_tail(&part->list, &image->partitions); + if (strncmp(pkcs11_prefix, part_image_key, strlen(pkcs11_prefix))) { + part = xzalloc(sizeof *part); + part->image = part_image_key; + part->partition_type = RAUC_KEY; + list_add_tail(&part->list, &image->partitions); + } - part = xzalloc(sizeof *part); - part->image = cfg_getstr(image->imagesec, "cert"); - if (!part->image) { + part_image_cert = cfg_getstr(image->imagesec, "cert"); + if (!part_image_cert) { image_error(image, "Mandatory 'cert' option is missing!\n"); - free(part); return -EINVAL; } - part->partition_type = RAUC_CERT; - list_add_tail(&part->list, &image->partitions); + if (strncmp(pkcs11_prefix, part_image_cert, strlen(pkcs11_prefix))) { + part = xzalloc(sizeof *part); + part->image = part_image_cert; + part->partition_type = RAUC_CERT; + list_add_tail(&part->list, &image->partitions); + } num_files = cfg_size(cfg, "file"); for (i = 0; i < num_files; i++) { diff --git a/test/basic-images.test b/test/basic-images.test index e1cd784..d35898f 100755 --- a/test/basic-images.test +++ b/test/basic-images.test @@ -89,6 +89,14 @@ check_size() { fi } +sfdisk_validate() { + if [ -n "$(sfdisk -q -V "${1}" 2>&1 | grep -v unallocated)" ]; then + echo "'sfdisk -V' failed with:" + sfdisk -V "${1}" 2>&1 + return 1 + fi +} + get_disk_usage() { local file="${1}" if [ ! -f "${file}" ]; then @@ -176,6 +184,11 @@ test_expect_success genext2fs,e2fsck "ext2" " check_ext images/test.ext2 ext2test 4194304 " +test_expect_success genext2fs,e2fsck "ext2percent" " + run_genimage ext2percent.config test.ext2 && + check_ext images/test.ext2 ext2test-percent 69632 +" + test_expect_success genext2fs,e2fsck "ext3" " run_genimage ext3.config test.ext3 && check_ext images/test.ext3 ext3test 4194304 @@ -212,7 +225,8 @@ exec_test_set_prereq sfdisk test_expect_success fdisk,sfdisk "hdimage" " setup_test_images && run_genimage hdimage.config test.hdimage && - check_size images/test.hdimage 9442816 && + check_size images/test.hdimage 10485760 && + sfdisk_validate images/test.hdimage && check_disk_usage_range images/test.hdimage 40960 57344 && # check the this identifier fdisk -l images/test.hdimage | grep identifier: > hdimage.fdisk && @@ -240,6 +254,7 @@ test_expect_success fdisk-gpt,sfdisk-gpt "hdimage4" " setup_test_images && run_genimage hdimage4.config test.hdimage && check_size images/test.hdimage 7356928 && + sfdisk_validate images/test.hdimage && # check the this identifier fdisk -l images/test.hdimage | grep identifier: > hdimage4.fdisk && # check partitions; filter output to handle different sfdisk versions diff --git a/test/ext2percent.config b/test/ext2percent.config new file mode 100644 index 0000000..f9cd848 --- /dev/null +++ b/test/ext2percent.config @@ -0,0 +1,7 @@ +image test.ext2 { + ext2 { + label = "ext2test" + fs-timestamp = "20000101000000" + } + size = 100% +} diff --git a/test/ext2test-percent.dump b/test/ext2test-percent.dump new file mode 100644 index 0000000..f771eb4 --- /dev/null +++ b/test/ext2test-percent.dump @@ -0,0 +1,40 @@ +Filesystem volume name: ext2test +Last mounted on: <not available> +Filesystem magic number: 0xEF53 +Filesystem revision #: 0 (original) +Filesystem features: (none) +Default mount options: (none) +Filesystem state: clean +Errors behavior: Unknown (continue) +Filesystem OS type: Linux +Inode count: 56 +Block count: 68 +Reserved block count: 3 +Free blocks: 36 +Free inodes: 5 +First block: 1 +Block size: 1024 +Fragment size: 1024 +Blocks per group: 72 +Fragments per group: 72 +Inodes per group: 56 +Inode blocks per group: 7 +Filesystem created: Sat Jan 1 00:00:00 2000 +Last mount time: n/a +Last write time: Sat Jan 1 00:00:00 2000 +Mount count: 0 +Maximum mount count: 20 +Last checked: Sat Jan 1 00:00:00 2000 +Check interval: 0 (<none>) +Reserved blocks uid: 0 (user root) +Reserved blocks gid: 0 (group root) + + +Group 0: (Blocks 1-67) + Primary superblock at 1, Group descriptors at 2-2 + Block bitmap at 3 (+2) + Inode bitmap at 4 (+3) + Inode table at 5-11 (+4) + 36 free blocks, 5 free inodes, 18 directories + Free blocks: 32-67 + Free inodes: 52-56 diff --git a/test/hdimage.config b/test/hdimage.config index 7287b6d..09978c4 100644 --- a/test/hdimage.config +++ b/test/hdimage.config @@ -1,6 +1,7 @@ image test.hdimage { hdimage { align = 1M + fill = true disk-signature = 0x12345678 } partition part1 { @@ -68,7 +69,8 @@ image test.hdimage-2 { } partition part6 { image = "part2.img" - size = 1M + autoresize = true partition-type = 0x83 } + size = 12M } @@ -29,6 +29,7 @@ #include <unistd.h> #include <fcntl.h> #include <ctype.h> +#include <dirent.h> #include "genimage.h" @@ -252,12 +253,16 @@ void *xzalloc(size_t n) * Like simple_strtoul() but handles an optional G, M, K or k * suffix for Gigabyte, Megabyte or Kilobyte */ -unsigned long long strtoul_suffix(const char *str, char **endp, int base) +unsigned long long strtoul_suffix(const char *str, char **endp, + cfg_bool_t *percent) { unsigned long long val; char *end; - val = strtoull(str, &end, base); + val = strtoull(str, &end, 0); + + if (percent) + *percent = cfg_false; switch (*end) { case 'G': @@ -272,6 +277,12 @@ unsigned long long strtoul_suffix(const char *str, char **endp, int base) end++; case '\0': break; + case '%': + if (percent) { + *percent = cfg_true; + break; + } + /* fall-through */ default: error("Invalid size suffix '%s' in '%s'\n", end, str); exit(1); @@ -449,7 +460,7 @@ int pad_file(struct image *image, const char *infile, if (!infile) { if ((unsigned long long)s.st_size > size) { - image_error(image, "input file '%s' too large\n", outfile); + image_error(image, "output file '%s' too large\n", outfile); ret = -EINVAL; goto err_out; } @@ -566,6 +577,49 @@ err_out: return ret; } +int extend_file(struct image *image, size_t size) +{ + const char *outfile = imageoutfile(image); + char buf = '\0'; + int f; + off_t offset; + int ret = 0; + + f = open_file(image, outfile, 0); + if (f < 0) + return f; + + offset = lseek(f, 0, SEEK_END); + if (offset < 0) { + ret = -errno; + image_error(image, "seek: %s\n", strerror(errno)); + goto out; + } + if ((size_t)offset > size) { + ret = -EINVAL; + image_error(image, "output file is larger than requested size\n"); + goto out; + } + if ((size_t)offset == size) + goto out; + + if (lseek(f, size - 1, SEEK_SET) < 0) { + ret = -errno; + image_error(image, "seek %s: %s\n", outfile, strerror(errno)); + goto out; + } + ret = write(f, &buf, 1); + if (ret < 1) { + ret = -errno; + image_error(image, "write %s: %s\n", outfile, strerror(errno)); + goto out; + } + ret = 0; +out: + close(f); + return ret; +} + int uuid_validate(const char *str) { int i; @@ -654,3 +708,52 @@ int reload_partitions(struct image *image) close(fd); return 0; } + +#define ROUND_UP(num,align) ((((num) + ((align) - 1)) & ~((align) - 1))) + +static unsigned long long dir_size(struct image *image, int dirfd, + const char *subdir, size_t blocksize) +{ + struct dirent *d; + DIR *dir; + int fd; + unsigned long long size = 0; + struct stat st; + + fd = openat(dirfd, subdir, O_RDONLY); + if (fd < 0) { + image_error(image, "failed to open '%s': %s", subdir, + strerror(errno)); + return 0; + } + + dir = fdopendir(dup(fd)); + if (dir == NULL) { + image_error(image, "failed to opendir '%s': %s", subdir, + strerror(errno)); + return 0; + } + while ((d = readdir(dir)) != NULL) { + if (d->d_type == DT_DIR) { + if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + size += dir_size(image, fd, d->d_name, blocksize); + continue; + } + if (d->d_type != DT_REG) + continue; + if (fstatat(fd, d->d_name, &st, AT_NO_AUTOMOUNT) < 0) { + image_error(image, "failed to stat '%s': %s", + d->d_name, strerror(errno)); + continue; + } + size += ROUND_UP(st.st_size, blocksize); + } + return size + blocksize; +} + +unsigned long long image_dir_size(struct image *image) +{ + return dir_size(image, AT_FDCWD, mountpath(image), 4096); +} |