Ubinize allows to make an UBI image from a set of file systems (only currently, only ubifs are supported). I had to do opposite: extract volume from from an UBI image.

Our UBI image is splited in Physical Erase Block (PEB) (= "Erase Blocks" on mtd devices). Each PEB begin with a magic number of 4 characters : "UBI#". It give us an hint of PEB size. PEB begin with a ubi_ec_hdr structure (see ubi-media.h). This structure contains two important values for us:

  • offset to a second structure called ubi_vid_hdr (see also ubi-media.h)
  • offset to data itself (also called Logical Erase Block (LEB))

Structure ubi_vid_hdr contains the volume id and logical block number. To extract volume from an UBI image, we have to scan image and, reorder and concatenate all LEB which own right volume id.

How to know correct volume id? There are PEB with special volume id : 0x7FFFEFFF. These PEB contain association table between name and volume id. You can use ubinfo to parse this table and get volume ids.

When we work on a freshly generated UBI image, there are some simplifications:

  • LEB are already in correct order
  • Volume are ordonned on image
  • PEB 0x7FFFEFFF are the two first of image

If you do same thing on a real UBI device (or a dump), you have to scan all PEB to found them. In add, you should take care of ubi_vid_hdr->sqnum in case you find to LEB with same logical block number.

One last thing: all data are big endian.

So, we can write this code:

#include "ubi-media.h"
...
lnum = 0
while  > 0) {
        ec_hdr = (struct ubi_ec_hdr *) buf;
        vid_hdr = (struct ubi_vdr_hdr *) (buf + be32toh(ec_hdr->vid_hdr_offset));
        if (be32toh(ec_hdr->magic) != UBI_EC_HDR_MAGIC) {
              error(0, 0, "Bad EC_HDR magic number");
        }
        if (be32toh(vid_hdr->magic) != UBI_VID_HDR_MAGIC) {
        	error(0, 0, "Bad VID_HDR magic number");
        }
        if (be32toh(vid_hdr->vol_id) == vol) {
                if (be32toh(vid_hdr->lnum) != lnum) {
                        error(0, 0, "spared logical block are not supported");
                }
        	lnum++;
                write(fdout, buf +be32toh(ec_hdr->data_offset), size - be32toh(ec_hdr->data_offset));
        }
}
...