static int build_sparse_ext(int fd, const char *filename) { unsigned int i; unsigned int block; int start_contiguous_block; u8 *block_bitmap; off64_t ret; block_bitmap = malloc(info.block_size); if (!block_bitmap) critical_error("failed to allocate block bitmap"); if (aux_info.first_data_block > 0) sparse_file_add_file(info.sparse_file, filename, 0, info.block_size * aux_info.first_data_block, 0); for (i = 0; i < aux_info.groups; i++) { u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; u32 last_block = min(info.blocks_per_group, aux_info.len_blocks - first_block); ret = lseek64(fd, (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap, SEEK_SET); if (ret < 0) critical_error_errno("failed to seek to block group bitmap %d", i); ret = read(fd, block_bitmap, info.block_size); if (ret < 0) critical_error_errno("failed to read block group bitmap %d", i); if (ret != (int)info.block_size) critical_error("failed to read all of block group bitmap %d", i); start_contiguous_block = -1; for (block = 0; block < last_block; block++) { if (start_contiguous_block >= 0) { if (!bitmap_get_bit(block_bitmap, block)) { u32 start_block = first_block + start_contiguous_block; u32 len_blocks = block - start_contiguous_block; sparse_file_add_file(info.sparse_file, filename, (u64)info.block_size * start_block, info.block_size * len_blocks, start_block); start_contiguous_block = -1; } } else { if (bitmap_get_bit(block_bitmap, block)) start_contiguous_block = block; } } if (start_contiguous_block >= 0) { u32 start_block = first_block + start_contiguous_block; u32 len_blocks = last_block - start_contiguous_block; sparse_file_add_file(info.sparse_file, filename, (u64)info.block_size * start_block, info.block_size * len_blocks, start_block); } } return 0; }
void inode_attach_resize(struct ext4_inode *inode, struct block_allocation *alloc) { u32 block_len = block_allocation_len(alloc); u32 superblocks = block_len / info.bg_desc_reserve_blocks; u32 i, j; u64 blocks; u64 size; if (block_len % info.bg_desc_reserve_blocks) critical_error("reserved blocks not a multiple of %d", info.bg_desc_reserve_blocks); append_oob_allocation(alloc, 1); u32 dind_block = get_oob_block(alloc, 0); u32 *dind_block_data = calloc(info.block_size, 1); if (!dind_block_data) critical_error_errno("calloc"); queue_data_block((u8 *)dind_block_data, info.block_size, dind_block); u32 *ind_block_data = calloc(info.block_size, info.bg_desc_reserve_blocks); if (!ind_block_data) critical_error_errno("calloc"); queue_data_block((u8 *)ind_block_data, info.block_size * info.bg_desc_reserve_blocks, get_block(alloc, 0)); for (i = 0; i < info.bg_desc_reserve_blocks; i++) { int r = (i - aux_info.bg_desc_blocks) % info.bg_desc_reserve_blocks; if (r < 0) r += info.bg_desc_reserve_blocks; dind_block_data[i] = get_block(alloc, r); for (j = 1; j < superblocks; j++) { u32 b = j * info.bg_desc_reserve_blocks + r; ind_block_data[r * aux_info.blocks_per_ind + j - 1] = get_block(alloc, b); } } u32 last_block = EXT4_NDIR_BLOCKS + aux_info.blocks_per_ind + aux_info.blocks_per_ind * (info.bg_desc_reserve_blocks - 1) + superblocks - 2; blocks = ((u64)block_len + 1) * info.block_size / 512; size = (u64)last_block * info.block_size; inode->i_block[EXT4_DIND_BLOCK] = dind_block; inode->i_flags = 0; inode->i_blocks_lo = blocks; inode->osd2.linux2.l_i_blocks_high = blocks >> 32; inode->i_size_lo = size; inode->i_size_high = size >> 32; }
static int read_ext(int fd) { off64_t ret; struct ext4_super_block sb; unsigned int i; ret = lseek64(fd, 1024, SEEK_SET); if (ret < 0) critical_error_errno("failed to seek to superblock"); ret = read(fd, &sb, sizeof(sb)); if (ret < 0) critical_error_errno("failed to read superblock"); if (ret != sizeof(sb)) critical_error("failed to read all of superblock"); ext4_parse_sb(&sb); ret = lseek64(fd, info.len, SEEK_SET); if (ret < 0) critical_error_errno("failed to seek to end of input image"); ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET); if (ret < 0) critical_error_errno("failed to seek to block group descriptors"); ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks); if (ret < 0) critical_error_errno("failed to read block group descriptors"); if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks) critical_error("failed to read all of block group descriptors"); if (verbose) { printf("Found filesystem with parameters:\n"); printf(" Size: %llu\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Label: %s\n", info.label); printf(" Blocks: %llu\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); printf(" Used %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); } return 0; }
/* Creates data buffers for the first backing_len bytes of a block allocation and queues them to be written */ static u8 *create_backing(struct block_allocation *alloc, unsigned long backing_len) { if (DIV_ROUND_UP(backing_len, info.block_size) > EXT4_NDIR_BLOCKS) critical_error("indirect backing larger than %d blocks", EXT4_NDIR_BLOCKS); u8 *data = calloc(backing_len, 1); if (!data) critical_error_errno("calloc"); u8 *ptr = data; for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { u32 region_block; u32 region_len; u32 len; get_region(alloc, ®ion_block, ®ion_len); len = min(region_len * info.block_size, backing_len); queue_data_block(ptr, len, region_block); ptr += len; backing_len -= len; } return data; }
/* Compute the rest of the parameters of the filesystem from the basic info */ void ext4_create_fs_aux_info() { aux_info.first_data_block = (info.block_size > 1024) ? 0 : 1; aux_info.len_blocks = info.len / info.block_size; aux_info.inode_table_blocks = DIV_ROUND_UP(info.inodes_per_group * info.inode_size, info.block_size); aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block, info.blocks_per_group); aux_info.blocks_per_ind = info.block_size / sizeof(u32); aux_info.blocks_per_dind = aux_info.blocks_per_ind * aux_info.blocks_per_ind; aux_info.blocks_per_tind = aux_info.blocks_per_dind * aux_info.blocks_per_dind; aux_info.bg_desc_blocks = DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc), info.block_size); aux_info.default_i_flags = EXT4_NOATIME_FL; u32 last_group_size = aux_info.len_blocks % info.blocks_per_group; u32 last_header_size = 2 + aux_info.inode_table_blocks; if (ext4_bg_has_super_block(aux_info.groups - 1)) last_header_size += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks; if (last_group_size > 0 && last_group_size < last_header_size) { aux_info.groups--; aux_info.len_blocks -= last_group_size; } aux_info.sb = calloc(info.block_size, 1); /* Alloc an array to hold the pointers to the backup superblocks */ aux_info.backup_sb = calloc(aux_info.groups, sizeof(char *)); if (!aux_info.sb) critical_error_errno("calloc"); aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks); if (!aux_info.bg_desc) critical_error_errno("calloc"); aux_info.xattrs = NULL; }
/* Queues a block of memory to be written to the specified data blocks */ void queue_data_block(u8 *data, u32 len, u32 block) { struct data_block *db = malloc(sizeof(struct data_block)); if (db == NULL) critical_error_errno("malloc"); db->block = block; db->len = len; db->data = data; db->filename = NULL; db->fill = 0; db->next = NULL; queue_db(db); }
/* Queues a chunk of a file on disk to be written to the specified data blocks */ void queue_data_file(const char *filename, off64_t offset, u32 len, u32 block) { struct data_block *db = malloc(sizeof(struct data_block)); if (db == NULL) critical_error_errno("malloc"); db->block = block; db->len = len; db->filename = strdup(filename); db->offset = offset; db->data = NULL; db->fill = 0; db->next = NULL; queue_db(db); }
/* Creates data buffers for the first backing_len bytes of a block allocation and queues them to be written */ static u8 *extent_create_backing(struct block_allocation *alloc, u64 backing_len) { u8 *data = calloc(backing_len, 1); if (!data) critical_error_errno("calloc"); u8 *ptr = data; for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { u32 region_block; u32 region_len; u32 len; get_region(alloc, ®ion_block, ®ion_len); len = min(region_len * info.block_size, backing_len); queue_data_block(ptr, len, region_block); ptr += len; backing_len -= len; } return data; }
static u32 build_default_directory_structure() { u32 inode; u32 root_inode; struct dentry dentries = { .filename = "lost+found", .file_type = EXT4_FT_DIR, .mode = S_IRWXU, .uid = 0, .gid = 0, .mtime = 0, }; root_inode = make_directory(0, 1, &dentries, 1); inode = make_directory(root_inode, 0, NULL, 0); *dentries.inode = inode; inode_set_permissions(inode, dentries.mode, dentries.uid, dentries.gid, dentries.mtime); return root_inode; } #ifndef USE_MINGW /* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, fs_config_func_t fs_config_func, struct selabel_handle *sehnd) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; bool needs_lost_and_found = false; if (full_path) { entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } } if (dir_inode == 0) { /* root directory, check if lost+found already exists */ for (i = 0; i < entries; i++) if (strcmp(namelist[i]->d_name, "lost+found") == 0) break; if (i == entries) needs_lost_and_found = true; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); dentries[i].mtime = stat.st_mtime; if (fs_config_func != NULL) { #ifdef ANDROID unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); fs_config_func(dentries[i].path, dir, &uid, &gid, &mode); dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; #else error("can't set android permissions - built without android support"); #endif } #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; asprintf(&sepath, "/%s", dentries[i].path); if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) { error("cannot lookup security context for %s", sepath); } if (dentries[i].secon) printf("Labeling %s as %s\n", sepath, dentries[i].secon); free(sepath); } #endif if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); if (needs_lost_and_found) { /* insert a lost+found directory at the beginning of the dentries */ struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry)); memset(tmp, 0, sizeof(struct dentry)); memcpy(tmp + 1, dentries, entries * sizeof(struct dentry)); dentries = tmp; dentries[0].filename = strdup("lost+found"); asprintf(&dentries[0].path, "%s/lost+found", dir_path); dentries[0].full_path = NULL; dentries[0].size = 0; dentries[0].mode = S_IRWXU; dentries[0].file_type = EXT4_FT_DIR; dentries[0].uid = 0; dentries[0].gid = 0; #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; asprintf(&sepath, "/%s", dentries[0].path); if (selabel_lookup(sehnd, &dentries[0].secon, sepath, dentries[0].mode) < 0) error("cannot lookup security context for %s", dentries[0].path); free(sepath); } #endif entries++; dirs++; } inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { entry_inode = build_directory_structure(dentries[i].full_path, dentries[i].path, inode, fs_config_func, sehnd); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].full_path, dentries[i].link); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); ret = inode_set_selinux(entry_inode, dentries[i].secon); if (ret) error("failed to set SELinux context on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); free(dentries[i].secon); } free(dentries); return inode; }
int make_ext4fs_internal(int fd, const char *directory, char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int init_itabs, struct selabel_handle *sehnd) { u32 root_inode_num; u16 root_mode; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %llu\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %llu\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); info.sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(); MTK_add_mountpoint(aux_info.sb,mountpoint); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); #ifdef USE_MINGW // Windows needs only 'create an empty fs image' functionality assert(!directory); root_inode_num = build_default_directory_structure(); #else if (directory) root_inode_num = build_directory_structure(directory, mountpoint, 0, fs_config_func, sehnd); else root_inode_num = build_default_directory_structure(); #endif root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; char *secontext = NULL; if (mountpoint[0] == '/') sepath = strdup(mountpoint); else asprintf(&sepath, "/%s", mountpoint); if (!sepath) critical_error_errno("malloc"); if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) { error("cannot lookup security context for %s", sepath); } if (secontext) { printf("Labeling %s as %s\n", sepath, secontext); inode_set_selinux(root_inode_num, secontext); } free(sepath); freecon(secontext); } #endif ext4_update_free(); if (init_itabs) init_unused_inode_tables(); ext4_queue_sb(); printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe) wipe_block_device(fd, info.len); write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(info.sparse_file); info.sparse_file = NULL; return 0; }
/* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory. full_path is an absolute or relative path, with a trailing slash, to the directory on disk that should be copied, or NULL if this is a directory that does not exist on disk (e.g. lost+found). dir_path is an absolute path, with trailing slash, to the same directory if the image were mounted at the specified mount point */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, fs_config_func_t fs_config_func, int verbose, time_t fixed_time) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; bool needs_lost_and_found = false; /* alphasort is locale-dependent; let's fix the locale to some sane value */ setlocale(LC_ALL, "C"); if (full_path) { entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { #ifdef __GLIBC__ /* The scandir function implemented in glibc has a bug that makes it erroneously fail with ENOMEM under certain circumstances. As a workaround we can retry the scandir call with the same arguments. GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */ if (errno == ENOMEM) entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); #endif if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } } } if (dir_inode == 0) { /* root directory, check if lost+found already exists */ for (i = 0; i < entries; i++) if (strcmp(namelist[i]->d_name, "lost+found") == 0) break; if (i == entries) needs_lost_and_found = true; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (fixed_time == -1) { dentries[i].mtime = stat.st_mtime; } else { dentries[i].mtime = fixed_time; } uint64_t capabilities; if (fs_config_func != NULL) { unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); if (fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities)) { dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; dentries[i].capabilities = capabilities; } } if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); if (needs_lost_and_found) { /* insert a lost+found directory at the beginning of the dentries */ struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry)); memset(tmp, 0, sizeof(struct dentry)); memcpy(tmp + 1, dentries, entries * sizeof(struct dentry)); dentries = tmp; dentries[0].filename = strdup("lost+found"); asprintf(&dentries[0].path, "%slost+found", dir_path); dentries[0].full_path = NULL; dentries[0].size = 0; dentries[0].mode = S_IRWXU; dentries[0].file_type = EXT4_FT_DIR; dentries[0].uid = 0; dentries[0].gid = 0; entries++; dirs++; } inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { char *subdir_full_path = NULL; char *subdir_dir_path; if (dentries[i].full_path) { ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path); if (ret < 0) critical_error_errno("asprintf"); } ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path); if (ret < 0) critical_error_errno("asprintf"); entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, inode, fs_config_func, verbose, fixed_time); free(subdir_full_path); free(subdir_dir_path); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].link); } else if (dentries[i].file_type == EXT4_FT_CHRDEV || dentries[i].file_type == EXT4_FT_BLKDEV || dentries[i].file_type == EXT4_FT_SOCK || dentries[i].file_type == EXT4_FT_FIFO) { entry_inode = make_special(dentries[i].full_path); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); ret = inode_set_capabilities(entry_inode, dentries[i].capabilities); if (ret) error("failed to set capability on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); } free(dentries); return inode; }
static u32 build_default_directory_structure() { u32 inode; u32 root_inode; struct dentry dentries = { .filename = "lost+found", .file_type = EXT4_FT_DIR, .mode = S_IRWXU, .uid = 0, .gid = 0, .mtime = 0, }; root_inode = make_directory(0, 1, &dentries, 1); inode = make_directory(root_inode, 0, NULL, 0); *dentries.inode = inode; inode_set_permissions(inode, dentries.mode, dentries.uid, dentries.gid, dentries.mtime); return root_inode; } /* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, int android) { int entries = 0; struct dentry *dentries; struct dirent **namelist; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); dentries[i].mtime = stat.st_mtime; if (android) { #ifdef ANDROID unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); //fs_config(dentries[i].path, dir, &uid, &gid, &mode); dentries[i].mode = stat.st_mode; dentries[i].uid = stat.st_uid; dentries[i].gid = stat.st_gid; #else error("can't set android permissions - built without android support"); #endif } if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { entry_inode = build_directory_structure(dentries[i].full_path, dentries[i].path, inode, android); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].full_path, dentries[i].link); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); } free(dentries); return inode; }
static u32 build_default_directory_structure() { u32 inode; u32 root_inode; struct dentry dentries = { .filename = "lost+found", .file_type = EXT4_FT_DIR, .mode = S_IRWXU, .uid = 0, .gid = 0, .mtime = 0, }; root_inode = make_directory(0, 1, &dentries, 1); inode = make_directory(root_inode, 0, NULL, 0); *dentries.inode = inode; inode_set_permissions(inode, dentries.mode, dentries.uid, dentries.gid, dentries.mtime); return root_inode; } #ifndef USE_MINGW /* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory. full_path is an absolute or relative path, with a trailing slash, to the directory on disk that should be copied, or NULL if this is a directory that does not exist on disk (e.g. lost+found). dir_path is an absolute path, with trailing slash, to the same directory if the image were mounted at the specified mount point */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, fs_config_func_t fs_config_func, struct selabel_handle *sehnd, int verbose) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; bool needs_lost_and_found = false; if (full_path) { entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } } if (dir_inode == 0) { /* root directory, check if lost+found already exists */ for (i = 0; i < entries; i++) if (strcmp(namelist[i]->d_name, "lost+found") == 0) break; if (i == entries) needs_lost_and_found = true; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); dentries[i].mtime = stat.st_mtime; uint64_t capabilities; if (fs_config_func != NULL) { #ifdef ANDROID unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities); dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; dentries[i].capabilities = capabilities; #else error("can't set android permissions - built without android support"); #endif } #ifndef USE_MINGW if (sehnd) { if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) { error("cannot lookup security context for %s", dentries[i].path); } if (dentries[i].secon && verbose) printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon); } #endif if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); if (needs_lost_and_found) { /* insert a lost+found directory at the beginning of the dentries */ struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry)); memset(tmp, 0, sizeof(struct dentry)); memcpy(tmp + 1, dentries, entries * sizeof(struct dentry)); dentries = tmp; dentries[0].filename = strdup("lost+found"); asprintf(&dentries[0].path, "%slost+found", dir_path); dentries[0].full_path = NULL; dentries[0].size = 0; dentries[0].mode = S_IRWXU; dentries[0].file_type = EXT4_FT_DIR; dentries[0].uid = 0; dentries[0].gid = 0; if (sehnd) { if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0) error("cannot lookup security context for %s", dentries[0].path); } entries++; dirs++; } inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { char *subdir_full_path = NULL; char *subdir_dir_path; if (dentries[i].full_path) { ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path); if (ret < 0) critical_error_errno("asprintf"); } ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path); if (ret < 0) critical_error_errno("asprintf"); entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, inode, fs_config_func, sehnd, verbose); free(subdir_full_path); free(subdir_dir_path); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].link); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); /* * It's important to call inode_set_selinux() before * inode_set_capabilities(). Extended attributes need to * be stored sorted order, and we guarantee this by making * the calls in the proper order. * Please see xattr_assert_sane() in contents.c */ ret = inode_set_selinux(entry_inode, dentries[i].secon); if (ret) error("failed to set SELinux context on %s\n", dentries[i].path); ret = inode_set_capabilities(entry_inode, dentries[i].capabilities); if (ret) error("failed to set capability on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); free(dentries[i].secon); } free(dentries); return inode; }
int main(int argc, char **argv) { int opt; const char *in = NULL; const char *out = NULL; int gzip = 0; int sparse = 1; int infd, outfd; int crc = 0; while ((opt = getopt(argc, argv, "cvzS")) != -1) { switch (opt) { case 'c': crc = 1; break; case 'v': verbose = 1; break; case 'z': gzip = 1; break; case 'S': sparse = 0; break; } } if (optind >= argc) { fprintf(stderr, "Expected image or block device after options\n"); usage(argv[0]); exit(EXIT_FAILURE); } in = argv[optind++]; if (optind >= argc) { fprintf(stderr, "Expected output image after input image\n"); usage(argv[0]); exit(EXIT_FAILURE); } out = argv[optind++]; if (optind < argc) { fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); usage(argv[0]); exit(EXIT_FAILURE); } infd = open(in, O_RDONLY); if (infd < 0) critical_error_errno("failed to open input image"); read_ext(infd); info.sparse_file = sparse_file_new(info.block_size, info.len); build_sparse_ext(infd, in); close(infd); if (strcmp(out, "-")) { outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (outfd < 0) { error_errno("open"); return EXIT_FAILURE; } } else { outfd = STDOUT_FILENO; } write_ext4_image(outfd, gzip, sparse, crc); close(outfd); sparse_file_destroy(info.sparse_file); return 0; }
static struct block_allocation *do_inode_allocate_extents( struct ext4_inode *inode, u64 len) { u32 block_len = DIV_ROUND_UP(len, info.block_size); struct block_allocation *alloc = allocate_blocks(block_len + 1); u32 extent_block = 0; u32 file_block = 0; struct ext4_extent *extent; u64 blocks; if (alloc == NULL) { error("Failed to allocate %d blocks\n", block_len + 1); return NULL; } int allocation_len = block_allocation_num_regions(alloc); if (allocation_len <= 3) { reduce_allocation(alloc, 1); } else { reserve_oob_blocks(alloc, 1); extent_block = get_oob_block(alloc, 0); } if (!extent_block) { struct ext4_extent_header *hdr = (struct ext4_extent_header *)&inode->i_block[0]; hdr->eh_magic = EXT4_EXT_MAGIC; hdr->eh_entries = allocation_len; hdr->eh_max = 3; hdr->eh_generation = 0; hdr->eh_depth = 0; extent = (struct ext4_extent *)&inode->i_block[3]; } else { struct ext4_extent_header *hdr = (struct ext4_extent_header *)&inode->i_block[0]; hdr->eh_magic = EXT4_EXT_MAGIC; hdr->eh_entries = 1; hdr->eh_max = 3; hdr->eh_generation = 0; hdr->eh_depth = 1; struct ext4_extent_idx *idx = (struct ext4_extent_idx *)&inode->i_block[3]; idx->ei_block = 0; idx->ei_leaf_lo = extent_block; idx->ei_leaf_hi = 0; idx->ei_unused = 0; u8 *data = calloc(info.block_size, 1); if (!data) critical_error_errno("calloc"); queue_data_block(data, info.block_size, extent_block); if (((int)(info.block_size - sizeof(struct ext4_extent_header) / sizeof(struct ext4_extent))) < allocation_len) { error("File size %llu is too big to fit in a single extent block\n", len); return NULL; } hdr = (struct ext4_extent_header *)data; hdr->eh_magic = EXT4_EXT_MAGIC; hdr->eh_entries = allocation_len; hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent); hdr->eh_generation = 0; hdr->eh_depth = 0; extent = (struct ext4_extent *)(data + sizeof(struct ext4_extent_header)); } for (; !last_region(alloc); extent++, get_next_region(alloc)) { u32 region_block; u32 region_len; get_region(alloc, ®ion_block, ®ion_len); extent->ee_block = file_block; extent->ee_len = region_len; extent->ee_start_hi = 0; extent->ee_start_lo = region_block; file_block += region_len; } if (extent_block) block_len += 1; blocks = (u64)block_len * info.block_size / 512; inode->i_flags |= EXT4_EXTENTS_FL; inode->i_size_lo = len; inode->i_size_high = len >> 32; inode->i_blocks_lo = blocks; inode->osd2.linux2.l_i_blocks_high = blocks >> 32; rewind_alloc(alloc); return alloc; }