diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 69bf8ac..27bda2a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,13 +9,13 @@ jobs: matrix: include: - os: ubuntu-22.04 - pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools + pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools mdadm - os: ubuntu-22.04 - pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools + pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools mdadm fake: sudo rm /usr/include/linux/fiemap.h /usr/include/linux/fs.h env: ac_cv_func_fallocate=no - os: ubuntu-20.04 - pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools + pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools mdadm steps: - name: Inspect environment diff --git a/Makefile.am b/Makefile.am index d45a57c..d5ba3b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ genimage_SOURCES = \ image-cramfs.c \ image-ext2.c \ image-f2fs.c \ + image-mdraid.c \ image-btrfs.c \ image-file.c \ image-fip.c \ @@ -75,6 +76,7 @@ EXTRA_DIST += \ test/ext4test.1.dump \ test/ext4test.2.dump \ test/f2fs.config \ + test/mdraid.config \ test/btrfs.config \ test/fip.config \ test/fit.its \ diff --git a/README.rst b/README.rst index d3f1e97..878874d 100644 --- a/README.rst +++ b/README.rst @@ -105,8 +105,8 @@ Here are all options for images: Additionally each image can have one of the following sections describing the type of the image: -cpio, cramfs, ext2, ext3, ext4, file, flash, hdimage, iso, jffs2, qemu, squashfs, -tar, ubi, ubifs, vfat. +cpio, cramfs, ext2, ext3, ext4, f2fs, file, flash, hdimage, iso, +jffs2, mdraid, qemu, squashfs, tar, ubi, ubifs, vfat. Partition options: @@ -297,6 +297,15 @@ Options: :usage-type: Specify the usage type for the filesystem. Only valid with mke2fs. More details can be found in the mke2fs man-page. +f2fs +**** +Generates F2FS images. + +Options: + +:label: Specify the volume-label. +:extraargs: Extra arguments passed to mkfs.f2fs + file **** @@ -472,6 +481,44 @@ Options: :extraargs: Extra arguments passed to mkfs.jffs2 +mdraid +**** +Generates MD RAID images. + +Options: + +:label: Text name of array (optional) eg: localhost:42 +:level: RAID level, currently only level 1 (default) is supported +:devices: Number of devices in array (default 1) +:role: 0 based index of this image in whole array. (autoassigned by default) +:timestamp: Unix timestamp of array creation (current time by default, has to match across array devices) +:raid-uuid: UUID of whole array (has to be identical across all disks in array, random by default) +:disk-uuid: UUID of disk (has to be unique for each array member disk, random by default) +:image: Image of data to be preloaded into array (optional) +:parent: Image to inherit array identity/config from (when creating extra members of existing array). + Effectively overrides all array-wide options mentioned here and replaces them with values from parent. + +For example here only the first image has configuration and the UUID/timestamp is generated on demand:: + + image mdraid-a.img { + mdraid { + level = 1 + devices = 2 + image = "mdraid-ext4.img" + } + } + +Then to create second member to that array we just inherit config from the parent member:: + + image mdraid-b.img { + mdraid { + parent = "mdraid-a.img" + } + } + +Default role number is 0 for the parent image and when other images inherit configuration from it, they are assigned roles from autoincrementing counter. + + qemu **** Generates a QEMU image. Needs at least one valid partition. @@ -683,6 +730,7 @@ variable. :mmd: path to the mmd program (default mmd) :mkcramfs: path to the mkcramfs program (default mkcramfs) :mkdosfs: path to the mkdosfs program (default mkdosfs) +:mkfsf2fs: path to the mkfs.f2fs program (default mkfs.f2fs) :mkfsjffs2: path to the mkfs.jffs2 program (default mkfs.jffs2) :mkfsubifs: path to the mkfs.ubifs program (default mkfs.ubifs) :mksquashfs: path to the mksquashfs program (default mksquashfs) @@ -742,3 +790,6 @@ commits (e.g. using ``git commit -s``) looking as follows: Signed-off-by: Random J Developer with your identity and email address matching the commit meta data. + +Before creating pull request, please make sure your tree is passing +all unit tests by running ``make distcheck``. diff --git a/genimage.c b/genimage.c index 357e431..665d39c 100644 --- a/genimage.c +++ b/genimage.c @@ -45,6 +45,7 @@ static struct image_handler *handlers[] = { &ext3_handler, &ext4_handler, &f2fs_handler, + &mdraid_handler, &btrfs_handler, &file_handler, &fit_handler, diff --git a/genimage.h b/genimage.h index 055e294..a7535ce 100644 --- a/genimage.h +++ b/genimage.h @@ -19,6 +19,11 @@ void image_debug(struct image *image, const char *fmt, ...) __attribute__ ((form void xasprintf(char **strp, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); void xstrcatf(char **strp, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); +unsigned long long roundup(unsigned long long value, unsigned long long align); +unsigned long long rounddown(unsigned long long value, unsigned long long align); +unsigned long long min_ull(unsigned long long x, unsigned long long y); +unsigned long long max_ull(unsigned long long x, unsigned long long y); + void disable_rootpath(void); const char *imagepath(void); const char *inputpath(void); @@ -112,6 +117,7 @@ extern struct image_handler ext2_handler; extern struct image_handler ext3_handler; extern struct image_handler ext4_handler; extern struct image_handler f2fs_handler; +extern struct image_handler mdraid_handler; extern struct image_handler btrfs_handler; extern struct image_handler file_handler; extern struct image_handler flash_handler; diff --git a/image-hd.c b/image-hd.c index 5d16667..dc77dbf 100644 --- a/image-hd.c +++ b/image-hd.c @@ -109,26 +109,6 @@ ct_assert(sizeof(struct gpt_partition_entry) == 128); #define GPT_PE_FLAG_HIDDEN (1ULL << 62) #define GPT_PE_FLAG_NO_AUTO (1ULL << 63) -static unsigned long long roundup(unsigned long long value, unsigned long long align) -{ - return ((value - 1)/align + 1) * align; -} - -static unsigned long long rounddown(unsigned long long value, unsigned long long align) -{ - return value - (value % align); -} - -static unsigned long long min_ull(unsigned long long x, unsigned long long y) -{ - return x < y ? x : y; -} - -static unsigned long long max_ull(unsigned long long x, unsigned long long y) -{ - return x > y ? x : y; -} - static unsigned long long partition_end(const struct partition *part) { return part->offset + part->size; diff --git a/image-mdraid.c b/image-mdraid.c new file mode 100644 index 0000000..7a48f22 --- /dev/null +++ b/image-mdraid.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2025 Tomas Mudrunka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * 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, see . + */ + +/* + +MDRAID Superblock generator +This should create valid mdraid superblock for raid1 with 1 device (more devices can be added once mounted). +Unlike mdadm this works completely in userspace and does not need kernel to create the ondisk structures. +It is still very basic, but following seems to be working: + +mdadm --examine test.img +losetup /dev/loop1 test.img +mdadm --assemble md /dev/loop1 + +Some docs: +https://raid.wiki.kernel.org/index.php/RAID_superblock_formats#Sub-versions_of_the_version-1_superblock +https://docs.huihoo.com/doxygen/linux/kernel/3.7/md__p_8h_source.html + +*/ + +#include +#include +#include +#include +#include +#include + +#include "genimage.h" + +#define DATA_OFFSET_SECTORS (2048) +#define DATA_OFFSET_BYTES (DATA_OFFSET_SECTORS * 512) +#define BITMAP_SECTORS_MAX 256 +#define MDRAID_ALIGN_BYTES 8 * 512 //(should be divisible by 8 sectors to keep 4kB alignment) + +static time_t mdraid_time = 0; //Array creation timestamp has to be identical across all the raid members, so we share it between invocations + +/* + * bitmap structures: + * Taken from Linux kernel drivers/md/md-bitmap.h + * (Currently it's missing from linux-libc-dev debian package, so cannot be simply included) + */ + +#ifndef BITMAP_MAGIC +#define BITMAP_MAGIC 0x6d746962 // This is actualy just char string saying "bitm" :-) + +// use these for bitmap->flags and bitmap->sb->state bit-fields +enum bitmap_state { + BITMAP_STALE = 1, // the bitmap file is out of date or had -EIO + BITMAP_WRITE_ERROR = 2, // A write error has occurred + BITMAP_HOSTENDIAN = 15, +}; + +// the superblock at the front of the bitmap file -- little endian +typedef struct bitmap_super_s { + __le32 magic; // 0 BITMAP_MAGIC + __le32 version; // 4 the bitmap major for now, could change... + __u8 uuid[16]; // 8 128 bit uuid - must match md device uuid + __le64 events; // 24 event counter for the bitmap (1)*/ + __le64 events_cleared; // 32 event counter when last bit cleared (2) + __le64 sync_size; // 40 the size of the md device's sync range(3) + __le32 state; // 48 bitmap state information + __le32 chunksize; // 52 the bitmap chunk size in bytes + __le32 daemon_sleep; // 56 seconds between disk flushes + __le32 write_behind; // 60 number of outstanding write-behind writes + __le32 sectors_reserved; // 64 number of 512-byte sectors that are + // reserved for the bitmap. + __le32 nodes; // 68 the maximum number of nodes in cluster. + __u8 cluster_name[64]; // 72 cluster name to which this md belongs + __u8 pad[256 - 136]; // set to zero +} bitmap_super_t; + +/* notes: + * (1) This event counter is updated before the eventcounter in the md superblock + * When a bitmap is loaded, it is only accepted if this event counter is equal + * to, or one greater than, the event counter in the superblock. + * (2) This event counter is updated when the other one is *if*and*only*if* the + * array is not degraded. As bits are not cleared when the array is degraded, + * this represents the last time that any bits were cleared. + * If a device is being added that has an event count with this value or + * higher, it is accepted as conforming to the bitmap. + * (3)This is the number of sectors represented by the bitmap, and is the range that + * resync happens across. For raid1 and raid5/6 it is the size of individual + * devices. For raid10 it is the size of the array. + */ +#endif //BITMAP_MAGIC + +//Superblock struct sanity check +ct_assert(offsetof(struct mdp_superblock_1, data_offset) == 128); +ct_assert(offsetof(struct mdp_superblock_1, utime) == 192); +ct_assert(sizeof(struct mdp_superblock_1) == 256); + +//This structure is used to store mdraid state data in handler_priv +typedef struct mdraid_img_s { + struct partition img_data_part; //Partition of data to be imported to raid + struct partition img_parent_part; //Partition of parent raid image (we can inherit config from it) + struct image *img_data; //Images for aforementioned partitions + struct image *img_parent; //Dtto + struct mdp_superblock_1 *sb; //Actual mdraid superblock that is gonna be stored on disk + bitmap_super_t bsb; //Actual bitmap superblock that is gonna be stored on disk + __le16 last_role; //This is counter used by slave devices to take roles +} mdraid_img_t; + +static unsigned int calc_sb_1_csum(struct mdp_superblock_1 *sb) +{ + unsigned int disk_csum, csum; + unsigned long long newcsum; + int size = sizeof(*sb) + __le32_to_cpu(sb->max_dev) * 2; + unsigned int *isuper = (unsigned int *)sb; + + //Temporarily set checksum in struct to 0 while remembering original value + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + newcsum = 0; + for (; size >= 4; size -= 4) { + newcsum += __le32_to_cpu(*isuper); + isuper++; + } + + if (size == 2) + newcsum += __le16_to_cpu(*(unsigned short *)isuper); + + csum = (newcsum & 0xffffffff) + (newcsum >> 32); + sb->sb_csum = disk_csum; //Set checksum in struct back to original value + return __cpu_to_le32(csum); +} + +static int mdraid_generate(struct image *image) +{ + mdraid_img_t *md = image->handler_priv; + mdraid_img_t *mdp = NULL; //Inheriting from this parent if not NULL + __le16 max_devices; + + //Determine max_devices while considering possibility of inheritance from other image + if (md->img_parent) { + mdp = md->img_parent->handler_priv; + max_devices = mdp->sb->raid_disks; + } else { + max_devices = cfg_getint(image->imagesec, "devices"); + } + + //Determine role of this device in array + __le16 role = cfg_getint(image->imagesec, "role"); + if (cfg_getint(image->imagesec, "role") == -1) { //If role is -1 it should be autoassigned to parenting devices + if (mdp) { + role = ++mdp->last_role; //Take role from master and increment its counter + } else { + role = 0; //Master has role of 0 + } + image_info(image, "MDRAID automaticaly assigned role %d.\n", role); + } + + if (role > MD_DISK_ROLE_MAX) { + image_error(image, "MDRAID role has to be >= 0 and <= %d.\n", MD_DISK_ROLE_MAX); + return 6; + } + + if (role >= max_devices) { + image_error(image, "MDRAID role of this image has to be lower than total number of %d devices (roles are counted from 0).\n", max_devices); + return 5; + } + + //MD Superblock and Bitmap Superblock + size_t superblock_size = sizeof(struct mdp_superblock_1) + max_devices * 2; + struct mdp_superblock_1 *sb = md->sb = xzalloc(superblock_size); + bitmap_super_t *bsb = &md->bsb; + + if (mdp) { + //We are inheriting the superblock in this case + memcpy(md->sb, mdp->sb, superblock_size); + //memcpy(&md->bsb, &mdp->bsb, sizeof(bitmap_super_t)); + } else { + //We are not inheriting superblock, therefore we need to fully initialize the array + + char *name = cfg_getstr(image->imagesec, "label"); + + //constant array information - 128 bytes + sb->magic = MD_SB_MAGIC; // MD_SB_MAGIC: 0xa92b4efc - little endian. + sb->major_version = 1; // Always 1 for 1.xx metadata version :-) + sb->feature_map = MD_FEATURE_BITMAP_OFFSET; // bit 0 set if 'bitmap_offset' is meaningful + sb->pad0 = 0; // always set to 0 when writing + + char *raid_uuid = cfg_getstr(image->imagesec, "raid-uuid"); + if (!raid_uuid) + raid_uuid = uuid_random(); + uuid_parse(raid_uuid, sb->set_uuid); // user-space generated. U8[16] + + strncpy(sb->set_name, name, 32); + sb->set_name[31] = 0; // set and interpreted by user-space. CHAR[32] + + long int timestamp = cfg_getint(image->imagesec, "timestamp"); + if (timestamp >= 0) { + sb->ctime = timestamp & 0xffffffffff; + } else { + sb->ctime = mdraid_time & 0xffffffffff; // lo 40 bits are seconds, top 24 are microseconds or 0 + } + + sb->level = 1; // -4 (multipath), -1 (linear), 0,1,4,5 + //sb->layout; // only for raid5 and raid10 currently + sb->size = (image->size - DATA_OFFSET_BYTES) / 512; // used size of component devices, in 512byte sectors + + sb->chunksize = 0; // in 512byte sectors - not used in raid 1 + sb->raid_disks = max_devices; + } + + sb->bitmap_offset = 8; /* sectors after start of superblock that bitmap starts + * NOTE: signed, so bitmap can be before superblock + * only meaningful of feature_map[0] is set. + */ + + // constant this-device information - 64 bytes + sb->data_offset = DATA_OFFSET_SECTORS; // sector start of data, often 0 + sb->data_size = sb->size; // sectors in this device that can be used for data + sb->super_offset = 8; // sector start of this superblock + + sb->dev_number = role; // permanent identifier of this device - not role in raid (They can be equal tho). + sb->cnt_corrected_read = 0; // number of read errors that were corrected by re-writing + + char *disk_uuid = cfg_getstr(image->imagesec, "disk-uuid"); + if (!disk_uuid) + disk_uuid = uuid_random(); + uuid_parse(disk_uuid, sb->device_uuid); // user-space setable, ignored by kernel U8[16] + + sb->devflags = 0; // per-device flags. Only two defined... + //#define WriteMostly1 1 // mask for writemostly flag in above + //#define FailFast1 2 // Should avoid retries and fixups and just fail + + /* Bad block log. If there are any bad blocks the feature flag is set. + * If offset and size are non-zero, that space is reserved and available + */ + sb->bblog_shift = 9; // shift from sectors to badblock size, typicaly 9-12 (shift by 9 is equal to 512 sectors per badblock) + sb->bblog_size = 8; // number of sectors reserved for list + sb->bblog_offset = sb->bitmap_offset + BITMAP_SECTORS_MAX + 8; // sector offset from superblock to bblog, signed - not unsigned + + // array state information - 64 bytes + sb->utime = sb->ctime; // 40 bits second, 24 bits microseconds + sb->events = 0; // incremented when superblock updated + sb->resync_offset = 0; // data before this offset (from data_offset) known to be in sync + sb->max_dev = max_devices; // size of devs[] array to consider + //__u8 pad3[64-32]; // set to 0 when writing + + /* device state information. Indexed by dev_number. + * 2 bytes per device + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + __le16 *dev_roles = (__le16 *)((char *)sb + sizeof(struct mdp_superblock_1)); // role in array, or 0xffff for a spare, or 0xfffe for faulty + //memset(dev_roles, 0xFF, max_devices*2); //All devices in array are set as inactive initialy + for (int i = 0; i < max_devices; i++) { //All devices are assigned roles equal to their dev_number initialy + dev_roles[i] = i; //Assign active role to all devices + } + + //Calculate superblock checksum + sb->sb_csum = calc_sb_1_csum(sb); + + //Prepare bitmap superblock (bitmaps don't have checksums for performance reasons) + bsb->magic = BITMAP_MAGIC; // 0 BITMAP_MAGIC - This is actualy just char string saying "bitm" :-) + bsb->version = 4; // v4 is compatible with mdraid v1.2, 4 the bitmap major for now, could change... + memcpy(bsb->uuid, sb->set_uuid, sizeof(bsb->uuid)); // 8 128 bit uuid - must match md device uuid + //bsb->events = 0; // 24 event counter for the bitmap (1)*/ + //bsb->events_cleared = 0; // 32 event counter when last bit cleared (2) + bsb->sync_size = sb->data_size; // 40 the size of the md device's sync range(3) + //bsb->state = 0; // 48 bitmap state information + bsb->chunksize = 64 * 1024 * 1024; // 52 the bitmap chunk size in bytes, 64MB is default on linux + bsb->daemon_sleep = 5; // 5 is considered safe default. 56 seconds between disk flushes + //bsb->write_behind = 0; // 60 number of outstanding write-behind writes + bsb->sectors_reserved = roundup(bsb->sync_size / bsb->chunksize, 8); // 64 number of 512-byte sectors that are reserved for the bitmap. + //bsb->nodes; // 68 the maximum number of nodes in cluster. + //bsb->cluster_name[64]; // 72 cluster name to which this md belongs + //__u8 pad[256 - 136]; // set to zero + + //Increase bitmap chunk size till we fit in sectors max + while (bsb->sectors_reserved > BITMAP_SECTORS_MAX) { + bsb->chunksize *= 2; + bsb->sectors_reserved = roundup(bsb->sync_size / bsb->chunksize, 8); + } + + //Construct image file + int ret; + ret = prepare_image(image, image->size); + if (ret) + return ret; + //Write superblock + ret = insert_data(image, sb, imageoutfile(image), superblock_size, sb->super_offset * 512); + if (ret) + return ret; + //Write bitmap + if (sb->feature_map & MD_FEATURE_BITMAP_OFFSET) { + ret = insert_data(image, bsb, imageoutfile(image), sizeof(*bsb), (sb->super_offset + sb->bitmap_offset) * 512); + if (ret) + return ret; + } + //Write data + if (md->img_data) { + ret = insert_image(image, md->img_data, md->img_data->size, DATA_OFFSET_BYTES, 0); + if (ret) + return ret; + } + + return 0; +} + +static int mdraid_parse(struct image *image, cfg_t *cfg) +{ + mdraid_img_t *md = xzalloc(sizeof(mdraid_img_t)); + image->handler_priv = md; + + //Common MDRAID subsystem init + if (!mdraid_time) + mdraid_time = time(NULL); + + //Sanity checks + int raid_level = cfg_getint(image->imagesec, "level"); + if (raid_level != 1) { + image_error(image, "MDRAID Currently only supporting raid level 1 (mirror)!\n"); + return 1; + } + + //Inherit config from parent + md->img_parent_part.image = cfg_getstr(image->imagesec, "parent"); + if (md->img_parent_part.image) { + //Add parent partition as dependency (so it's built first) + list_add_tail(&md->img_parent_part.list, &image->partitions); + + //Find parent image + md->img_parent = image_get(md->img_parent_part.image); + if (!md->img_parent) { + image_error(image, "MDRAID cannot find parent image to inherit metadata config from: %s\n", md->img_parent_part.image); + return 9; + } + + //Inherit image size from parent + image_info(image, "MDRAID will inherit array metadata config from parent: %s\n", md->img_parent->file); + image->size = md->img_parent->size; + } + + //Find data partition to be put inside the array + if (md->img_parent) { + md->img_data_part.image = cfg_getstr(md->img_parent->imagesec, "image"); + } else { + md->img_data_part.image = cfg_getstr(image->imagesec, "image"); + } + + //Add data partition as dependency (so it's built first) + if (md->img_data_part.image) { + list_add_tail(&md->img_data_part.list, &image->partitions); + } + + return 0; +} + +static int mdraid_setup(struct image *image, cfg_t *cfg) +{ + mdraid_img_t *md = image->handler_priv; + + //Find data image and its metadata if data partition exists + if (md->img_data_part.image) { + image_info(image, "MDRAID using data from: %s\n", md->img_data_part.image); + md->img_data = image_get(md->img_data_part.image); + if (!md->img_data) { + image_error(image, "MDRAID cannot get image definition: %s\n", md->img_data_part.image); + return 8; + } + if (image->size == 0) + image->size = roundup(md->img_data->size + DATA_OFFSET_BYTES, MDRAID_ALIGN_BYTES); + if (image->size < (md->img_data->size + DATA_OFFSET_BYTES)) { + image_error(image, "MDRAID image too small to fit %s\n", md->img_data->file); + return 3; + } + } else { + image_info(image, "MDRAID is created without data.\n"); + } + + //Make sure size is aligned + if (image->size != roundup(image->size, MDRAID_ALIGN_BYTES)) { + image_error(image, "MDRAID image size has to be aligned to %d bytes!\n", MDRAID_ALIGN_BYTES); + return 4; + } + + return 0; +} + +static cfg_opt_t mdraid_opts[] = { + CFG_STR("label", "localhost:42", CFGF_NONE), + CFG_INT("level", 1, CFGF_NONE), + CFG_INT("devices", 1, CFGF_NONE), + CFG_INT("role", -1, CFGF_NONE), + CFG_INT("timestamp", -1, CFGF_NONE), + CFG_STR("raid-uuid", NULL, CFGF_NONE), + CFG_STR("disk-uuid", NULL, CFGF_NONE), + CFG_STR("image", NULL, CFGF_NONE), + CFG_STR("parent", NULL, CFGF_NONE), + CFG_END() +}; + +struct image_handler mdraid_handler = { + .type = "mdraid", + .no_rootpath = cfg_true, + .parse = mdraid_parse, + .setup = mdraid_setup, + .generate = mdraid_generate, + .opts = mdraid_opts, +}; diff --git a/test/mdraid.config b/test/mdraid.config new file mode 100644 index 0000000..aafddad --- /dev/null +++ b/test/mdraid.config @@ -0,0 +1,16 @@ +image test.mdraid-a { + mdraid { + level = 1 + devices = 2 + timestamp = 638022222 + raid-uuid = "de9980f1-0449-4e83-84bd-98e4b1ca3fe3" + disk-uuid = "eb3b107b-ae9d-4c6b-994a-ec412d36959b" + } + size = 5M +} + +image test.mdraid-b { + mdraid { + parent = "test.mdraid-a" + } +} diff --git a/test/misc.test b/test/misc.test index 0b4396f..d6fc9e8 100755 --- a/test/misc.test +++ b/test/misc.test @@ -104,6 +104,20 @@ test_expect_success fiptool "fip" " fiptool info images/test.fip " +exec_test_set_prereq mdadm +test_expect_success mdadm "mdraid" " + run_genimage_root mdraid.config test.mdraid-a && + LANG=C mdadm --examine images/test.mdraid-a | tee images/test.mdraid-a.txt && + LANG=C mdadm --examine images/test.mdraid-b | tee images/test.mdraid-b.txt && + grep 'Checksum.*correct$' images/test.mdraid-a.txt && + grep 'State.*active$' images/test.mdraid-a.txt && + grep 'Internal Bitmap.*sectors' images/test.mdraid-a.txt && + grep 'Bad Block Log.*entries available' images/test.mdraid-a.txt && + grep -Ev '(Device UUID|Checksum|Device Role) :' images/test.mdraid-a.txt | tail -n +2 > images/test.mdraid-a.arr.txt && + grep -Ev '(Device UUID|Checksum|Device Role) :' images/test.mdraid-b.txt | tail -n +2 > images/test.mdraid-b.arr.txt && + diff images/test.mdraid-a.arr.txt images/test.mdraid-b.arr.txt +" + test_done # vim: syntax=sh diff --git a/util.c b/util.c index fa61ebd..f4c7230 100644 --- a/util.c +++ b/util.c @@ -41,6 +41,26 @@ #define AT_NO_AUTOMOUNT 0x800 #endif +unsigned long long roundup(unsigned long long value, unsigned long long align) +{ + return ((value - 1)/align + 1) * align; +} + +unsigned long long rounddown(unsigned long long value, unsigned long long align) +{ + return value - (value % align); +} + +unsigned long long min_ull(unsigned long long x, unsigned long long y) +{ + return x < y ? x : y; +} + +unsigned long long max_ull(unsigned long long x, unsigned long long y) +{ + return x > y ? x : y; +} + static int loglevel(void) { static int level = -1;