Import make_ext4fs sources

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
This commit is contained in:
Jo-Philipp Wich 2015-04-04 22:26:16 +02:00
commit fb5c011b49
45 changed files with 8010 additions and 0 deletions

154
Android.mk Normal file
View File

@ -0,0 +1,154 @@
# Copyright 2010 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
libext4_utils_src_files := \
make_ext4fs.c \
ext4fixup.c \
ext4_utils.c \
allocate.c \
contents.c \
extent.c \
indirect.c \
uuid.c \
sha1.c \
wipe.c \
crc16.c \
ext4_sb.c
#
# -- All host/targets including windows
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(libext4_utils_src_files)
LOCAL_MODULE := libext4_utils_host
LOCAL_STATIC_LIBRARIES := \
libsparse_host \
libz
ifneq ($(HOST_OS),windows)
LOCAL_STATIC_LIBRARIES += libselinux
endif
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := make_ext4fs_main.c canned_fs_config.c
LOCAL_MODULE := make_ext4fs
LOCAL_STATIC_LIBRARIES += \
libcutils \
libext4_utils_host \
libsparse_host \
libz
ifeq ($(HOST_OS),windows)
LOCAL_LDLIBS += -lws2_32
else
LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_CFLAGS := -DHOST
endif
include $(BUILD_HOST_EXECUTABLE)
#
# -- All host/targets excluding windows
#
libext4_utils_src_files += \
ext4_crypt.cpp \
e4crypt_static.c \
unencrypted_properties.cpp
ifneq ($(HOST_OS),windows)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(libext4_utils_src_files)
LOCAL_MODULE := libext4_utils
LOCAL_C_INCLUDES += system/core/logwrapper/include
LOCAL_SHARED_LIBRARIES := \
libcutils \
libselinux \
libsparse \
libz
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(libext4_utils_src_files) \
ext4_crypt_init_extensions.cpp
LOCAL_MODULE := libext4_utils_static
LOCAL_STATIC_LIBRARIES := \
libsparse_static
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := make_ext4fs_main.c canned_fs_config.c
LOCAL_MODULE := make_ext4fs
LOCAL_SHARED_LIBRARIES := \
libcutils \
libext4_utils \
libselinux \
libz
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := ext2simg.c
LOCAL_MODULE := ext2simg
LOCAL_SHARED_LIBRARIES += \
libext4_utils \
libselinux \
libsparse \
libz
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := ext2simg.c
LOCAL_MODULE := ext2simg
LOCAL_STATIC_LIBRARIES += \
libext4_utils_host \
libselinux \
libsparse_host \
libz
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := setup_fs.c
LOCAL_MODULE := setup_fs
LOCAL_SHARED_LIBRARIES += libcutils
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := ext4fixup_main.c
LOCAL_MODULE := ext4fixup
LOCAL_SHARED_LIBRARIES += \
libext4_utils \
libsparse \
libz
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := ext4fixup_main.c
LOCAL_MODULE := ext4fixup
LOCAL_STATIC_LIBRARIES += \
libext4_utils_host \
libsparse_host \
libz
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := mkuserimg.sh
LOCAL_SRC_FILES := mkuserimg.sh
LOCAL_MODULE_CLASS := EXECUTABLES
# We don't need any additional suffix.
LOCAL_MODULE_SUFFIX :=
LOCAL_BUILT_MODULE_STEM := $(notdir $(LOCAL_SRC_FILES))
LOCAL_IS_HOST_MODULE := true
include $(BUILD_PREBUILT)
endif

0
MODULE_LICENSE_APACHE2 Normal file
View File

190
NOTICE Normal file
View File

@ -0,0 +1,190 @@
Copyright (c) 2010, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

781
allocate.c Normal file
View File

@ -0,0 +1,781 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "allocate.h"
#include <sparse/sparse.h>
#include <stdio.h>
#include <stdlib.h>
struct region {
u32 block;
u32 len;
int bg;
struct region *next;
struct region *prev;
};
struct block_group_info {
u32 first_block;
int header_blocks;
int data_blocks_used;
int has_superblock;
u8 *bitmaps;
u8 *block_bitmap;
u8 *inode_bitmap;
u8 *inode_table;
u32 free_blocks;
u32 first_free_block;
u32 free_inodes;
u32 first_free_inode;
u16 flags;
u16 used_dirs;
};
struct xattr_list_element {
struct ext4_inode *inode;
struct ext4_xattr_header *header;
struct xattr_list_element *next;
};
struct block_allocation *create_allocation()
{
struct block_allocation *alloc = malloc(sizeof(struct block_allocation));
alloc->list.first = NULL;
alloc->list.last = NULL;
alloc->oob_list.first = NULL;
alloc->oob_list.last = NULL;
alloc->list.iter = NULL;
alloc->list.partial_iter = 0;
alloc->oob_list.iter = NULL;
alloc->oob_list.partial_iter = 0;
alloc->filename = NULL;
alloc->next = NULL;
return alloc;
}
static struct ext4_xattr_header *xattr_list_find(struct ext4_inode *inode)
{
struct xattr_list_element *element;
for (element = aux_info.xattrs; element != NULL; element = element->next) {
if (element->inode == inode)
return element->header;
}
return NULL;
}
static void xattr_list_insert(struct ext4_inode *inode, struct ext4_xattr_header *header)
{
struct xattr_list_element *element = malloc(sizeof(struct xattr_list_element));
element->inode = inode;
element->header = header;
element->next = aux_info.xattrs;
aux_info.xattrs = element;
}
static void region_list_remove(struct region_list *list, struct region *reg)
{
if (reg->prev)
reg->prev->next = reg->next;
if (reg->next)
reg->next->prev = reg->prev;
if (list->first == reg)
list->first = reg->next;
if (list->last == reg)
list->last = reg->prev;
reg->next = NULL;
reg->prev = NULL;
}
static void region_list_append(struct region_list *list, struct region *reg)
{
if (list->first == NULL) {
list->first = reg;
list->last = reg;
list->iter = reg;
list->partial_iter = 0;
reg->prev = NULL;
} else {
list->last->next = reg;
reg->prev = list->last;
list->last = reg;
}
reg->next = NULL;
}
#if 0
static void dump_starting_from(struct region *reg)
{
for (; reg; reg = reg->next) {
printf("%p: Blocks %d-%d (%d)\n", reg,
reg->block, reg->block + reg->len - 1, reg->len)
}
}
static void dump_region_lists(struct block_allocation *alloc) {
printf("Main list:\n");
dump_starting_from(alloc->list.first);
printf("OOB list:\n");
dump_starting_from(alloc->oob_list.first);
}
#endif
void print_blocks(FILE* f, struct block_allocation *alloc)
{
struct region *reg;
for (reg = alloc->list.first; reg; reg = reg->next) {
if (reg->len == 1) {
fprintf(f, " %d", reg->block);
} else {
fprintf(f, " %d-%d", reg->block, reg->block + reg->len - 1);
}
}
fputc('\n', f);
}
void append_region(struct block_allocation *alloc,
u32 block, u32 len, int bg_num)
{
struct region *reg;
reg = malloc(sizeof(struct region));
reg->block = block;
reg->len = len;
reg->bg = bg_num;
reg->next = NULL;
region_list_append(&alloc->list, reg);
}
static void allocate_bg_inode_table(struct block_group_info *bg)
{
if (bg->inode_table != NULL)
return;
u32 block = bg->first_block + 2;
if (bg->has_superblock)
block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1;
bg->inode_table = calloc(aux_info.inode_table_blocks, info.block_size);
if (bg->inode_table == NULL)
critical_error_errno("calloc");
sparse_file_add_data(ext4_sparse_file, bg->inode_table,
aux_info.inode_table_blocks * info.block_size, block);
bg->flags &= ~EXT4_BG_INODE_UNINIT;
}
static int bitmap_set_bit(u8 *bitmap, u32 bit)
{
if (bitmap[bit / 8] & 1 << (bit % 8))
return 1;
bitmap[bit / 8] |= 1 << (bit % 8);
return 0;
}
static int bitmap_set_8_bits(u8 *bitmap, u32 bit)
{
int ret = bitmap[bit / 8];
bitmap[bit / 8] = 0xFF;
return ret;
}
/* Marks a the first num_blocks blocks in a block group as used, and accounts
for them in the block group free block info. */
static int reserve_blocks(struct block_group_info *bg, u32 start, u32 num)
{
unsigned int i = 0;
u32 block = start;
if (num > bg->free_blocks)
return -1;
for (i = 0; i < num && block % 8 != 0; i++, block++) {
if (bitmap_set_bit(bg->block_bitmap, block)) {
error("attempted to reserve already reserved block");
return -1;
}
}
for (; i + 8 <= (num & ~7); i += 8, block += 8) {
if (bitmap_set_8_bits(bg->block_bitmap, block)) {
error("attempted to reserve already reserved block");
return -1;
}
}
for (; i < num; i++, block++) {
if (bitmap_set_bit(bg->block_bitmap, block)) {
error("attempted to reserve already reserved block");
return -1;
}
}
bg->free_blocks -= num;
if (start == bg->first_free_block)
bg->first_free_block = start + num;
return 0;
}
static void free_blocks(struct block_group_info *bg, u32 num_blocks)
{
unsigned int i;
u32 block = bg->first_free_block - 1;
for (i = 0; i < num_blocks; i++, block--)
bg->block_bitmap[block / 8] &= ~(1 << (block % 8));
bg->free_blocks += num_blocks;
bg->first_free_block -= num_blocks;
}
/* Reduces an existing allocation by len blocks by return the last blocks
to the free pool in their block group. Assumes that the blocks being
returned were the last ones allocated out of the block group */
void reduce_allocation(struct block_allocation *alloc, u32 len)
{
while (len) {
struct region *last_reg = alloc->list.last;
if (last_reg->len > len) {
free_blocks(&aux_info.bgs[last_reg->bg], len);
last_reg->len -= len;
len = 0;
} else {
struct region *reg = alloc->list.last->prev;
free_blocks(&aux_info.bgs[last_reg->bg], last_reg->len);
len -= last_reg->len;
if (reg) {
reg->next = NULL;
} else {
alloc->list.first = NULL;
alloc->list.last = NULL;
alloc->list.iter = NULL;
alloc->list.partial_iter = 0;
}
free(last_reg);
}
}
}
static void init_bg(struct block_group_info *bg, unsigned int i)
{
int header_blocks = 2 + aux_info.inode_table_blocks;
bg->has_superblock = ext4_bg_has_super_block(i);
if (bg->has_superblock)
header_blocks += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
bg->bitmaps = calloc(info.block_size, 2);
bg->block_bitmap = bg->bitmaps;
bg->inode_bitmap = bg->bitmaps + info.block_size;
bg->header_blocks = header_blocks;
bg->first_block = aux_info.first_data_block + i * info.blocks_per_group;
u32 block = bg->first_block;
if (bg->has_superblock)
block += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
sparse_file_add_data(ext4_sparse_file, bg->bitmaps, 2 * info.block_size,
block);
bg->data_blocks_used = 0;
bg->free_blocks = info.blocks_per_group;
bg->first_free_block = 0;
bg->free_inodes = info.inodes_per_group;
bg->first_free_inode = 1;
bg->flags = EXT4_BG_INODE_UNINIT;
if (reserve_blocks(bg, bg->first_free_block, bg->header_blocks) < 0)
error("failed to reserve %u blocks in block group %u\n", bg->header_blocks, i);
if (bg->first_block + info.blocks_per_group > aux_info.len_blocks) {
u32 overrun = bg->first_block + info.blocks_per_group - aux_info.len_blocks;
reserve_blocks(bg, info.blocks_per_group - overrun, overrun);
}
}
void block_allocator_init()
{
unsigned int i;
aux_info.bgs = calloc(sizeof(struct block_group_info), aux_info.groups);
if (aux_info.bgs == NULL)
critical_error_errno("calloc");
for (i = 0; i < aux_info.groups; i++)
init_bg(&aux_info.bgs[i], i);
}
void block_allocator_free()
{
unsigned int i;
for (i = 0; i < aux_info.groups; i++) {
free(aux_info.bgs[i].bitmaps);
free(aux_info.bgs[i].inode_table);
}
free(aux_info.bgs);
}
static u32 ext4_allocate_blocks_from_block_group(u32 len, int bg_num)
{
if (get_free_blocks(bg_num) < len)
return EXT4_ALLOCATE_FAILED;
u32 block = aux_info.bgs[bg_num].first_free_block;
struct block_group_info *bg = &aux_info.bgs[bg_num];
if (reserve_blocks(bg, bg->first_free_block, len) < 0) {
error("failed to reserve %u blocks in block group %u\n", len, bg_num);
return EXT4_ALLOCATE_FAILED;
}
aux_info.bgs[bg_num].data_blocks_used += len;
return bg->first_block + block;
}
/* Allocate a single block and return its block number */
u32 allocate_block()
{
unsigned int i;
for (i = 0; i < aux_info.groups; i++) {
u32 block = ext4_allocate_blocks_from_block_group(1, i);
if (block != EXT4_ALLOCATE_FAILED)
return block;
}
return EXT4_ALLOCATE_FAILED;
}
static struct region *ext4_allocate_best_fit_partial(u32 len)
{
unsigned int i;
unsigned int found_bg = 0;
u32 found_bg_len = 0;
for (i = 0; i < aux_info.groups; i++) {
u32 bg_len = aux_info.bgs[i].free_blocks;
if ((len <= bg_len && (found_bg_len == 0 || bg_len < found_bg_len)) ||
(len > found_bg_len && bg_len > found_bg_len)) {
found_bg = i;
found_bg_len = bg_len;
}
}
if (found_bg_len) {
u32 allocate_len = min(len, found_bg_len);
struct region *reg;
u32 block = ext4_allocate_blocks_from_block_group(allocate_len, found_bg);
if (block == EXT4_ALLOCATE_FAILED) {
error("failed to allocate %d blocks in block group %d", allocate_len, found_bg);
return NULL;
}
reg = malloc(sizeof(struct region));
reg->block = block;
reg->len = allocate_len;
reg->next = NULL;
reg->prev = NULL;
reg->bg = found_bg;
return reg;
} else {
error("failed to allocate %u blocks, out of space?", len);
}
return NULL;
}
static struct region *ext4_allocate_best_fit(u32 len)
{
struct region *first_reg = NULL;
struct region *prev_reg = NULL;
struct region *reg;
while (len > 0) {
reg = ext4_allocate_best_fit_partial(len);
if (reg == NULL)
return NULL;
if (first_reg == NULL)
first_reg = reg;
if (prev_reg) {
prev_reg->next = reg;
reg->prev = prev_reg;
}
prev_reg = reg;
len -= reg->len;
}
return first_reg;
}
/* Allocate len blocks. The blocks may be spread across multiple block groups,
and are returned in a linked list of the blocks in each block group. The
allocation algorithm is:
1. If the remaining allocation is larger than any available contiguous region,
allocate the largest contiguous region and loop
2. Otherwise, allocate the smallest contiguous region that it fits in
*/
struct block_allocation *allocate_blocks(u32 len)
{
struct region *reg = ext4_allocate_best_fit(len);
if (reg == NULL)
return NULL;
struct block_allocation *alloc = create_allocation();
alloc->list.first = reg;
alloc->list.last = reg;
alloc->list.iter = alloc->list.first;
alloc->list.partial_iter = 0;
return alloc;
}
/* Returns the number of discontiguous regions used by an allocation */
int block_allocation_num_regions(struct block_allocation *alloc)
{
unsigned int i;
struct region *reg = alloc->list.first;
for (i = 0; reg != NULL; reg = reg->next)
i++;
return i;
}
int block_allocation_len(struct block_allocation *alloc)
{
unsigned int i;
struct region *reg = alloc->list.first;
for (i = 0; reg != NULL; reg = reg->next)
i += reg->len;
return i;
}
/* Returns the block number of the block'th block in an allocation */
u32 get_block(struct block_allocation *alloc, u32 block)
{
struct region *reg = alloc->list.iter;
block += alloc->list.partial_iter;
for (; reg; reg = reg->next) {
if (block < reg->len)
return reg->block + block;
block -= reg->len;
}
return EXT4_ALLOCATE_FAILED;
}
u32 get_oob_block(struct block_allocation *alloc, u32 block)
{
struct region *reg = alloc->oob_list.iter;
block += alloc->oob_list.partial_iter;
for (; reg; reg = reg->next) {
if (block < reg->len)
return reg->block + block;
block -= reg->len;
}
return EXT4_ALLOCATE_FAILED;
}
/* Gets the starting block and length in blocks of the first region
of an allocation */
void get_region(struct block_allocation *alloc, u32 *block, u32 *len)
{
*block = alloc->list.iter->block;
*len = alloc->list.iter->len - alloc->list.partial_iter;
}
/* Move to the next region in an allocation */
void get_next_region(struct block_allocation *alloc)
{
alloc->list.iter = alloc->list.iter->next;
alloc->list.partial_iter = 0;
}
/* Returns the number of free blocks in a block group */
u32 get_free_blocks(u32 bg)
{
return aux_info.bgs[bg].free_blocks;
}
int last_region(struct block_allocation *alloc)
{
return (alloc->list.iter == NULL);
}
void rewind_alloc(struct block_allocation *alloc)
{
alloc->list.iter = alloc->list.first;
alloc->list.partial_iter = 0;
}
static struct region *do_split_allocation(struct block_allocation *alloc, u32 len)
{
struct region *reg = alloc->list.iter;
struct region *new;
struct region *tmp;
while (reg && len >= reg->len) {
len -= reg->len;
reg = reg->next;
}
if (reg == NULL && len > 0)
return NULL;
if (len > 0) {
new = malloc(sizeof(struct region));
new->bg = reg->bg;
new->block = reg->block + len;
new->len = reg->len - len;
new->next = reg->next;
new->prev = reg;
reg->next = new;
reg->len = len;
tmp = alloc->list.iter;
alloc->list.iter = new;
return tmp;
} else {
return reg;
}
}
/* Splits an allocation into two allocations. The returned allocation will
point to the first half, and the original allocation ptr will point to the
second half. */
static struct region *split_allocation(struct block_allocation *alloc, u32 len)
{
/* First make sure there is a split at the current ptr */
do_split_allocation(alloc, alloc->list.partial_iter);
/* Then split off len blocks */
struct region *middle = do_split_allocation(alloc, len);
alloc->list.partial_iter = 0;
return middle;
}
/* Reserve the next blocks for oob data (indirect or extent blocks) */
int reserve_oob_blocks(struct block_allocation *alloc, int blocks)
{
struct region *oob = split_allocation(alloc, blocks);
struct region *next;
if (oob == NULL)
return -1;
while (oob && oob != alloc->list.iter) {
next = oob->next;
region_list_remove(&alloc->list, oob);
region_list_append(&alloc->oob_list, oob);
oob = next;
}
return 0;
}
static int advance_list_ptr(struct region_list *list, int blocks)
{
struct region *reg = list->iter;
while (reg != NULL && blocks > 0) {
if (reg->len > list->partial_iter + blocks) {
list->partial_iter += blocks;
return 0;
}
blocks -= (reg->len - list->partial_iter);
list->partial_iter = 0;
reg = reg->next;
}
if (blocks > 0)
return -1;
return 0;
}
/* Move the allocation pointer forward */
int advance_blocks(struct block_allocation *alloc, int blocks)
{
return advance_list_ptr(&alloc->list, blocks);
}
int advance_oob_blocks(struct block_allocation *alloc, int blocks)
{
return advance_list_ptr(&alloc->oob_list, blocks);
}
int append_oob_allocation(struct block_allocation *alloc, u32 len)
{
struct region *reg = ext4_allocate_best_fit(len);
if (reg == NULL) {
error("failed to allocate %d blocks", len);
return -1;
}
for (; reg; reg = reg->next)
region_list_append(&alloc->oob_list, reg);
return 0;
}
/* Returns an ext4_inode structure for an inode number */
struct ext4_inode *get_inode(u32 inode)
{
inode -= 1;
int bg = inode / info.inodes_per_group;
inode %= info.inodes_per_group;
allocate_bg_inode_table(&aux_info.bgs[bg]);
return (struct ext4_inode *)(aux_info.bgs[bg].inode_table + inode *
info.inode_size);
}
struct ext4_xattr_header *get_xattr_block_for_inode(struct ext4_inode *inode)
{
struct ext4_xattr_header *block = xattr_list_find(inode);
if (block != NULL)
return block;
u32 block_num = allocate_block();
block = calloc(info.block_size, 1);
if (block == NULL) {
error("get_xattr: failed to allocate %d", info.block_size);
return NULL;
}
block->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
block->h_refcount = cpu_to_le32(1);
block->h_blocks = cpu_to_le32(1);
inode->i_blocks_lo = cpu_to_le32(le32_to_cpu(inode->i_blocks_lo) + (info.block_size / 512));
inode->i_file_acl_lo = cpu_to_le32(block_num);
int result = sparse_file_add_data(ext4_sparse_file, block, info.block_size, block_num);
if (result != 0) {
error("get_xattr: sparse_file_add_data failure %d", result);
free(block);
return NULL;
}
xattr_list_insert(inode, block);
return block;
}
/* Mark the first len inodes in a block group as used */
u32 reserve_inodes(int bg, u32 num)
{
unsigned int i;
u32 inode;
if (get_free_inodes(bg) < num)
return EXT4_ALLOCATE_FAILED;
for (i = 0; i < num; i++) {
inode = aux_info.bgs[bg].first_free_inode + i - 1;
aux_info.bgs[bg].inode_bitmap[inode / 8] |= 1 << (inode % 8);
}
inode = aux_info.bgs[bg].first_free_inode;
aux_info.bgs[bg].first_free_inode += num;
aux_info.bgs[bg].free_inodes -= num;
return inode;
}
/* Returns the first free inode number
TODO: Inodes should be allocated in the block group of the data? */
u32 allocate_inode()
{
unsigned int bg;
u32 inode;
for (bg = 0; bg < aux_info.groups; bg++) {
inode = reserve_inodes(bg, 1);
if (inode != EXT4_ALLOCATE_FAILED)
return bg * info.inodes_per_group + inode;
}
return EXT4_ALLOCATE_FAILED;
}
/* Returns the number of free inodes in a block group */
u32 get_free_inodes(u32 bg)
{
return aux_info.bgs[bg].free_inodes;
}
/* Increments the directory count of the block group that contains inode */
void add_directory(u32 inode)
{
int bg = (inode - 1) / info.inodes_per_group;
aux_info.bgs[bg].used_dirs += 1;
}
/* Returns the number of inodes in a block group that are directories */
u16 get_directories(int bg)
{
return aux_info.bgs[bg].used_dirs;
}
/* Returns the flags for a block group */
u16 get_bg_flags(int bg)
{
return aux_info.bgs[bg].flags;
}
/* Frees the memory used by a linked list of allocation regions */
void free_alloc(struct block_allocation *alloc)
{
struct region *reg;
reg = alloc->list.first;
while (reg) {
struct region *next = reg->next;
free(reg);
reg = next;
}
reg = alloc->oob_list.first;
while (reg) {
struct region *next = reg->next;
free(reg);
reg = next;
}
free(alloc);
}

74
allocate.h Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ALLOCATE_H_
#define _ALLOCATE_H_
#define EXT4_ALLOCATE_FAILED (u32)(~0)
#include "ext4_utils.h"
struct region;
struct region_list {
struct region *first;
struct region *last;
struct region *iter;
u32 partial_iter;
};
struct block_allocation {
struct region_list list;
struct region_list oob_list;
char* filename;
struct block_allocation* next;
};
void block_allocator_init();
void block_allocator_free();
u32 allocate_block();
struct block_allocation *allocate_blocks(u32 len);
int block_allocation_num_regions(struct block_allocation *alloc);
int block_allocation_len(struct block_allocation *alloc);
struct ext4_inode *get_inode(u32 inode);
struct ext4_xattr_header *get_xattr_block_for_inode(struct ext4_inode *inode);
void reduce_allocation(struct block_allocation *alloc, u32 len);
u32 get_block(struct block_allocation *alloc, u32 block);
u32 get_oob_block(struct block_allocation *alloc, u32 block);
void get_next_region(struct block_allocation *alloc);
void get_region(struct block_allocation *alloc, u32 *block, u32 *len);
u32 get_free_blocks(u32 bg);
u32 get_free_inodes(u32 bg);
u32 reserve_inodes(int bg, u32 inodes);
void add_directory(u32 inode);
u16 get_directories(int bg);
u16 get_bg_flags(int bg);
void init_unused_inode_tables(void);
u32 allocate_inode();
void free_alloc(struct block_allocation *alloc);
int reserve_oob_blocks(struct block_allocation *alloc, int blocks);
int advance_blocks(struct block_allocation *alloc, int blocks);
int advance_oob_blocks(struct block_allocation *alloc, int blocks);
int last_region(struct block_allocation *alloc);
void rewind_alloc(struct block_allocation *alloc);
void append_region(struct block_allocation *alloc,
u32 block, u32 len, int bg);
struct block_allocation *create_allocation();
int append_oob_allocation(struct block_allocation *alloc, u32 len);
void print_blocks(FILE* f, struct block_allocation *alloc);
#endif

109
canned_fs_config.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include "private/android_filesystem_config.h"
#include "canned_fs_config.h"
typedef struct {
const char* path;
unsigned uid;
unsigned gid;
unsigned mode;
uint64_t capabilities;
} Path;
static Path* canned_data = NULL;
static int canned_alloc = 0;
static int canned_used = 0;
static int path_compare(const void* a, const void* b) {
return strcmp(((Path*)a)->path, ((Path*)b)->path);
}
int load_canned_fs_config(const char* fn) {
FILE* f = fopen(fn, "r");
if (f == NULL) {
fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
return -1;
}
char line[PATH_MAX + 200];
while (fgets(line, sizeof(line), f)) {
while (canned_used >= canned_alloc) {
canned_alloc = (canned_alloc+1) * 2;
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
}
Path* p = canned_data + canned_used;
p->path = strdup(strtok(line, " "));
p->uid = atoi(strtok(NULL, " "));
p->gid = atoi(strtok(NULL, " "));
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
p->capabilities = 0;
char* token = NULL;
do {
token = strtok(NULL, " ");
if (token && strncmp(token, "capabilities=", 13) == 0) {
p->capabilities = strtoll(token+13, NULL, 0);
break;
}
} while (token);
canned_used++;
}
fclose(f);
qsort(canned_data, canned_used, sizeof(Path), path_compare);
printf("loaded %d fs_config entries\n", canned_used);
return 0;
}
void canned_fs_config(const char* path, int dir,
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
Path key;
key.path = path+1; // canned paths lack the leading '/'
Path* p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
if (p == NULL) {
fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
exit(1);
}
*uid = p->uid;
*gid = p->gid;
*mode = p->mode;
*capabilities = p->capabilities;
#if 0
// for debugging, run the built-in fs_config and compare the results.
unsigned c_uid, c_gid, c_mode;
uint64_t c_capabilities;
fs_config(path, dir, &c_uid, &c_gid, &c_mode, &c_capabilities);
if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
if (c_capabilities != *capabilities) printf("%s capabilities %llx %llx\n", path, *capabilities, c_capabilities);
#endif
}

26
canned_fs_config.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _CANNED_FS_CONFIG_H
#define _CANNED_FS_CONFIG_H
#include <inttypes.h>
int load_canned_fs_config(const char* fn);
void canned_fs_config(const char* path, int dir,
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities);
#endif

490
contents.c Normal file
View File

@ -0,0 +1,490 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#ifdef HAVE_ANDROID_OS
#include <linux/capability.h>
#else
#include <private/android_filesystem_capability.h>
#endif
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_CAPS_SUFFIX "capability"
#include "ext4_utils.h"
#include "make_ext4fs.h"
#include "allocate.h"
#include "contents.h"
#include "extent.h"
#include "indirect.h"
#ifdef USE_MINGW
#define S_IFLNK 0 /* used by make_link, not needed under mingw */
#endif
static struct block_allocation* saved_allocation_head = NULL;
struct block_allocation* get_saved_allocation_chain() {
return saved_allocation_head;
}
static u32 dentry_size(u32 entries, struct dentry *dentries)
{
u32 len = 24;
unsigned int i;
unsigned int dentry_len;
for (i = 0; i < entries; i++) {
dentry_len = 8 + EXT4_ALIGN(strlen(dentries[i].filename), 4);
if (len % info.block_size + dentry_len > info.block_size)
len += info.block_size - (len % info.block_size);
len += dentry_len;
}
return len;
}
static struct ext4_dir_entry_2 *add_dentry(u8 *data, u32 *offset,
struct ext4_dir_entry_2 *prev, u32 inode, const char *name,
u8 file_type)
{
u8 name_len = strlen(name);
u16 rec_len = 8 + EXT4_ALIGN(name_len, 4);
struct ext4_dir_entry_2 *dentry;
u32 start_block = *offset / info.block_size;
u32 end_block = (*offset + rec_len - 1) / info.block_size;
if (start_block != end_block) {
/* Adding this dentry will cross a block boundary, so pad the previous
dentry to the block boundary */
if (!prev)
critical_error("no prev");
prev->rec_len += end_block * info.block_size - *offset;
*offset = end_block * info.block_size;
}
dentry = (struct ext4_dir_entry_2 *)(data + *offset);
dentry->inode = inode;
dentry->rec_len = rec_len;
dentry->name_len = name_len;
dentry->file_type = file_type;
memcpy(dentry->name, name, name_len);
*offset += rec_len;
return dentry;
}
/* Creates a directory structure for an array of directory entries, dentries,
and stores the location of the structure in an inode. The new inode's
.. link is set to dir_inode_num. Stores the location of the inode number
of each directory entry into dentries[i].inode, to be filled in later
when the inode for the entry is allocated. Returns the inode number of the
new directory */
u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries,
u32 dirs)
{
struct ext4_inode *inode;
u32 blocks;
u32 len;
u32 offset = 0;
u32 inode_num;
u8 *data;
unsigned int i;
struct ext4_dir_entry_2 *dentry;
blocks = DIV_ROUND_UP(dentry_size(entries, dentries), info.block_size);
len = blocks * info.block_size;
if (dir_inode_num) {
inode_num = allocate_inode(info);
} else {
dir_inode_num = EXT4_ROOT_INO;
inode_num = EXT4_ROOT_INO;
}
if (inode_num == EXT4_ALLOCATE_FAILED) {
error("failed to allocate inode\n");
return EXT4_ALLOCATE_FAILED;
}
add_directory(inode_num);
inode = get_inode(inode_num);
if (inode == NULL) {
error("failed to get inode %u", inode_num);
return EXT4_ALLOCATE_FAILED;
}
data = inode_allocate_data_extents(inode, len, len);
if (data == NULL) {
error("failed to allocate %u extents", len);
return EXT4_ALLOCATE_FAILED;
}
inode->i_mode = S_IFDIR;
inode->i_links_count = dirs + 2;
inode->i_flags |= aux_info.default_i_flags;
dentry = NULL;
dentry = add_dentry(data, &offset, NULL, inode_num, ".", EXT4_FT_DIR);
if (!dentry) {
error("failed to add . directory");
return EXT4_ALLOCATE_FAILED;
}
dentry = add_dentry(data, &offset, dentry, dir_inode_num, "..", EXT4_FT_DIR);
if (!dentry) {
error("failed to add .. directory");
return EXT4_ALLOCATE_FAILED;
}
for (i = 0; i < entries; i++) {
dentry = add_dentry(data, &offset, dentry, 0,
dentries[i].filename, dentries[i].file_type);
if (offset > len || (offset == len && i != entries - 1))
critical_error("internal error: dentry for %s ends at %d, past %d\n",
dentries[i].filename, offset, len);
dentries[i].inode = &dentry->inode;
if (!dentry) {
error("failed to add directory");
return EXT4_ALLOCATE_FAILED;
}
}
/* pad the last dentry out to the end of the block */
dentry->rec_len += len - offset;
return inode_num;
}
/* Creates a file on disk. Returns the inode number of the new file */
u32 make_file(const char *filename, u64 len)
{
struct ext4_inode *inode;
u32 inode_num;
inode_num = allocate_inode(info);
if (inode_num == EXT4_ALLOCATE_FAILED) {
error("failed to allocate inode\n");
return EXT4_ALLOCATE_FAILED;
}
inode = get_inode(inode_num);
if (inode == NULL) {
error("failed to get inode %u", inode_num);
return EXT4_ALLOCATE_FAILED;
}
if (len > 0) {
struct block_allocation* alloc = inode_allocate_file_extents(inode, len, filename);
if (alloc) {
alloc->filename = strdup(filename);
alloc->next = saved_allocation_head;
saved_allocation_head = alloc;
}
}
inode->i_mode = S_IFREG;
inode->i_links_count = 1;
inode->i_flags |= aux_info.default_i_flags;
return inode_num;
}
/* Creates a file on disk. Returns the inode number of the new file */
u32 make_link(const char *link)
{
struct ext4_inode *inode;
u32 inode_num;
u32 len = strlen(link);
inode_num = allocate_inode(info);
if (inode_num == EXT4_ALLOCATE_FAILED) {
error("failed to allocate inode\n");
return EXT4_ALLOCATE_FAILED;
}
inode = get_inode(inode_num);
if (inode == NULL) {
error("failed to get inode %u", inode_num);
return EXT4_ALLOCATE_FAILED;
}
inode->i_mode = S_IFLNK;
inode->i_links_count = 1;
inode->i_flags |= aux_info.default_i_flags;
inode->i_size_lo = len;
if (len + 1 <= sizeof(inode->i_block)) {
/* Fast symlink */
memcpy((char*)inode->i_block, link, len);
} else {
u8 *data = inode_allocate_data_indirect(inode, info.block_size, info.block_size);
memcpy(data, link, len);
inode->i_blocks_lo = info.block_size / 512;
}
return inode_num;
}
int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime)
{
struct ext4_inode *inode = get_inode(inode_num);
if (!inode)
return -1;
inode->i_mode |= mode;
inode->i_uid = uid;
inode->i_gid = gid;
inode->i_mtime = mtime;
inode->i_atime = mtime;
inode->i_ctime = mtime;
return 0;
}
/*
* Returns the amount of free space available in the specified
* xattr region
*/
static size_t xattr_free_space(struct ext4_xattr_entry *entry, char *end)
{
while(!IS_LAST_ENTRY(entry) && (((char *) entry) < end)) {
end -= EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size));
entry = EXT4_XATTR_NEXT(entry);
}
if (((char *) entry) > end) {
error("unexpected read beyond end of xattr space");
return 0;
}
return end - ((char *) entry);
}
/*
* Returns a pointer to the free space immediately after the
* last xattr element
*/
static struct ext4_xattr_entry* xattr_get_last(struct ext4_xattr_entry *entry)
{
for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
// skip entry
}
return entry;
}
/*
* assert that the elements in the ext4 xattr section are in sorted order
*
* The ext4 filesystem requires extended attributes to be sorted when
* they're not stored in the inode. The kernel ext4 code uses the following
* sorting algorithm:
*
* 1) First sort extended attributes by their name_index. For example,
* EXT4_XATTR_INDEX_USER (1) comes before EXT4_XATTR_INDEX_SECURITY (6).
* 2) If the name_indexes are equal, then sorting is based on the length
* of the name. For example, XATTR_SELINUX_SUFFIX ("selinux") comes before
* XATTR_CAPS_SUFFIX ("capability") because "selinux" is shorter than "capability"
* 3) If the name_index and name_length are equal, then memcmp() is used to determine
* which name comes first. For example, "selinux" would come before "yelinux".
*
* This method is intended to implement the sorting function defined in
* the Linux kernel file fs/ext4/xattr.c function ext4_xattr_find_entry().
*/
static void xattr_assert_sane(struct ext4_xattr_entry *entry)
{
for( ; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);
if (IS_LAST_ENTRY(next)) {
return;
}
int cmp = next->e_name_index - entry->e_name_index;
if (cmp == 0)
cmp = next->e_name_len - entry->e_name_len;
if (cmp == 0)
cmp = memcmp(next->e_name, entry->e_name, next->e_name_len);
if (cmp < 0) {
error("BUG: extended attributes are not sorted\n");
return;
}
if (cmp == 0) {
error("BUG: duplicate extended attributes detected\n");
return;
}
}
}
#define NAME_HASH_SHIFT 5
#define VALUE_HASH_SHIFT 16
static void ext4_xattr_hash_entry(struct ext4_xattr_header *header,
struct ext4_xattr_entry *entry)
{
u32 hash = 0;
char *name = entry->e_name;
int n;
for (n = 0; n < entry->e_name_len; n++) {
hash = (hash << NAME_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
*name++;
}
if (entry->e_value_block == 0 && entry->e_value_size != 0) {
u32 *value = (u32 *)((char *)header +
le16_to_cpu(entry->e_value_offs));
for (n = (le32_to_cpu(entry->e_value_size) +
EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {
hash = (hash << VALUE_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
le32_to_cpu(*value++);
}
}
entry->e_hash = cpu_to_le32(hash);
}
#undef NAME_HASH_SHIFT
#undef VALUE_HASH_SHIFT
static struct ext4_xattr_entry* xattr_addto_range(
void *block_start,
void *block_end,
struct ext4_xattr_entry *first,
int name_index,
const char *name,
const void *value,
size_t value_len)
{
size_t name_len = strlen(name);
if (name_len > 255)
return NULL;
size_t available_size = xattr_free_space(first, block_end);
size_t needed_size = EXT4_XATTR_LEN(name_len) + EXT4_XATTR_SIZE(value_len);
if (needed_size > available_size)
return NULL;
struct ext4_xattr_entry *new_entry = xattr_get_last(first);
memset(new_entry, 0, EXT4_XATTR_LEN(name_len));
new_entry->e_name_len = name_len;
new_entry->e_name_index = name_index;
memcpy(new_entry->e_name, name, name_len);
new_entry->e_value_block = 0;
new_entry->e_value_size = cpu_to_le32(value_len);
char *val = (char *) new_entry + available_size - EXT4_XATTR_SIZE(value_len);
size_t e_value_offs = val - (char *) block_start;
new_entry->e_value_offs = cpu_to_le16(e_value_offs);
memset(val, 0, EXT4_XATTR_SIZE(value_len));
memcpy(val, value, value_len);
xattr_assert_sane(first);
return new_entry;
}
static int xattr_addto_inode(struct ext4_inode *inode, int name_index,
const char *name, const void *value, size_t value_len)
{
struct ext4_xattr_ibody_header *hdr = (struct ext4_xattr_ibody_header *) (inode + 1);
struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (hdr + 1);
char *block_end = ((char *) inode) + info.inode_size;
struct ext4_xattr_entry *result =
xattr_addto_range(first, block_end, first, name_index, name, value, value_len);
if (result == NULL)
return -1;
hdr->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
inode->i_extra_isize = cpu_to_le16(sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE);
return 0;
}
static int xattr_addto_block(struct ext4_inode *inode, int name_index,
const char *name, const void *value, size_t value_len)
{
struct ext4_xattr_header *header = get_xattr_block_for_inode(inode);
if (!header)
return -1;
struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (header + 1);
char *block_end = ((char *) header) + info.block_size;
struct ext4_xattr_entry *result =
xattr_addto_range(header, block_end, first, name_index, name, value, value_len);
if (result == NULL)
return -1;
ext4_xattr_hash_entry(header, result);
return 0;
}
static int xattr_add(u32 inode_num, int name_index, const char *name,
const void *value, size_t value_len)
{
if (!value)
return 0;
struct ext4_inode *inode = get_inode(inode_num);
if (!inode)
return -1;
int result = xattr_addto_inode(inode, name_index, name, value, value_len);
if (result != 0) {
result = xattr_addto_block(inode, name_index, name, value, value_len);
}
return result;
}
int inode_set_selinux(u32 inode_num, const char *secon)
{
if (!secon)
return 0;
return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY,
XATTR_SELINUX_SUFFIX, secon, strlen(secon) + 1);
}
int inode_set_capabilities(u32 inode_num, uint64_t capabilities) {
if (capabilities == 0)
return 0;
struct vfs_cap_data cap_data;
memset(&cap_data, 0, sizeof(cap_data));
cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
cap_data.data[0].inheritable = 0;
cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
cap_data.data[1].inheritable = 0;
return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY,
XATTR_CAPS_SUFFIX, &cap_data, sizeof(cap_data));
}

45
contents.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
struct dentry {
char *path;
char *full_path;
const char *filename;
char *link;
unsigned long size;
u8 file_type;
u16 mode;
u16 uid;
u16 gid;
u32 *inode;
u32 mtime;
char *secon;
uint64_t capabilities;
};
u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries,
u32 dirs);
u32 make_file(const char *filename, u64 len);
u32 make_link(const char *link);
int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime);
int inode_set_selinux(u32 inode_num, const char *secon);
int inode_set_capabilities(u32 inode_num, uint64_t capabilities);
struct block_allocation* get_saved_allocation_chain();
#endif

58
crc16.c Normal file
View File

@ -0,0 +1,58 @@
/*-
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
* code or tables extracted from it, as desired without restriction.
*/
/* CRC32 code derived from work by Gary S. Brown. */
/* Code taken from FreeBSD 8 */
/* Converted to crc16 */
#include "ext4_utils.h"
static u16 crc16_tab[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
};
u16 ext4_crc16(u16 crc_in, const void *buf, int size)
{
const u8 *p = buf;
u16 crc = crc_in;
while (size--)
crc = crc16_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
return crc;
}

147
e4crypt_static.c Normal file
View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2015 Google, Inc.
*/
#define TAG "ext4_utils"
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/xattr.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <cutils/klog.h>
#include "ext4_crypt.h"
/* keyring keyctl commands */
#define KEYCTL_SETPERM 5 /* set permissions for a key in a keyring */
#define KEYCTL_UNLINK 9 /* unlink a key from a keyring */
#define KEYCTL_SEARCH 10 /* search for a key in a keyring */
#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
#define EXT4_KEYREF_DELIMITER ((char)'.')
/* Validate that all path items are available and accessible. */
static int is_path_valid(const char *path)
{
if (access(path, W_OK)) {
KLOG_ERROR(TAG, "Can't access %s: %s\n",strerror(errno), path);
return 0;
}
return 1;
}
/* Checks whether the policy provided is valid */
static int is_keyref_valid(const char *keyref)
{
char *period = 0;
size_t key_location_len = 0;
/* Key ref must have a key and location delimiter character. */
period = strchr(keyref, EXT4_KEYREF_DELIMITER);
if (!period) {
return 0;
}
/* period must be >= keyref. */
key_location_len = period - keyref;
if (strncmp(keyref, "@t", key_location_len) == 0 ||
strncmp(keyref, "@p", key_location_len) == 0 ||
strncmp(keyref, "@s", key_location_len) == 0 ||
strncmp(keyref, "@u", key_location_len) == 0 ||
strncmp(keyref, "@g", key_location_len) == 0 ||
strncmp(keyref, "@us", key_location_len) == 0)
return 1;
return 0;
}
static int is_dir_empty(const char *dirname)
{
int n = 0;
struct dirent *d;
DIR *dir;
dir = opendir(dirname);
while ((d = readdir(dir)) != NULL) {
if (strcmp(d->d_name, "lost+found") == 0) {
// Skip lost+found directory
} else if (++n > 2) {
break;
}
}
closedir(dir);
return n <= 2;
}
int do_policy_set(const char *directory, const char *policy)
{
struct stat st;
ssize_t ret;
if (!is_keyref_valid(policy)) {
KLOG_ERROR(TAG, "Policy has invalid format.\n");
return -EINVAL;
}
if (!is_path_valid(directory)) {
return -EINVAL;
}
stat(directory, &st);
if (!S_ISDIR(st.st_mode)) {
KLOG_ERROR(TAG, "Can only set policy on a directory (%s)\n", directory);
return -EINVAL;
}
if (!is_dir_empty(directory)) {
KLOG_ERROR(TAG, "Can only set policy on an empty directory (%s)\n", directory);
return -EINVAL;
}
ret = lsetxattr(directory, XATTR_NAME_ENCRYPTION_POLICY, policy,
strlen(policy), 0);
if (ret) {
KLOG_ERROR(TAG, "Failed to set encryption policy for %s: %s\n",
directory, strerror(errno));
return -EINVAL;
}
KLOG_INFO(TAG, "Encryption policy for %s is set to %s\n", directory, policy);
return 0;
}
static long keyctl(int cmd, ...)
{
va_list va;
unsigned long arg2, arg3, arg4, arg5;
va_start(va, cmd);
arg2 = va_arg(va, unsigned long);
arg3 = va_arg(va, unsigned long);
arg4 = va_arg(va, unsigned long);
arg5 = va_arg(va, unsigned long);
va_end(va);
return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
}
key_serial_t add_key(const char *type,
const char *description,
const void *payload,
size_t plen,
key_serial_t ringid)
{
return syscall(__NR_add_key, type, description, payload, plen, ringid);
}
long keyctl_setperm(key_serial_t id, int permissions)
{
return keyctl(KEYCTL_SETPERM, id, permissions);
}

195
ext2simg.c Normal file
View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <libgen.h>
#include <unistd.h>
#include <sparse/sparse.h>
#include "ext4_utils.h"
#include "make_ext4fs.h"
#include "allocate.h"
#if defined(__APPLE__) && defined(__MACH__)
#define off64_t off_t
#endif
#ifndef USE_MINGW /* O_BINARY is windows-specific flag */
#define O_BINARY 0
#endif
extern struct fs_info info;
static int verbose = 0;
static void usage(char *path)
{
fprintf(stderr, "%s [ options ] <image or block device> <output image>\n", path);
fprintf(stderr, "\n");
fprintf(stderr, " -c include CRC block\n");
fprintf(stderr, " -v verbose output\n");
fprintf(stderr, " -z gzip output\n");
fprintf(stderr, " -S don't use sparse output format\n");
}
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(ext4_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(ext4_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(ext4_sparse_file, filename,
(u64)info.block_size * start_block,
info.block_size * len_blocks, start_block);
}
}
return 0;
}
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, verbose);
ext4_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(ext4_sparse_file);
return 0;
}

577
ext4.h Normal file
View File

@ -0,0 +1,577 @@
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
****************************************************************************
****************************************************************************/
#ifndef _EXT4_H
#define _EXT4_H
#include <sys/types.h>
#undef EXT4FS_DEBUG
#ifdef EXT4FS_DEBUG
#define ext4_debug(f, a...) do { printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", __FILE__, __LINE__, __func__); printk(KERN_DEBUG f, ## a); } while (0)
#else
#define ext4_debug(f, a...) do {} while (0)
#endif
#define EXT4_ERROR_INODE(inode, fmt, a...) ext4_error_inode(__func__, (inode), (fmt), ## a);
#define EXT4_ERROR_FILE(file, fmt, a...) ext4_error_file(__func__, (file), (fmt), ## a);
typedef int ext4_grpblk_t;
typedef unsigned long long ext4_fsblk_t;
typedef __u32 ext4_lblk_t;
typedef unsigned int ext4_group_t;
#define EXT4_MB_HINT_MERGE 0x0001
#define EXT4_MB_HINT_RESERVED 0x0002
#define EXT4_MB_HINT_METADATA 0x0004
#define EXT4_MB_HINT_FIRST 0x0008
#define EXT4_MB_HINT_BEST 0x0010
#define EXT4_MB_HINT_DATA 0x0020
#define EXT4_MB_HINT_NOPREALLOC 0x0040
#define EXT4_MB_HINT_GROUP_ALLOC 0x0080
#define EXT4_MB_HINT_GOAL_ONLY 0x0100
#define EXT4_MB_HINT_TRY_GOAL 0x0200
#define EXT4_MB_DELALLOC_RESERVED 0x0400
#define EXT4_MB_STREAM_ALLOC 0x0800
struct ext4_allocation_request {
struct inode *inode;
unsigned int len;
ext4_lblk_t logical;
ext4_lblk_t lleft;
ext4_lblk_t lright;
ext4_fsblk_t goal;
ext4_fsblk_t pleft;
ext4_fsblk_t pright;
unsigned int flags;
};
#define EXT4_BAD_INO 1
#define EXT4_ROOT_INO 2
#define EXT4_BOOT_LOADER_INO 5
#define EXT4_UNDEL_DIR_INO 6
#define EXT4_RESIZE_INO 7
#define EXT4_JOURNAL_INO 8
#define EXT4_GOOD_OLD_FIRST_INO 11
#define EXT4_LINK_MAX 65000
#define EXT4_MIN_BLOCK_SIZE 1024
#define EXT4_MAX_BLOCK_SIZE 65536
#define EXT4_MIN_BLOCK_LOG_SIZE 10
#define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof(__u32))
#define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
#define EXT4_INODE_SIZE(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ? EXT4_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
#define EXT4_FIRST_INO(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ? EXT4_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
#define EXT4_BLOCK_ALIGN(size, blkbits) EXT4_ALIGN((size), (1 << (blkbits)))
struct ext4_group_desc
{
__le32 bg_block_bitmap_lo;
__le32 bg_inode_bitmap_lo;
__le32 bg_inode_table_lo;
__le16 bg_free_blocks_count_lo;
__le16 bg_free_inodes_count_lo;
__le16 bg_used_dirs_count_lo;
__le16 bg_flags;
__u32 bg_reserved[2];
__le16 bg_itable_unused_lo;
__le16 bg_checksum;
__le32 bg_block_bitmap_hi;
__le32 bg_inode_bitmap_hi;
__le32 bg_inode_table_hi;
__le16 bg_free_blocks_count_hi;
__le16 bg_free_inodes_count_hi;
__le16 bg_used_dirs_count_hi;
__le16 bg_itable_unused_hi;
__u32 bg_reserved2[3];
};
#define EXT4_BG_INODE_UNINIT 0x0001
#define EXT4_BG_BLOCK_UNINIT 0x0002
#define EXT4_BG_INODE_ZEROED 0x0004
#define EXT4_MIN_DESC_SIZE 32
#define EXT4_MIN_DESC_SIZE_64BIT 64
#define EXT4_MAX_DESC_SIZE EXT4_MIN_BLOCK_SIZE
#define EXT4_DESC_SIZE(s) (EXT4_SB(s)->s_desc_size)
#define EXT4_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group)
#define EXT4_DESC_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / EXT4_DESC_SIZE(s))
#define EXT4_INODES_PER_GROUP(s) ((s)->s_inodes_per_group)
#define EXT4_NDIR_BLOCKS 12
#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS
#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1)
#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1)
#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1)
#define EXT4_SECRM_FL 0x00000001
#define EXT4_UNRM_FL 0x00000002
#define EXT4_COMPR_FL 0x00000004
#define EXT4_SYNC_FL 0x00000008
#define EXT4_IMMUTABLE_FL 0x00000010
#define EXT4_APPEND_FL 0x00000020
#define EXT4_NODUMP_FL 0x00000040
#define EXT4_NOATIME_FL 0x00000080
#define EXT4_DIRTY_FL 0x00000100
#define EXT4_COMPRBLK_FL 0x00000200
#define EXT4_NOCOMPR_FL 0x00000400
#define EXT4_ECOMPR_FL 0x00000800
#define EXT4_INDEX_FL 0x00001000
#define EXT4_IMAGIC_FL 0x00002000
#define EXT4_JOURNAL_DATA_FL 0x00004000
#define EXT4_NOTAIL_FL 0x00008000
#define EXT4_DIRSYNC_FL 0x00010000
#define EXT4_TOPDIR_FL 0x00020000
#define EXT4_HUGE_FILE_FL 0x00040000
#define EXT4_EXTENTS_FL 0x00080000
#define EXT4_EA_INODE_FL 0x00200000
#define EXT4_EOFBLOCKS_FL 0x00400000
#define EXT4_RESERVED_FL 0x80000000
#define EXT4_FL_USER_VISIBLE 0x004BDFFF
#define EXT4_FL_USER_MODIFIABLE 0x004B80FF
#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL | EXT4_SYNC_FL | EXT4_IMMUTABLE_FL | EXT4_APPEND_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL | EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL | EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL)
#define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
#define EXT4_OTHER_FLMASK (EXT4_NODUMP_FL | EXT4_NOATIME_FL)
struct ext4_new_group_data {
__u32 group;
__u64 block_bitmap;
__u64 inode_bitmap;
__u64 inode_table;
__u32 blocks_count;
__u16 reserved_blocks;
__u16 unused;
__u32 free_blocks_count;
};
#define EXT4_GET_BLOCKS_CREATE 0x0001
#define EXT4_GET_BLOCKS_UNINIT_EXT 0x0002
#define EXT4_GET_BLOCKS_CREATE_UNINIT_EXT (EXT4_GET_BLOCKS_UNINIT_EXT| EXT4_GET_BLOCKS_CREATE)
#define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004
#define EXT4_GET_BLOCKS_PRE_IO 0x0008
#define EXT4_GET_BLOCKS_CONVERT 0x0010
#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO| EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT| EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
#define EXT4_FREE_BLOCKS_METADATA 0x0001
#define EXT4_FREE_BLOCKS_FORGET 0x0002
#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
#define EXT4_IOC_GETFLAGS FS_IOC_GETFLAGS
#define EXT4_IOC_SETFLAGS FS_IOC_SETFLAGS
#define EXT4_IOC_GETVERSION _IOR('f', 3, long)
#define EXT4_IOC_SETVERSION _IOW('f', 4, long)
#define EXT4_IOC_GETVERSION_OLD FS_IOC_GETVERSION
#define EXT4_IOC_SETVERSION_OLD FS_IOC_SETVERSION
#define EXT4_IOC_GETRSVSZ _IOR('f', 5, long)
#define EXT4_IOC_SETRSVSZ _IOW('f', 6, long)
#define EXT4_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
#define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input)
#define EXT4_IOC_MIGRATE _IO('f', 9)
#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12)
#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
#define EXT4_IOC32_GETFLAGS FS_IOC32_GETFLAGS
#define EXT4_IOC32_SETFLAGS FS_IOC32_SETFLAGS
#define EXT4_IOC32_GETVERSION _IOR('f', 3, int)
#define EXT4_IOC32_SETVERSION _IOW('f', 4, int)
#define EXT4_IOC32_GETRSVSZ _IOR('f', 5, int)
#define EXT4_IOC32_SETRSVSZ _IOW('f', 6, int)
#define EXT4_IOC32_GROUP_EXTEND _IOW('f', 7, unsigned int)
#define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION
#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION
#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF
struct ext4_inode {
__le16 i_mode;
__le16 i_uid;
__le32 i_size_lo;
__le32 i_atime;
__le32 i_ctime;
__le32 i_mtime;
__le32 i_dtime;
__le16 i_gid;
__le16 i_links_count;
__le32 i_blocks_lo;
__le32 i_flags;
union {
struct {
__le32 l_i_version;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1;
__le32 i_block[EXT4_N_BLOCKS];
__le32 i_generation;
__le32 i_file_acl_lo;
__le32 i_size_high;
__le32 i_obso_faddr;
union {
struct {
__le16 l_i_blocks_high;
__le16 l_i_file_acl_high;
__le16 l_i_uid_high;
__le16 l_i_gid_high;
__u32 l_i_reserved2;
} linux2;
struct {
__le16 h_i_reserved1;
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__le16 h_i_reserved1;
__le16 m_i_file_acl_high;
__u32 m_i_reserved2[2];
} masix2;
} osd2;
__le16 i_extra_isize;
__le16 i_pad1;
__le32 i_ctime_extra;
__le32 i_mtime_extra;
__le32 i_atime_extra;
__le32 i_crtime;
__le32 i_crtime_extra;
__le32 i_version_hi;
};
struct move_extent {
__u32 reserved;
__u32 donor_fd;
__u64 orig_start;
__u64 donor_start;
__u64 len;
__u64 moved_len;
};
#define EXT4_EPOCH_BITS 2
#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) ((offsetof(typeof(*ext4_inode), field) + sizeof((ext4_inode)->field)) <= (EXT4_GOOD_OLD_INODE_SIZE + (einode)->i_extra_isize))
#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) do { (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) (raw_inode)->xtime ## _extra = ext4_encode_extra_time(&(inode)->xtime); } while (0)
#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) do { if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) (raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec); if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) (raw_inode)->xtime ## _extra = ext4_encode_extra_time(&(einode)->xtime); } while (0)
#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) do { (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) ext4_decode_extra_time(&(inode)->xtime, raw_inode->xtime ## _extra); } while (0)
#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode) do { if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) (einode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) ext4_decode_extra_time(&(einode)->xtime, raw_inode->xtime ## _extra); } while (0)
#define i_disk_version osd1.linux1.l_i_version
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_file_acl_high osd2.linux2.l_i_file_acl_high
#define i_blocks_high osd2.linux2.l_i_blocks_high
#define i_uid_low i_uid
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
#define i_gid_high osd2.linux2.l_i_gid_high
#define i_reserved2 osd2.linux2.l_i_reserved2
#define EXT4_VALID_FS 0x0001
#define EXT4_ERROR_FS 0x0002
#define EXT4_ORPHAN_FS 0x0004
#define EXT2_FLAGS_SIGNED_HASH 0x0001
#define EXT2_FLAGS_UNSIGNED_HASH 0x0002
#define EXT2_FLAGS_TEST_FILESYS 0x0004
#define EXT4_MOUNT_OLDALLOC 0x00002
#define EXT4_MOUNT_GRPID 0x00004
#define EXT4_MOUNT_DEBUG 0x00008
#define EXT4_MOUNT_ERRORS_CONT 0x00010
#define EXT4_MOUNT_ERRORS_RO 0x00020
#define EXT4_MOUNT_ERRORS_PANIC 0x00040
#define EXT4_MOUNT_MINIX_DF 0x00080
#define EXT4_MOUNT_NOLOAD 0x00100
#define EXT4_MOUNT_DATA_FLAGS 0x00C00
#define EXT4_MOUNT_JOURNAL_DATA 0x00400
#define EXT4_MOUNT_ORDERED_DATA 0x00800
#define EXT4_MOUNT_WRITEBACK_DATA 0x00C00
#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000
#define EXT4_MOUNT_NO_UID32 0x02000
#define EXT4_MOUNT_XATTR_USER 0x04000
#define EXT4_MOUNT_POSIX_ACL 0x08000
#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000
#define EXT4_MOUNT_BARRIER 0x20000
#define EXT4_MOUNT_NOBH 0x40000
#define EXT4_MOUNT_QUOTA 0x80000
#define EXT4_MOUNT_USRQUOTA 0x100000
#define EXT4_MOUNT_GRPQUOTA 0x200000
#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000
#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000
#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000
#define EXT4_MOUNT_I_VERSION 0x2000000
#define EXT4_MOUNT_DELALLOC 0x8000000
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000
#define EXT4_MOUNT_DISCARD 0x40000000
#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt
#define test_opt(sb, opt) (EXT4_SB(sb)->s_mount_opt & EXT4_MOUNT_##opt)
#define ext4_set_bit ext2_set_bit
#define ext4_set_bit_atomic ext2_set_bit_atomic
#define ext4_clear_bit ext2_clear_bit
#define ext4_clear_bit_atomic ext2_clear_bit_atomic
#define ext4_test_bit ext2_test_bit
#define ext4_find_first_zero_bit ext2_find_first_zero_bit
#define ext4_find_next_zero_bit ext2_find_next_zero_bit
#define ext4_find_next_bit ext2_find_next_bit
#define EXT4_DFL_MAX_MNT_COUNT 20
#define EXT4_DFL_CHECKINTERVAL 0
#define EXT4_ERRORS_CONTINUE 1
#define EXT4_ERRORS_RO 2
#define EXT4_ERRORS_PANIC 3
#define EXT4_ERRORS_DEFAULT EXT4_ERRORS_CONTINUE
struct ext4_super_block {
__le32 s_inodes_count;
__le32 s_blocks_count_lo;
__le32 s_r_blocks_count_lo;
__le32 s_free_blocks_count_lo;
__le32 s_free_inodes_count;
__le32 s_first_data_block;
__le32 s_log_block_size;
__le32 s_obso_log_frag_size;
__le32 s_blocks_per_group;
__le32 s_obso_frags_per_group;
__le32 s_inodes_per_group;
__le32 s_mtime;
__le32 s_wtime;
__le16 s_mnt_count;
__le16 s_max_mnt_count;
__le16 s_magic;
__le16 s_state;
__le16 s_errors;
__le16 s_minor_rev_level;
__le32 s_lastcheck;
__le32 s_checkinterval;
__le32 s_creator_os;
__le32 s_rev_level;
__le16 s_def_resuid;
__le16 s_def_resgid;
__le32 s_first_ino;
__le16 s_inode_size;
__le16 s_block_group_nr;
__le32 s_feature_compat;
__le32 s_feature_incompat;
__le32 s_feature_ro_compat;
__u8 s_uuid[16];
char s_volume_name[16];
char s_last_mounted[64];
__le32 s_algorithm_usage_bitmap;
__u8 s_prealloc_blocks;
__u8 s_prealloc_dir_blocks;
__le16 s_reserved_gdt_blocks;
__u8 s_journal_uuid[16];
__le32 s_journal_inum;
__le32 s_journal_dev;
__le32 s_last_orphan;
__le32 s_hash_seed[4];
__u8 s_def_hash_version;
__u8 s_reserved_char_pad;
__le16 s_desc_size;
__le32 s_default_mount_opts;
__le32 s_first_meta_bg;
__le32 s_mkfs_time;
__le32 s_jnl_blocks[17];
__le32 s_blocks_count_hi;
__le32 s_r_blocks_count_hi;
__le32 s_free_blocks_count_hi;
__le16 s_min_extra_isize;
__le16 s_want_extra_isize;
__le32 s_flags;
__le16 s_raid_stride;
__le16 s_mmp_interval;
__le64 s_mmp_block;
__le32 s_raid_stripe_width;
__u8 s_log_groups_per_flex;
__u8 s_reserved_char_pad2;
__le16 s_reserved_pad;
__le64 s_kbytes_written;
__u32 s_reserved[160];
};
#define EXT4_SB(sb) (sb)
#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
#define EXT4_OS_LINUX 0
#define EXT4_OS_HURD 1
#define EXT4_OS_MASIX 2
#define EXT4_OS_FREEBSD 3
#define EXT4_OS_LITES 4
#define EXT4_GOOD_OLD_REV 0
#define EXT4_DYNAMIC_REV 1
#define EXT4_CURRENT_REV EXT4_GOOD_OLD_REV
#define EXT4_MAX_SUPP_REV EXT4_DYNAMIC_REV
#define EXT4_GOOD_OLD_INODE_SIZE 128
#define EXT4_HAS_COMPAT_FEATURE(sb,mask) ((EXT4_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask)) != 0)
#define EXT4_HAS_RO_COMPAT_FEATURE(sb,mask) ((EXT4_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask)) != 0)
#define EXT4_HAS_INCOMPAT_FEATURE(sb,mask) ((EXT4_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask)) != 0)
#define EXT4_SET_COMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask)
#define EXT4_SET_RO_COMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask)
#define EXT4_SET_INCOMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask)
#define EXT4_CLEAR_COMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask)
#define EXT4_CLEAR_RO_COMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask)
#define EXT4_CLEAR_INCOMPAT_FEATURE(sb,mask) EXT4_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask)
#define EXT4_FEATURE_COMPAT_DIR_PREALLOC 0x0001
#define EXT4_FEATURE_COMPAT_IMAGIC_INODES 0x0002
#define EXT4_FEATURE_COMPAT_HAS_JOURNAL 0x0004
#define EXT4_FEATURE_COMPAT_EXT_ATTR 0x0008
#define EXT4_FEATURE_COMPAT_RESIZE_INODE 0x0010
#define EXT4_FEATURE_COMPAT_DIR_INDEX 0x0020
#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
#define EXT4_FEATURE_INCOMPAT_RECOVER 0x0004
#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000
#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| EXT4_FEATURE_INCOMPAT_RECOVER| EXT4_FEATURE_INCOMPAT_META_BG| EXT4_FEATURE_INCOMPAT_EXTENTS| EXT4_FEATURE_INCOMPAT_64BIT| EXT4_FEATURE_INCOMPAT_FLEX_BG)
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| EXT4_FEATURE_RO_COMPAT_LARGE_FILE| EXT4_FEATURE_RO_COMPAT_GDT_CSUM| EXT4_FEATURE_RO_COMPAT_DIR_NLINK | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | EXT4_FEATURE_RO_COMPAT_BTREE_DIR | EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
#define EXT4_DEF_RESUID 0
#define EXT4_DEF_RESGID 0
#define EXT4_DEF_INODE_READAHEAD_BLKS 32
#define EXT4_DEFM_DEBUG 0x0001
#define EXT4_DEFM_BSDGROUPS 0x0002
#define EXT4_DEFM_XATTR_USER 0x0004
#define EXT4_DEFM_ACL 0x0008
#define EXT4_DEFM_UID16 0x0010
#define EXT4_DEFM_JMODE 0x0060
#define EXT4_DEFM_JMODE_DATA 0x0020
#define EXT4_DEFM_JMODE_ORDERED 0x0040
#define EXT4_DEFM_JMODE_WBACK 0x0060
#define EXT4_DEF_MIN_BATCH_TIME 0
#define EXT4_DEF_MAX_BATCH_TIME 15000
#define EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME 4
#define EXT4_NAME_LEN 255
struct ext4_dir_entry {
__le32 inode;
__le16 rec_len;
__le16 name_len;
char name[EXT4_NAME_LEN];
};
struct ext4_dir_entry_2 {
__le32 inode;
__le16 rec_len;
__u8 name_len;
__u8 file_type;
char name[EXT4_NAME_LEN];
};
#define EXT4_FT_UNKNOWN 0
#define EXT4_FT_REG_FILE 1
#define EXT4_FT_DIR 2
#define EXT4_FT_CHRDEV 3
#define EXT4_FT_BLKDEV 4
#define EXT4_FT_FIFO 5
#define EXT4_FT_SOCK 6
#define EXT4_FT_SYMLINK 7
#define EXT4_FT_MAX 8
#define EXT4_DIR_PAD 4
#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & ~EXT4_DIR_ROUND)
#define EXT4_MAX_REC_LEN ((1<<16)-1)
#define is_dx(dir) (EXT4_HAS_COMPAT_FEATURE(dir->i_sb, EXT4_FEATURE_COMPAT_DIR_INDEX) && (EXT4_I(dir)->i_flags & EXT4_INDEX_FL))
#define EXT4_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT4_LINK_MAX)
#define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1)
#define DX_HASH_LEGACY 0
#define DX_HASH_HALF_MD4 1
#define DX_HASH_TEA 2
#define DX_HASH_LEGACY_UNSIGNED 3
#define DX_HASH_HALF_MD4_UNSIGNED 4
#define DX_HASH_TEA_UNSIGNED 5
#endif

120
ext4_crypt.cpp Normal file
View File

@ -0,0 +1,120 @@
#define TAG "ext4_utils"
#include "ext4_crypt.h"
#include <string>
#include <fstream>
#include <map>
#include <errno.h>
#include <sys/mount.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
#include "unencrypted_properties.h"
namespace {
std::map<std::string, std::string> s_password_store;
}
bool e4crypt_non_default_key(const char* dir)
{
int type = e4crypt_get_password_type(dir);
// ext4enc:TODO Use consts, not 1 here
return type != -1 && type != 1;
}
int e4crypt_get_password_type(const char* path)
{
UnencryptedProperties props(path);
if (props.Get<std::string>(properties::key).empty()) {
KLOG_INFO(TAG, "No master key, so not ext4enc\n");
return -1;
}
return props.Get<int>(properties::type, 1);
}
int e4crypt_change_password(const char* path, int crypt_type,
const char* password)
{
// ext4enc:TODO Encrypt master key with password securely. Store hash of
// master key for validation
UnencryptedProperties props(path);
if ( props.Set(properties::password, password)
&& props.Set(properties::type, crypt_type))
return 0;
return -1;
}
int e4crypt_crypto_complete(const char* path)
{
KLOG_INFO(TAG, "ext4 crypto complete called on %s\n", path);
if (UnencryptedProperties(path).Get<std::string>(properties::key).empty()) {
KLOG_INFO(TAG, "No master key, so not ext4enc\n");
return -1;
}
return 0;
}
int e4crypt_check_passwd(const char* path, const char* password)
{
UnencryptedProperties props(path);
if (props.Get<std::string>(properties::key).empty()) {
KLOG_INFO(TAG, "No master key, so not ext4enc\n");
return -1;
}
auto actual_password = props.Get<std::string>(properties::password);
if (actual_password == password) {
s_password_store[path] = password;
return 0;
} else {
return -1;
}
}
int e4crypt_restart(const char* path)
{
int rc = 0;
KLOG_INFO(TAG, "ext4 restart called on %s\n", path);
property_set("vold.decrypt", "trigger_reset_main");
KLOG_INFO(TAG, "Just asked init to shut down class main\n");
sleep(2);
std::string tmp_path = std::string() + path + "/tmp_mnt";
// ext4enc:TODO add retry logic
rc = umount(tmp_path.c_str());
if (rc) {
KLOG_ERROR(TAG, "umount %s failed with rc %d, msg %s\n",
tmp_path.c_str(), rc, strerror(errno));
return rc;
}
// ext4enc:TODO add retry logic
rc = umount(path);
if (rc) {
KLOG_ERROR(TAG, "umount %s failed with rc %d, msg %s\n",
path, rc, strerror(errno));
return rc;
}
return 0;
}
const char* e4crypt_get_password(const char* path)
{
// ext4enc:TODO scrub password after timeout
auto i = s_password_store.find(path);
if (i == s_password_store.end()) {
return 0;
} else {
return i->second.c_str();
}
}

50
ext4_crypt.h Normal file
View File

@ -0,0 +1,50 @@
#include <stdbool.h>
#include <sys/cdefs.h>
#include <sys/types.h>
__BEGIN_DECLS
// These functions assume they are being called from init
// They will not operate properly outside of init
int e4crypt_install_keyring();
int e4crypt_install_key(const char* dir);
int e4crypt_create_device_key(const char* dir,
int ensure_dir_exists(const char* dir));
// General functions
bool e4crypt_non_default_key(const char* dir);
int e4crypt_set_directory_policy(const char* dir);
int e4crypt_main(int argc, char* argv[]);
int e4crypt_change_password(const char* path, int crypt_type,
const char* password);
int e4crypt_get_password_type(const char* path);
int e4crypt_crypto_complete(const char* dir);
int e4crypt_check_passwd(const char* dir, const char* password);
const char* e4crypt_get_password(const char* dir);
int e4crypt_restart(const char* dir);
// Key functions. ext4enc:TODO Move to own file
// ext4enc:TODO - get these keyring standard definitions from proper system file
// keyring serial number type
typedef int32_t key_serial_t;
// special process keyring shortcut IDs
#define KEY_SPEC_THREAD_KEYRING -1 // key ID for thread-specific keyring
#define KEY_SPEC_PROCESS_KEYRING -2 // key ID for process-specific keyring
#define KEY_SPEC_SESSION_KEYRING -3 // key ID for session-specific keyring
#define KEY_SPEC_USER_KEYRING -4 // key ID for UID-specific keyring
#define KEY_SPEC_USER_SESSION_KEYRING -5 // key ID for UID-session keyring
#define KEY_SPEC_GROUP_KEYRING -6 // key ID for GID-specific keyring
key_serial_t add_key(const char *type,
const char *description,
const void *payload,
size_t plen,
key_serial_t ringid);
long keyctl_setperm(key_serial_t id, int permissions);
// Set policy on directory
int do_policy_set(const char *directory, const char *policy);
__END_DECLS

View File

@ -0,0 +1,269 @@
#define TAG "ext4_utils"
#include "ext4_crypt.h"
#include <string>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include "unencrypted_properties.h"
// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
#define EXT4_MAX_KEY_SIZE 76
struct ext4_encryption_key {
uint32_t mode;
char raw[EXT4_MAX_KEY_SIZE];
uint32_t size;
};
static const std::string keyring = "@s";
static const std::string arbitrary_sequence_number = "42";
static key_serial_t device_keyring = -1;
static std::string vold_command(std::string const& command)
{
KLOG_INFO(TAG, "Running command %s\n", command.c_str());
int sock = socket_local_client("vold",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (sock < 0) {
KLOG_INFO(TAG, "Cannot open vold, failing command\n");
return "";
}
class CloseSocket
{
int sock_;
public:
CloseSocket(int sock) : sock_(sock) {}
~CloseSocket() { close(sock_); }
};
CloseSocket cs(sock);
// Use arbitrary sequence number. This should only be used when the
// framework is down, so this is (mostly) OK.
std::string actual_command = arbitrary_sequence_number + " " + command;
if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) {
KLOG_ERROR(TAG, "Cannot write command\n");
return "";
}
while (1) {
struct timeval to;
to.tv_sec = 10;
to.tv_usec = 0;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(sock, &read_fds);
int rc = select(sock + 1, &read_fds, NULL, NULL, &to);
if (rc < 0) {
KLOG_ERROR(TAG, "Error in select %s\n", strerror(errno));
return "";
} else if (!rc) {
KLOG_ERROR(TAG, "Timeout\n");
return "";
} else if (FD_ISSET(sock, &read_fds)) {
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
rc = read(sock, buffer, sizeof(buffer));
if (rc <= 0) {
if (rc == 0) {
KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n");
} else {
KLOG_ERROR(TAG, "Error reading data (%s)\n", strerror(errno));
}
return "";
}
// We don't truly know that this is the correct result. However,
// since this will only be used when the framework is down,
// it should be OK unless someone is running vdc at the same time.
// Worst case we force a reboot in the very rare synchronization
// error
return std::string(buffer, rc);
}
}
}
int e4crypt_create_device_key(const char* dir,
int ensure_dir_exists(const char*))
{
// Make sure folder exists. Use make_dir to set selinux permissions.
KLOG_INFO(TAG, "Creating test device key\n");
UnencryptedProperties props(dir);
if (ensure_dir_exists(props.GetPath().c_str())) {
KLOG_ERROR(TAG, "Failed to create %s with error %s\n",
props.GetPath().c_str(), strerror(errno));
return -1;
}
if (props.Get<std::string>(properties::key).empty()) {
// Create new key since it doesn't already exist
std::ifstream urandom("/dev/urandom", std::ifstream::binary);
if (!urandom) {
KLOG_ERROR(TAG, "Failed to open /dev/urandom\n");
return -1;
}
// ext4enc:TODO Don't hardcode 32
std::string key_material(32, '\0');
urandom.read(&key_material[0], key_material.length());
if (!urandom) {
KLOG_ERROR(TAG, "Failed to read random bytes\n");
return -1;
}
if (!props.Set(properties::key, key_material)) {
KLOG_ERROR(TAG, "Failed to write key material\n");
return -1;
}
}
if (!props.Remove(properties::ref)) {
KLOG_ERROR(TAG, "Failed to remove key ref\n");
return -1;
}
return 0;
}
int e4crypt_install_keyring()
{
device_keyring = add_key("keyring",
"e4crypt",
0,
0,
KEY_SPEC_SESSION_KEYRING);
if (device_keyring == -1) {
KLOG_ERROR(TAG, "Failed to create keyring\n");
return -1;
}
KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n",
device_keyring, getpid());
// ext4enc:TODO set correct permissions
long result = keyctl_setperm(device_keyring, 0x3f3f3f3f);
if (result) {
KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result);
return -1;
}
return 0;
}
int e4crypt_install_key(const char* dir)
{
UnencryptedProperties props(dir);
auto key = props.Get<std::string>(properties::key);
// Get password to decrypt as needed
if (e4crypt_non_default_key(dir)) {
std::string result = vold_command("cryptfs getpw");
// result is either
// 200 0 -1
// or
// 200 0 {{sensitive}} 0001020304
// where 0001020304 is hex encoding of password
std::istringstream i(result);
std::string bit;
i >> bit;
if (bit != "200") {
KLOG_ERROR(TAG, "Expecting 200\n");
return -1;
}
i >> bit;
if (bit != arbitrary_sequence_number) {
KLOG_ERROR(TAG, "Expecting %s\n", arbitrary_sequence_number.c_str());
return -1;
}
i >> bit;
if (bit != "{{sensitive}}") {
KLOG_INFO(TAG, "Not encrypted\n");
return -1;
}
i >> bit;
}
// Add key to keyring
ext4_encryption_key ext4_key = {0, {0}, 0};
if (key.length() > sizeof(ext4_key.raw)) {
KLOG_ERROR(TAG, "Key too long\n");
return -1;
}
ext4_key.mode = 0;
memcpy(ext4_key.raw, &key[0], key.length());
ext4_key.size = key.length();
// ext4enc:TODO Use better reference not 1234567890
key_serial_t key_id = add_key("logon", "ext4-key:1234567890",
(void*)&ext4_key, sizeof(ext4_key),
device_keyring);
if (key_id == -1) {
KLOG_ERROR(TAG, "Failed to insert key into keyring with error %s\n",
strerror(errno));
return -1;
}
KLOG_INFO(TAG, "Added key %d to keyring %d in process %d\n",
key_id, device_keyring, getpid());
// ext4enc:TODO set correct permissions
long result = keyctl_setperm(key_id, 0x3f3f3f3f);
if (result) {
KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result);
return -1;
}
// Save reference to key so we can set policy later
if (!props.Set(properties::ref, "ext4-key:1234567890")) {
KLOG_ERROR(TAG, "Cannot save key reference\n");
return -1;
}
return 0;
}
int e4crypt_set_directory_policy(const char* dir)
{
// Only set policy on first level /data directories
// To make this less restrictive, consider using a policy file.
// However this is overkill for as long as the policy is simply
// to apply a global policy to all /data folders created via makedir
if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
return 0;
}
UnencryptedProperties props("/data");
std::string ref = props.Get<std::string>(properties::ref);
std::string policy = keyring + "." + ref;
KLOG_INFO(TAG, "Setting policy %s\n", policy.c_str());
int result = do_policy_set(dir, policy.c_str());
if (result) {
KLOG_ERROR(TAG, "Setting policy on %s failed!\n", dir);
return -1;
}
return 0;
}

88
ext4_extents.h Normal file
View File

@ -0,0 +1,88 @@
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
****************************************************************************
****************************************************************************/
#ifndef _EXT4_EXTENTS
#define _EXT4_EXTENTS
#include "ext4.h"
#define AGGRESSIVE_TEST_
#define EXTENTS_STATS__
#define CHECK_BINSEARCH__
#define EXT_DEBUG__
#ifdef EXT_DEBUG
#define ext_debug(a...) printk(a)
#else
#define ext_debug(a...)
#endif
#define EXT_STATS_
struct ext4_extent {
__le32 ee_block;
__le16 ee_len;
__le16 ee_start_hi;
__le32 ee_start_lo;
};
struct ext4_extent_idx {
__le32 ei_block;
__le32 ei_leaf_lo;
__le16 ei_leaf_hi;
__u16 ei_unused;
};
struct ext4_extent_header {
__le16 eh_magic;
__le16 eh_entries;
__le16 eh_max;
__le16 eh_depth;
__le32 eh_generation;
};
#define EXT4_EXT_MAGIC 0xf30a
struct ext4_ext_path {
ext4_fsblk_t p_block;
__u16 p_depth;
struct ext4_extent *p_ext;
struct ext4_extent_idx *p_idx;
struct ext4_extent_header *p_hdr;
struct buffer_head *p_bh;
};
#define EXT4_EXT_CACHE_NO 0
#define EXT4_EXT_CACHE_GAP 1
#define EXT4_EXT_CACHE_EXTENT 2
#define EXT_CONTINUE 0
#define EXT_BREAK 1
#define EXT_REPEAT 2
#define EXT_MAX_BLOCK 0xffffffff
#define EXT_INIT_MAX_LEN (1UL << 15)
#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1)
#define EXT_FIRST_EXTENT(__hdr__) ((struct ext4_extent *) (((char *) (__hdr__)) + sizeof(struct ext4_extent_header)))
#define EXT_FIRST_INDEX(__hdr__) ((struct ext4_extent_idx *) (((char *) (__hdr__)) + sizeof(struct ext4_extent_header)))
#define EXT_HAS_FREE_INDEX(__path__) (le16_to_cpu((__path__)->p_hdr->eh_entries) < le16_to_cpu((__path__)->p_hdr->eh_max))
#define EXT_LAST_EXTENT(__hdr__) (EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_LAST_INDEX(__hdr__) (EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_MAX_EXTENT(__hdr__) (EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)
#define EXT_MAX_INDEX(__hdr__) (EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)
#endif

59
ext4_kernel_headers.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXT4_UTILS_EXT4_KERNEL_HEADERS_H_
#define _EXT4_UTILS_EXT4_KERNEL_HEADERS_H_
#include <stdint.h>
#ifdef __BIONIC__
#include <sys/types.h>
#else
#define __le64 uint64_t
#define __le32 uint32_t
#define __le16 uint16_t
#define __be64 uint64_t
#define __be32 uint32_t
#define __be16 uint16_t
#define __u64 uint64_t
#define __u32 uint32_t
#define __u16 uint16_t
#define __u8 uint8_t
#endif
#include "ext4.h"
#include "xattr.h"
#include "ext4_extents.h"
#include "jbd2.h"
#ifndef __BIONIC__
#undef __le64
#undef __le32
#undef __le16
#undef __be64
#undef __be32
#undef __be16
#undef __u64
#undef __u32
#undef __u16
#undef __u8
#endif
#endif

47
ext4_sb.c Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include "ext4_sb.h"
int ext4_parse_sb(struct ext4_super_block *sb, struct fs_info *info)
{
uint64_t len_blocks;
if (sb->s_magic != EXT4_SUPER_MAGIC)
return -EINVAL;
if ((sb->s_state & EXT4_VALID_FS) != EXT4_VALID_FS)
return -EINVAL;
info->block_size = 1024 << sb->s_log_block_size;
info->blocks_per_group = sb->s_blocks_per_group;
info->inodes_per_group = sb->s_inodes_per_group;
info->inode_size = sb->s_inode_size;
info->inodes = sb->s_inodes_count;
info->feat_ro_compat = sb->s_feature_ro_compat;
info->feat_compat = sb->s_feature_compat;
info->feat_incompat = sb->s_feature_incompat;
info->bg_desc_reserve_blocks = sb->s_reserved_gdt_blocks;
info->label = sb->s_volume_name;
len_blocks = ((uint64_t)sb->s_blocks_count_hi << 32) +
sb->s_blocks_count_lo;
info->len = (uint64_t)info->block_size * len_blocks;
return 0;
}

52
ext4_sb.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXT4_UTILS_EXT4_SB_H_
#define _EXT4_UTILS_EXT4_SB_H_
#include "ext4_kernel_headers.h"
#define EXT4_SUPER_MAGIC 0xEF53
#ifdef __cplusplus
extern "C" {
#endif
struct fs_info {
int64_t len; /* If set to 0, ask the block device for the size,
* if less than 0, reserve that much space at the
* end of the partition, else use the size given. */
uint32_t block_size;
uint32_t blocks_per_group;
uint32_t inodes_per_group;
uint32_t inode_size;
uint32_t inodes;
uint32_t journal_blocks;
uint16_t feat_ro_compat;
uint16_t feat_compat;
uint16_t feat_incompat;
uint32_t bg_desc_reserve_blocks;
const char *label;
uint8_t no_journal;
};
int ext4_parse_sb(struct ext4_super_block *sb, struct fs_info *info);
#ifdef __cplusplus
}
#endif
#endif

544
ext4_utils.c Normal file
View File

@ -0,0 +1,544 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "uuid.h"
#include "allocate.h"
#include "indirect.h"
#include "extent.h"
#include <sparse/sparse.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stddef.h>
#include <string.h>
#ifdef USE_MINGW
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <sys/ioctl.h>
#endif
#if defined(__linux__)
#include <linux/fs.h>
#elif defined(__APPLE__) && defined(__MACH__)
#include <sys/disk.h>
#endif
int force = 0;
struct fs_info info;
struct fs_aux_info aux_info;
struct sparse_file *ext4_sparse_file;
jmp_buf setjmp_env;
/* returns 1 if a is a power of b */
static int is_power_of(int a, int b)
{
while (a > b) {
if (a % b)
return 0;
a /= b;
}
return (a == b) ? 1 : 0;
}
int bitmap_get_bit(u8 *bitmap, u32 bit)
{
if (bitmap[bit / 8] & (1 << (bit % 8)))
return 1;
return 0;
}
void bitmap_clear_bit(u8 *bitmap, u32 bit)
{
bitmap[bit / 8] &= ~(1 << (bit % 8));
return;
}
/* Returns 1 if the bg contains a backup superblock. On filesystems with
the sparse_super feature, only block groups 0, 1, and powers of 3, 5,
and 7 have backup superblocks. Otherwise, all block groups have backup
superblocks */
int ext4_bg_has_super_block(int bg)
{
/* Without sparse_super, every block group has a superblock */
if (!(info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER))
return 1;
if (bg == 0 || bg == 1)
return 1;
if (is_power_of(bg, 3) || is_power_of(bg, 5) || is_power_of(bg, 7))
return 1;
return 0;
}
/* Function to read the primary superblock */
void read_sb(int fd, struct ext4_super_block *sb)
{
off64_t ret;
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");
}
/* Function to write a primary or backup superblock at a given offset */
void write_sb(int fd, unsigned long long offset, struct ext4_super_block *sb)
{
off64_t ret;
ret = lseek64(fd, offset, SEEK_SET);
if (ret < 0)
critical_error_errno("failed to seek to superblock");
ret = write(fd, sb, sizeof(*sb));
if (ret < 0)
critical_error_errno("failed to write superblock");
if (ret != sizeof(*sb))
critical_error("failed to write all of superblock");
}
/* Write the filesystem image to a file */
void write_ext4_image(int fd, int gz, int sparse, int crc)
{
sparse_file_write(ext4_sparse_file, fd, gz, sparse, crc);
}
/* 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;
}
void ext4_free_fs_aux_info()
{
unsigned int i;
for (i=0; i<aux_info.groups; i++) {
if (aux_info.backup_sb[i])
free(aux_info.backup_sb[i]);
}
free(aux_info.sb);
free(aux_info.bg_desc);
}
/* Fill in the superblock memory buffer based on the filesystem parameters */
void ext4_fill_in_sb()
{
unsigned int i;
struct ext4_super_block *sb = aux_info.sb;
sb->s_inodes_count = info.inodes_per_group * aux_info.groups;
sb->s_blocks_count_lo = aux_info.len_blocks;
sb->s_r_blocks_count_lo = 0;
sb->s_free_blocks_count_lo = 0;
sb->s_free_inodes_count = 0;
sb->s_first_data_block = aux_info.first_data_block;
sb->s_log_block_size = log_2(info.block_size / 1024);
sb->s_obso_log_frag_size = log_2(info.block_size / 1024);
sb->s_blocks_per_group = info.blocks_per_group;
sb->s_obso_frags_per_group = info.blocks_per_group;
sb->s_inodes_per_group = info.inodes_per_group;
sb->s_mtime = 0;
sb->s_wtime = 0;
sb->s_mnt_count = 0;
sb->s_max_mnt_count = 0xFFFF;
sb->s_magic = EXT4_SUPER_MAGIC;
sb->s_state = EXT4_VALID_FS;
sb->s_errors = EXT4_ERRORS_RO;
sb->s_minor_rev_level = 0;
sb->s_lastcheck = 0;
sb->s_checkinterval = 0;
sb->s_creator_os = EXT4_OS_LINUX;
sb->s_rev_level = EXT4_DYNAMIC_REV;
sb->s_def_resuid = EXT4_DEF_RESUID;
sb->s_def_resgid = EXT4_DEF_RESGID;
sb->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;
sb->s_inode_size = info.inode_size;
sb->s_block_group_nr = 0;
sb->s_feature_compat = info.feat_compat;
sb->s_feature_incompat = info.feat_incompat;
sb->s_feature_ro_compat = info.feat_ro_compat;
generate_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid);
memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
strncpy(sb->s_volume_name, info.label, sizeof(sb->s_volume_name));
memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
sb->s_algorithm_usage_bitmap = 0;
sb->s_reserved_gdt_blocks = info.bg_desc_reserve_blocks;
sb->s_prealloc_blocks = 0;
sb->s_prealloc_dir_blocks = 0;
//memcpy(sb->s_journal_uuid, sb->s_uuid, sizeof(sb->s_journal_uuid));
if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
sb->s_journal_inum = EXT4_JOURNAL_INO;
sb->s_journal_dev = 0;
sb->s_last_orphan = 0;
sb->s_hash_seed[0] = 0; /* FIXME */
sb->s_def_hash_version = DX_HASH_TEA;
sb->s_reserved_char_pad = EXT4_JNL_BACKUP_BLOCKS;
sb->s_desc_size = sizeof(struct ext2_group_desc);
sb->s_default_mount_opts = 0; /* FIXME */
sb->s_first_meta_bg = 0;
sb->s_mkfs_time = 0;
//sb->s_jnl_blocks[17]; /* FIXME */
sb->s_blocks_count_hi = aux_info.len_blocks >> 32;
sb->s_r_blocks_count_hi = 0;
sb->s_free_blocks_count_hi = 0;
sb->s_min_extra_isize = sizeof(struct ext4_inode) -
EXT4_GOOD_OLD_INODE_SIZE;
sb->s_want_extra_isize = sizeof(struct ext4_inode) -
EXT4_GOOD_OLD_INODE_SIZE;
sb->s_flags = 2;
sb->s_raid_stride = 0;
sb->s_mmp_interval = 0;
sb->s_mmp_block = 0;
sb->s_raid_stripe_width = 0;
sb->s_log_groups_per_flex = 0;
sb->s_kbytes_written = 0;
for (i = 0; i < aux_info.groups; i++) {
u64 group_start_block = aux_info.first_data_block + i *
info.blocks_per_group;
u32 header_size = 0;
if (ext4_bg_has_super_block(i)) {
if (i != 0) {
aux_info.backup_sb[i] = calloc(info.block_size, 1);
memcpy(aux_info.backup_sb[i], sb, info.block_size);
/* Update the block group nr of this backup superblock */
aux_info.backup_sb[i]->s_block_group_nr = i;
sparse_file_add_data(ext4_sparse_file, aux_info.backup_sb[i],
info.block_size, group_start_block);
}
sparse_file_add_data(ext4_sparse_file, aux_info.bg_desc,
aux_info.bg_desc_blocks * info.block_size,
group_start_block + 1);
header_size = 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
}
aux_info.bg_desc[i].bg_block_bitmap = group_start_block + header_size;
aux_info.bg_desc[i].bg_inode_bitmap = group_start_block + header_size + 1;
aux_info.bg_desc[i].bg_inode_table = group_start_block + header_size + 2;
aux_info.bg_desc[i].bg_free_blocks_count = sb->s_blocks_per_group;
aux_info.bg_desc[i].bg_free_inodes_count = sb->s_inodes_per_group;
aux_info.bg_desc[i].bg_used_dirs_count = 0;
}
}
void ext4_queue_sb(void)
{
/* The write_data* functions expect only block aligned calls.
* This is not an issue, except when we write out the super
* block on a system with a block size > 1K. So, we need to
* deal with that here.
*/
if (info.block_size > 1024) {
u8 *buf = calloc(info.block_size, 1);
memcpy(buf + 1024, (u8*)aux_info.sb, 1024);
sparse_file_add_data(ext4_sparse_file, buf, info.block_size, 0);
} else {
sparse_file_add_data(ext4_sparse_file, aux_info.sb, 1024, 1);
}
}
void ext4_parse_sb_info(struct ext4_super_block *sb)
{
if (sb->s_magic != EXT4_SUPER_MAGIC)
error("superblock magic incorrect");
if ((sb->s_state & EXT4_VALID_FS) != EXT4_VALID_FS)
error("filesystem state not valid");
ext4_parse_sb(sb, &info);
ext4_create_fs_aux_info();
memcpy(aux_info.sb, sb, sizeof(*sb));
if (aux_info.first_data_block != sb->s_first_data_block)
critical_error("first data block does not match");
}
void ext4_create_resize_inode()
{
struct block_allocation *reserve_inode_alloc = create_allocation();
u32 reserve_inode_len = 0;
unsigned int i;
struct ext4_inode *inode = get_inode(EXT4_RESIZE_INO);
if (inode == NULL) {
error("failed to get resize inode");
return;
}
for (i = 0; i < aux_info.groups; i++) {
if (ext4_bg_has_super_block(i)) {
u64 group_start_block = aux_info.first_data_block + i *
info.blocks_per_group;
u32 reserved_block_start = group_start_block + 1 +
aux_info.bg_desc_blocks;
u32 reserved_block_len = info.bg_desc_reserve_blocks;
append_region(reserve_inode_alloc, reserved_block_start,
reserved_block_len, i);
reserve_inode_len += reserved_block_len;
}
}
inode_attach_resize(inode, reserve_inode_alloc);
inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
inode->i_links_count = 1;
free_alloc(reserve_inode_alloc);
}
/* Allocate the blocks to hold a journal inode and connect them to the
reserved journal inode */
void ext4_create_journal_inode()
{
struct ext4_inode *inode = get_inode(EXT4_JOURNAL_INO);
if (inode == NULL) {
error("failed to get journal inode");
return;
}
u8 *journal_data = inode_allocate_data_extents(inode,
info.journal_blocks * info.block_size,
info.journal_blocks * info.block_size);
if (!journal_data) {
error("failed to allocate extents for journal data");
return;
}
inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
inode->i_links_count = 1;
journal_superblock_t *jsb = (journal_superblock_t *)journal_data;
jsb->s_header.h_magic = htonl(JBD2_MAGIC_NUMBER);
jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2);
jsb->s_blocksize = htonl(info.block_size);
jsb->s_maxlen = htonl(info.journal_blocks);
jsb->s_nr_users = htonl(1);
jsb->s_first = htonl(1);
jsb->s_sequence = htonl(1);
memcpy(aux_info.sb->s_jnl_blocks, &inode->i_block, sizeof(inode->i_block));
}
/* Update the number of free blocks and inodes in the filesystem and in each
block group */
void ext4_update_free()
{
u32 i;
for (i = 0; i < aux_info.groups; i++) {
u32 bg_free_blocks = get_free_blocks(i);
u32 bg_free_inodes = get_free_inodes(i);
u16 crc;
aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks;
aux_info.sb->s_free_blocks_count_lo += bg_free_blocks;
aux_info.bg_desc[i].bg_free_inodes_count = bg_free_inodes;
aux_info.sb->s_free_inodes_count += bg_free_inodes;
aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i);
aux_info.bg_desc[i].bg_flags = get_bg_flags(i);
crc = ext4_crc16(~0, aux_info.sb->s_uuid, sizeof(aux_info.sb->s_uuid));
crc = ext4_crc16(crc, &i, sizeof(i));
crc = ext4_crc16(crc, &aux_info.bg_desc[i], offsetof(struct ext2_group_desc, bg_checksum));
aux_info.bg_desc[i].bg_checksum = crc;
}
}
u64 get_block_device_size(int fd)
{
u64 size = 0;
int ret;
#if defined(__linux__)
ret = ioctl(fd, BLKGETSIZE64, &size);
#elif defined(__APPLE__) && defined(__MACH__)
ret = ioctl(fd, DKIOCGETBLOCKCOUNT, &size);
#else
close(fd);
return 0;
#endif
if (ret)
return 0;
return size;
}
int is_block_device_fd(int fd)
{
#ifdef USE_MINGW
return 0;
#else
struct stat st;
int ret = fstat(fd, &st);
if (ret < 0)
return 0;
return S_ISBLK(st.st_mode);
#endif
}
u64 get_file_size(int fd)
{
struct stat buf;
int ret;
u64 reserve_len = 0;
s64 computed_size;
ret = fstat(fd, &buf);
if (ret)
return 0;
if (info.len < 0)
reserve_len = -info.len;
if (S_ISREG(buf.st_mode))
computed_size = buf.st_size - reserve_len;
else if (S_ISBLK(buf.st_mode))
computed_size = get_block_device_size(fd) - reserve_len;
else
computed_size = 0;
if (computed_size < 0) {
warn("Computed filesystem size less than 0");
computed_size = 0;
}
return computed_size;
}
u64 parse_num(const char *arg)
{
char *endptr;
u64 num = strtoull(arg, &endptr, 10);
if (*endptr == 'k' || *endptr == 'K')
num *= 1024LL;
else if (*endptr == 'm' || *endptr == 'M')
num *= 1024LL * 1024LL;
else if (*endptr == 'g' || *endptr == 'G')
num *= 1024LL * 1024LL * 1024LL;
return num;
}
int read_ext(int fd, int verbose)
{
off64_t ret;
struct ext4_super_block sb;
read_sb(fd, &sb);
ext4_parse_sb_info(&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: %"PRIu64"\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: %"PRIu64"\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;
}

170
ext4_utils.h Normal file
View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXT4_UTILS_H_
#define _EXT4_UTILS_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <stdint.h>
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define ftruncate64 ftruncate
#define mmap64 mmap
#define off64_t off_t
#endif
#include "ext4_sb.h"
extern int force;
#define warn(fmt, args...) do { fprintf(stderr, "warning: %s: " fmt "\n", __func__, ## args); } while (0)
#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); if (!force) longjmp(setjmp_env, EXIT_FAILURE); } while (0)
#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
#define critical_error(fmt, args...) do { fprintf(stderr, "critical error: %s: " fmt "\n", __func__, ## args); longjmp(setjmp_env, EXIT_FAILURE); } while (0)
#define critical_error_errno(s, args...) critical_error(s ": %s", ##args, strerror(errno))
#define EXT4_JNL_BACKUP_BLOCKS 1
#ifndef min /* already defined by windows.h */
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
#define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
/* XXX */
#define cpu_to_le32(x) (x)
#define cpu_to_le16(x) (x)
#define le32_to_cpu(x) (x)
#define le16_to_cpu(x) (x)
#ifdef __LP64__
typedef unsigned long u64;
typedef signed long s64;
#else
typedef unsigned long long u64;
typedef signed long long s64;
#endif
typedef unsigned int u32;
typedef unsigned short int u16;
typedef unsigned char u8;
struct block_group_info;
struct xattr_list_element;
struct ext2_group_desc {
u32 bg_block_bitmap;
u32 bg_inode_bitmap;
u32 bg_inode_table;
u16 bg_free_blocks_count;
u16 bg_free_inodes_count;
u16 bg_used_dirs_count;
u16 bg_flags;
u32 bg_reserved[2];
u16 bg_reserved16;
u16 bg_checksum;
};
struct fs_aux_info {
struct ext4_super_block *sb;
struct ext4_super_block **backup_sb;
struct ext2_group_desc *bg_desc;
struct block_group_info *bgs;
struct xattr_list_element *xattrs;
u32 first_data_block;
u64 len_blocks;
u32 inode_table_blocks;
u32 groups;
u32 bg_desc_blocks;
u32 default_i_flags;
u32 blocks_per_ind;
u32 blocks_per_dind;
u32 blocks_per_tind;
};
extern struct fs_info info;
extern struct fs_aux_info aux_info;
extern struct sparse_file *ext4_sparse_file;
extern jmp_buf setjmp_env;
static inline int log_2(int j)
{
int i;
for (i = 0; j > 0; i++)
j >>= 1;
return i - 1;
}
int bitmap_get_bit(u8 *bitmap, u32 bit);
void bitmap_clear_bit(u8 *bitmap, u32 bit);
int ext4_bg_has_super_block(int bg);
void read_sb(int fd, struct ext4_super_block *sb);
void write_sb(int fd, unsigned long long offset, struct ext4_super_block *sb);
void write_ext4_image(int fd, int gz, int sparse, int crc);
void ext4_create_fs_aux_info(void);
void ext4_free_fs_aux_info(void);
void ext4_fill_in_sb(void);
void ext4_create_resize_inode(void);
void ext4_create_journal_inode(void);
void ext4_update_free(void);
void ext4_queue_sb(void);
u64 get_block_device_size(int fd);
int is_block_device_fd(int fd);
u64 get_file_size(int fd);
u64 parse_num(const char *arg);
void ext4_parse_sb_info(struct ext4_super_block *sb);
u16 ext4_crc16(u16 crc_in, const void *buf, int size);
typedef void (*fs_config_func_t)(const char *path, int dir, unsigned *uid, unsigned *gid,
unsigned *mode, uint64_t *capabilities);
struct selabel_handle;
int make_ext4fs_internal(int fd, const char *directory,
const char *mountpoint, fs_config_func_t fs_config_func, int gzip,
int sparse, int crc, int wipe,
struct selabel_handle *sehnd, int verbose, time_t fixed_time,
FILE* block_list_file);
int read_ext(int fd, int verbose);
#ifdef __cplusplus
}
#endif
#endif

811
ext4fixup.c Normal file
View File

@ -0,0 +1,811 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "make_ext4fs.h"
#include "ext4_extents.h"
#include "allocate.h"
#include "ext4fixup.h"
#include <sparse/sparse.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef USE_MINGW
#include <sys/mman.h>
#endif
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
#endif
/* The inode block count for a file/directory is in units of 512 byte blocks,
* _NOT_ the filesystem block size!
*/
#define INODE_BLOCK_SIZE 512
#define MAX_EXT4_BLOCK_SIZE 4096
/* The two modes the recurse_dir() can be in */
#define SANITY_CHECK_PASS 1
#define MARK_INODE_NUMS 2
#define UPDATE_INODE_NUMS 3
/* Magic numbers to indicate what state the update process is in */
#define MAGIC_STATE_MARKING_INUMS 0x7000151515565512ll
#define MAGIC_STATE_UPDATING_INUMS 0x6121131211735123ll
#define MAGIC_STATE_UPDATING_SB 0x15e1715151558477ll
/* Internal state variables corresponding to the magic numbers */
#define STATE_UNSET 0
#define STATE_MARKING_INUMS 1
#define STATE_UPDATING_INUMS 2
#define STATE_UPDATING_SB 3
/* Used for automated testing of this programs ability to stop and be restarted wthout error */
static int bail_phase = 0;
static int bail_loc = 0;
static int bail_count = 0;
static int count = 0;
/* global flags */
static int verbose = 0;
static int no_write = 0;
static int new_inodes_per_group = 0;
static int no_write_fixup_state = 0;
static int compute_new_inum(unsigned int old_inum)
{
unsigned int group, offset;
group = (old_inum - 1) / info.inodes_per_group;
offset = (old_inum -1) % info.inodes_per_group;
return (group * new_inodes_per_group) + offset + 1;
}
static int get_fs_fixup_state(int fd)
{
unsigned long long magic;
int ret, len;
if (no_write) {
return no_write_fixup_state;
}
lseek64(fd, 0, SEEK_SET);
len = read(fd, &magic, sizeof(magic));
if (len != sizeof(magic)) {
critical_error("cannot read fixup_state\n");
}
switch (magic) {
case MAGIC_STATE_MARKING_INUMS:
ret = STATE_MARKING_INUMS;
break;
case MAGIC_STATE_UPDATING_INUMS:
ret = STATE_UPDATING_INUMS;
break;
case MAGIC_STATE_UPDATING_SB:
ret = STATE_UPDATING_SB;
break;
default:
ret = STATE_UNSET;
}
return ret;
}
static int set_fs_fixup_state(int fd, int state)
{
unsigned long long magic;
struct ext4_super_block sb;
int len;
if (no_write) {
no_write_fixup_state = state;
return 0;
}
switch (state) {
case STATE_MARKING_INUMS:
magic = MAGIC_STATE_MARKING_INUMS;
break;
case STATE_UPDATING_INUMS:
magic = MAGIC_STATE_UPDATING_INUMS;
break;
case STATE_UPDATING_SB:
magic = MAGIC_STATE_UPDATING_SB;
break;
case STATE_UNSET:
default:
magic = 0ll;
break;
}
lseek64(fd, 0, SEEK_SET);
len = write(fd, &magic, sizeof(magic));
if (len != sizeof(magic)) {
critical_error("cannot write fixup_state\n");
}
read_sb(fd, &sb);
if (magic) {
/* If we are in the process of updating the filesystem, make it unmountable */
sb.s_desc_size |= 1;
} else {
/* we are done, so make the filesystem mountable again */
sb.s_desc_size &= ~1;
}
if (!no_write) {
write_sb(fd, 1024, &sb);
}
return 0;
}
static int read_inode(int fd, unsigned int inum, struct ext4_inode *inode)
{
unsigned int bg_num, bg_offset;
off64_t inode_offset;
int len;
bg_num = (inum-1) / info.inodes_per_group;
bg_offset = (inum-1) % info.inodes_per_group;
inode_offset = ((unsigned long long)aux_info.bg_desc[bg_num].bg_inode_table * info.block_size) +
(bg_offset * info.inode_size);
if (lseek64(fd, inode_offset, SEEK_SET) < 0) {
critical_error_errno("failed to seek to inode %d\n", inum);
}
len=read(fd, inode, sizeof(*inode));
if (len != sizeof(*inode)) {
critical_error_errno("failed to read inode %d\n", inum);
}
return 0;
}
static int read_block(int fd, unsigned long long block_num, void *block)
{
off64_t off;
unsigned int len;
off = block_num * info.block_size;
if (lseek64(fd, off, SEEK_SET) , 0) {
critical_error_errno("failed to seek to block %lld\n", block_num);
}
len=read(fd, block, info.block_size);
if (len != info.block_size) {
critical_error_errno("failed to read block %lld\n", block_num);
}
return 0;
}
static int write_block(int fd, unsigned long long block_num, void *block)
{
off64_t off;
unsigned int len;
if (no_write) {
return 0;
}
off = block_num * info.block_size;
if (lseek64(fd, off, SEEK_SET) < 0) {
critical_error_errno("failed to seek to block %lld\n", block_num);
}
len=write(fd, block, info.block_size);
if (len != info.block_size) {
critical_error_errno("failed to write block %lld\n", block_num);
}
return 0;
}
static void check_inode_bitmap(int fd, unsigned int bg_num)
{
unsigned int inode_bitmap_block_num;
unsigned char block[MAX_EXT4_BLOCK_SIZE];
int i, bitmap_updated = 0;
/* Using the bg_num, aux_info.bg_desc[], info.inodes_per_group and
* new_inodes_per_group, retrieve the inode bitmap, and make sure
* the bits between the old and new size are clear
*/
inode_bitmap_block_num = aux_info.bg_desc[bg_num].bg_inode_bitmap;
read_block(fd, inode_bitmap_block_num, block);
for (i = info.inodes_per_group; i < new_inodes_per_group; i++) {
if (bitmap_get_bit(block, i)) {
bitmap_clear_bit(block, i);
bitmap_updated = 1;
}
}
if (bitmap_updated) {
if (verbose) {
printf("Warning: updated inode bitmap for block group %d\n", bg_num);
}
write_block(fd, inode_bitmap_block_num, block);
}
return;
}
/* Update the superblock and bgdesc of the specified block group */
static int update_superblocks_and_bg_desc(int fd, int state)
{
off64_t ret;
struct ext4_super_block sb;
unsigned int num_block_groups, total_new_inodes;
unsigned int i;
read_sb(fd, &sb);
/* Compute how many more inodes are now available */
num_block_groups = DIV_ROUND_UP(aux_info.len_blocks, info.blocks_per_group);
total_new_inodes = num_block_groups * (new_inodes_per_group - sb.s_inodes_per_group);
if (verbose) {
printf("created %d additional inodes\n", total_new_inodes);
}
/* Update the free inodes count in each block group descriptor */
for (i = 0; i < num_block_groups; i++) {
if (state == STATE_UPDATING_SB) {
aux_info.bg_desc[i].bg_free_inodes_count += (new_inodes_per_group - sb.s_inodes_per_group);
}
check_inode_bitmap(fd, i);
}
/* First some sanity checks */
if ((sb.s_inodes_count + total_new_inodes) != (new_inodes_per_group * num_block_groups)) {
critical_error("Failed sanity check on new inode count\n");
}
if (new_inodes_per_group % (info.block_size/info.inode_size)) {
critical_error("Failed sanity check on new inode per group alignment\n");
}
/* Update the free inodes count in the superblock */
sb.s_inodes_count += total_new_inodes;
sb.s_free_inodes_count += total_new_inodes;
sb.s_inodes_per_group = new_inodes_per_group;
for (i = 0; i < aux_info.groups; i++) {
if (ext4_bg_has_super_block(i)) {
unsigned int sb_offset;
if (i == 0) {
/* The first superblock is offset by 1K to leave room for boot sectors */
sb_offset = 1024;
} else {
sb_offset = 0;
}
sb.s_block_group_nr = i;
/* Don't write out the backup superblocks with the bit set in the s_desc_size
* which prevents the filesystem from mounting. The bit for the primary
* superblock will be cleared on the final call to set_fs_fixup_state() */
if (i != 0) {
sb.s_desc_size &= ~1;
}
if (!no_write) {
write_sb(fd,
(unsigned long long)i
* info.blocks_per_group * info.block_size
+ sb_offset,
&sb);
}
ret = lseek64(fd, ((unsigned long long)i * info.blocks_per_group * info.block_size) +
(info.block_size * (aux_info.first_data_block + 1)), SEEK_SET);
if (ret < 0)
critical_error_errno("failed to seek to block group descriptors");
if (!no_write) {
ret = write(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks);
if (ret < 0)
critical_error_errno("failed to write block group descriptors");
if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks)
critical_error("failed to write all of block group descriptors");
}
}
if ((bail_phase == 4) && ((unsigned int)bail_count == i)) {
critical_error("bailing at phase 4\n");
}
}
return 0;
}
static int get_direct_blocks(struct ext4_inode *inode, unsigned long long *block_list,
unsigned int *count)
{
unsigned int i = 0;
unsigned int ret = 0;
unsigned int sectors_per_block;
sectors_per_block = info.block_size / INODE_BLOCK_SIZE;
while ((i < (inode->i_blocks_lo / sectors_per_block)) && (i < EXT4_NDIR_BLOCKS)) {
block_list[i] = inode->i_block[i];
i++;
}
*count += i;
if ((inode->i_blocks_lo / sectors_per_block) > EXT4_NDIR_BLOCKS) {
ret = 1;
}
return ret;
}
static int get_indirect_blocks(int fd, struct ext4_inode *inode,
unsigned long long *block_list, unsigned int *count)
{
unsigned int i;
unsigned int *indirect_block;
unsigned int sectors_per_block;
sectors_per_block = info.block_size / INODE_BLOCK_SIZE;
indirect_block = (unsigned int *)malloc(info.block_size);
if (indirect_block == 0) {
critical_error("failed to allocate memory for indirect_block\n");
}
read_block(fd, inode->i_block[EXT4_NDIR_BLOCKS], indirect_block);
for(i = 0; i < (inode->i_blocks_lo / sectors_per_block - EXT4_NDIR_BLOCKS); i++) {
block_list[EXT4_NDIR_BLOCKS+i] = indirect_block[i];
}
*count += i;
free(indirect_block);
return 0;
}
static int get_block_list_indirect(int fd, struct ext4_inode *inode, unsigned long long *block_list)
{
unsigned int count=0;
if (get_direct_blocks(inode, block_list, &count)) {
get_indirect_blocks(fd, inode, block_list, &count);
}
return count;
}
static int get_extent_ents(struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
{
int i, j;
struct ext4_extent *extent;
off64_t fs_block_num;
if (ext_hdr->eh_depth != 0) {
critical_error("get_extent_ents called with eh_depth != 0\n");
}
/* The extent entries immediately follow the header, so add 1 to the pointer
* and cast it to an extent pointer.
*/
extent = (struct ext4_extent *)(ext_hdr + 1);
for (i = 0; i < ext_hdr->eh_entries; i++) {
fs_block_num = ((off64_t)extent->ee_start_hi << 32) | extent->ee_start_lo;
for (j = 0; j < extent->ee_len; j++) {
block_list[extent->ee_block+j] = fs_block_num+j;
}
extent++;
}
return 0;
}
static int get_extent_idx(int fd, struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
{
int i;
struct ext4_extent_idx *extent_idx;
struct ext4_extent_header *tmp_ext_hdr;
off64_t fs_block_num;
unsigned char block[MAX_EXT4_BLOCK_SIZE];
/* Sanity check */
if (ext_hdr->eh_depth == 0) {
critical_error("get_extent_idx called with eh_depth == 0\n");
}
/* The extent entries immediately follow the header, so add 1 to the pointer
* and cast it to an extent pointer.
*/
extent_idx = (struct ext4_extent_idx *)(ext_hdr + 1);
for (i = 0; i < ext_hdr->eh_entries; i++) {
fs_block_num = ((off64_t)extent_idx->ei_leaf_hi << 32) | extent_idx->ei_leaf_lo;
read_block(fd, fs_block_num, block);
tmp_ext_hdr = (struct ext4_extent_header *)block;
if (tmp_ext_hdr->eh_depth == 0) {
get_extent_ents(tmp_ext_hdr, block_list); /* leaf node, fill in block_list */
} else {
get_extent_idx(fd, tmp_ext_hdr, block_list); /* recurse down the tree */
}
}
return 0;
}
static int get_block_list_extents(int fd, struct ext4_inode *inode, unsigned long long *block_list)
{
struct ext4_extent_header *extent_hdr;
extent_hdr = (struct ext4_extent_header *)inode->i_block;
if (extent_hdr->eh_magic != EXT4_EXT_MAGIC) {
critical_error("extent header has unexpected magic value 0x%4.4x\n",
extent_hdr->eh_magic);
}
if (extent_hdr->eh_depth == 0) {
get_extent_ents((struct ext4_extent_header *)inode->i_block, block_list);
return 0;
}
get_extent_idx(fd, (struct ext4_extent_header *)inode->i_block, block_list);
return 0;
}
static int is_entry_dir(int fd, struct ext4_dir_entry_2 *dirp, int pass)
{
struct ext4_inode inode;
int ret = 0;
if (dirp->file_type == EXT4_FT_DIR) {
ret = 1;
} else if (dirp->file_type == EXT4_FT_UNKNOWN) {
/* Somebody was too lazy to fill in the dir entry,
* so we have to go fetch it from the inode. Grrr.
*/
/* if UPDATE_INODE_NUMS pass and the inode high bit is not
* set return false so we don't recurse down the tree that is
* already updated. Otherwise, fetch inode, and return answer.
*/
if ((pass == UPDATE_INODE_NUMS) && !(dirp->inode & 0x80000000)) {
ret = 0;
} else {
read_inode(fd, (dirp->inode & 0x7fffffff), &inode);
if (S_ISDIR(inode.i_mode)) {
ret = 1;
}
}
}
return ret;
}
static int recurse_dir(int fd, struct ext4_inode *inode, char *dirbuf, int dirsize, int mode)
{
unsigned long long *block_list;
unsigned int num_blocks;
struct ext4_dir_entry_2 *dirp, *prev_dirp = 0;
char name[256];
unsigned int i, leftover_space, is_dir;
struct ext4_inode tmp_inode;
int tmp_dirsize;
char *tmp_dirbuf;
switch (mode) {
case SANITY_CHECK_PASS:
case MARK_INODE_NUMS:
case UPDATE_INODE_NUMS:
break;
default:
critical_error("recurse_dir() called witn unknown mode!\n");
}
if (dirsize % info.block_size) {
critical_error("dirsize %d not a multiple of block_size %d. This is unexpected!\n",
dirsize, info.block_size);
}
num_blocks = dirsize / info.block_size;
block_list = malloc((num_blocks + 1) * sizeof(*block_list));
if (block_list == 0) {
critical_error("failed to allocate memory for block_list\n");
}
if (inode->i_flags & EXT4_EXTENTS_FL) {
get_block_list_extents(fd, inode, block_list);
} else {
/* A directory that requires doubly or triply indirect blocks in huge indeed,
* and will almost certainly not exist, especially since make_ext4fs only creates
* directories with extents, and the kernel will too, but check to make sure the
* directory is not that big and give an error if so. Our limit is 12 direct blocks,
* plus block_size/4 singly indirect blocks, which for a filesystem with 4K blocks
* is a directory 1036 blocks long, or 4,243,456 bytes long! Assuming an average
* filename length of 20 (which I think is generous) thats 20 + 8 bytes overhead
* per entry, or 151,552 entries in the directory!
*/
if (num_blocks > (info.block_size / 4 + EXT4_NDIR_BLOCKS)) {
critical_error("Non-extent based directory is too big!\n");
}
get_block_list_indirect(fd, inode, block_list);
}
/* Read in all the blocks for this directory */
for (i = 0; i < num_blocks; i++) {
read_block(fd, block_list[i], dirbuf + (i * info.block_size));
}
dirp = (struct ext4_dir_entry_2 *)dirbuf;
while (dirp < (struct ext4_dir_entry_2 *)(dirbuf + dirsize)) {
count++;
leftover_space = (char *)(dirbuf + dirsize) - (char *)dirp;
if (((mode == SANITY_CHECK_PASS) || (mode == UPDATE_INODE_NUMS)) &&
(leftover_space <= 8) && prev_dirp) {
/* This is a bug in an older version of make_ext4fs, where it
* didn't properly include the rest of the block in rec_len.
* Update rec_len on the previous entry to include the rest of
* the block and exit the loop.
*/
if (verbose) {
printf("fixing up short rec_len for diretory entry for %s\n", name);
}
prev_dirp->rec_len += leftover_space;
break;
}
if (dirp->inode == 0) {
/* This is the last entry in the directory */
break;
}
strncpy(name, dirp->name, dirp->name_len);
name[dirp->name_len]='\0';
/* Only recurse on pass UPDATE_INODE_NUMS if the high bit is set.
* Otherwise, this inode entry has already been updated
* and we'll do the wrong thing. Also don't recurse on . or ..,
* and certainly not on non-directories!
*/
/* Hrm, looks like filesystems made by fastboot on stingray set the file_type
* flag, but the lost+found directory has the type set to Unknown, which
* seems to imply I need to read the inode and get it.
*/
is_dir = is_entry_dir(fd, dirp, mode);
if ( is_dir && (strcmp(name, ".") && strcmp(name, "..")) &&
((mode == SANITY_CHECK_PASS) || (mode == MARK_INODE_NUMS) ||
((mode == UPDATE_INODE_NUMS) && (dirp->inode & 0x80000000))) ) {
/* A directory! Recurse! */
read_inode(fd, dirp->inode & 0x7fffffff, &tmp_inode);
if (!S_ISDIR(tmp_inode.i_mode)) {
critical_error("inode %d for name %s does not point to a directory\n",
dirp->inode & 0x7fffffff, name);
}
if (verbose) {
printf("inode %d %s use extents\n", dirp->inode & 0x7fffffff,
(tmp_inode.i_flags & EXT4_EXTENTS_FL) ? "does" : "does not");
}
tmp_dirsize = tmp_inode.i_blocks_lo * INODE_BLOCK_SIZE;
if (verbose) {
printf("dir size = %d bytes\n", tmp_dirsize);
}
tmp_dirbuf = malloc(tmp_dirsize);
if (tmp_dirbuf == 0) {
critical_error("failed to allocate memory for tmp_dirbuf\n");
}
recurse_dir(fd, &tmp_inode, tmp_dirbuf, tmp_dirsize, mode);
free(tmp_dirbuf);
}
if (verbose) {
if (is_dir) {
printf("Directory %s\n", name);
} else {
printf("Non-directory %s\n", name);
}
}
/* Process entry based on current mode. Either set high bit or change inode number */
if (mode == MARK_INODE_NUMS) {
dirp->inode |= 0x80000000;
} else if (mode == UPDATE_INODE_NUMS) {
if (dirp->inode & 0x80000000) {
dirp->inode = compute_new_inum(dirp->inode & 0x7fffffff);
}
}
if ((bail_phase == mode) && (bail_loc == 1) && (bail_count == count)) {
critical_error("Bailing at phase %d, loc 1 and count %d\n", mode, count);
}
/* Point dirp at the next entry */
prev_dirp = dirp;
dirp = (struct ext4_dir_entry_2*)((char *)dirp + dirp->rec_len);
}
/* Write out all the blocks for this directory */
for (i = 0; i < num_blocks; i++) {
write_block(fd, block_list[i], dirbuf + (i * info.block_size));
if ((bail_phase == mode) && (bail_loc == 2) && (bail_count <= count)) {
critical_error("Bailing at phase %d, loc 2 and count %d\n", mode, count);
}
}
free(block_list);
return 0;
}
int ext4fixup(char *fsdev)
{
return ext4fixup_internal(fsdev, 0, 0, 0, 0, 0);
}
int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
int stop_phase, int stop_loc, int stop_count)
{
int fd;
struct ext4_inode root_inode;
unsigned int dirsize;
char *dirbuf;
if (setjmp(setjmp_env))
return EXIT_FAILURE; /* Handle a call to longjmp() */
verbose = v_flag;
no_write = n_flag;
bail_phase = stop_phase;
bail_loc = stop_loc;
bail_count = stop_count;
fd = open(fsdev, O_RDWR);
if (fd < 0)
critical_error_errno("failed to open filesystem image");
read_ext(fd, verbose);
if (info.feat_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) {
critical_error("Filesystem needs recovery first, mount and unmount to do that\n");
}
/* Clear the low bit which is set while this tool is in progress.
* If the tool crashes, it will still be set when we restart.
* The low bit is set to make the filesystem unmountable while
* it is being fixed up. Also allow 0, which means the old ext2
* size is in use.
*/
if (((aux_info.sb->s_desc_size & ~1) != sizeof(struct ext2_group_desc)) &&
((aux_info.sb->s_desc_size & ~1) != 0))
critical_error("error: bg_desc_size != sizeof(struct ext2_group_desc)\n");
if ((info.feat_incompat & EXT4_FEATURE_INCOMPAT_FILETYPE) == 0) {
critical_error("Expected filesystem to have filetype flag set\n");
}
#if 0 // If we have to fix the directory rec_len issue, we can't use this check
/* Check to see if the inodes/group is copacetic */
if (info.inodes_per_blockgroup % (info.block_size/info.inode_size) == 0) {
/* This filesystem has either already been updated, or was
* made correctly.
*/
if (verbose) {
printf("%s: filesystem correct, no work to do\n", me);
}
exit(0);
}
#endif
/* Compute what the new value of inodes_per_blockgroup will be when we're done */
new_inodes_per_group=EXT4_ALIGN(info.inodes_per_group,(info.block_size/info.inode_size));
read_inode(fd, EXT4_ROOT_INO, &root_inode);
if (!S_ISDIR(root_inode.i_mode)) {
critical_error("root inode %d does not point to a directory\n", EXT4_ROOT_INO);
}
if (verbose) {
printf("inode %d %s use extents\n", EXT4_ROOT_INO,
(root_inode.i_flags & EXT4_EXTENTS_FL) ? "does" : "does not");
}
dirsize = root_inode.i_blocks_lo * INODE_BLOCK_SIZE;
if (verbose) {
printf("root dir size = %d bytes\n", dirsize);
}
dirbuf = malloc(dirsize);
if (dirbuf == 0) {
critical_error("failed to allocate memory for dirbuf\n");
}
/* Perform a sanity check pass first, try to catch any errors that will occur
* before we actually change anything, so we don't leave a filesystem in a
* corrupted, unrecoverable state. Set no_write, make it quiet, and do a recurse
* pass and a update_superblock pass. Set flags back to requested state when done.
* Only perform sanity check if the state is unset. If the state is _NOT_ unset,
* then the tool has already been run and interrupted, and it presumably ran and
* passed sanity checked before it got interrupted. It is _NOT_ safe to run sanity
* check if state is unset because it assumes inodes are to be computed using the
* old inodes/group, but some inode numbers may be updated to the new number.
*/
if (get_fs_fixup_state(fd) == STATE_UNSET) {
verbose = 0;
no_write = 1;
recurse_dir(fd, &root_inode, dirbuf, dirsize, SANITY_CHECK_PASS);
update_superblocks_and_bg_desc(fd, STATE_UNSET);
verbose = v_flag;
no_write = n_flag;
set_fs_fixup_state(fd, STATE_MARKING_INUMS);
}
if (get_fs_fixup_state(fd) == STATE_MARKING_INUMS) {
count = 0; /* Reset debugging counter */
if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, MARK_INODE_NUMS)) {
set_fs_fixup_state(fd, STATE_UPDATING_INUMS);
}
}
if (get_fs_fixup_state(fd) == STATE_UPDATING_INUMS) {
count = 0; /* Reset debugging counter */
if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, UPDATE_INODE_NUMS)) {
set_fs_fixup_state(fd, STATE_UPDATING_SB);
}
}
if (get_fs_fixup_state(fd) == STATE_UPDATING_SB) {
/* set the new inodes/blockgroup number,
* and sets the state back to 0.
*/
if (!update_superblocks_and_bg_desc(fd, STATE_UPDATING_SB)) {
set_fs_fixup_state(fd, STATE_UNSET);
}
}
close(fd);
return 0;
}

20
ext4fixup.h Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
int ext4fixup(char *fsdev);
int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
int stop_phase, int stop_loc, int stop_count);

68
ext4fixup_main.c Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include "ext4fixup.h"
static void usage(char *me)
{
fprintf(stderr, "%s: usage: %s [-vn] <image or block device>\n", me, me);
}
int main(int argc, char **argv)
{
int opt;
int verbose = 0;
int no_write = 0;
char *fsdev;
char *me;
int stop_phase = 0, stop_loc = 0, stop_count = 0;
me = basename(argv[0]);
while ((opt = getopt(argc, argv, "vnd:")) != -1) {
switch (opt) {
case 'v':
verbose = 1;
break;
case 'n':
no_write = 1;
break;
case 'd':
sscanf(optarg, "%d,%d,%d", &stop_phase, &stop_loc, &stop_count);
break;
}
}
if (optind >= argc) {
fprintf(stderr, "expected image or block device after options\n");
usage(me);
exit(EXIT_FAILURE);
}
fsdev = argv[optind++];
if (optind < argc) {
fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
usage(me);
exit(EXIT_FAILURE);
}
return ext4fixup_internal(fsdev, verbose, no_write, stop_phase, stop_loc, stop_count);
}

232
extent.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "extent.h"
#include <sparse/sparse.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
/* 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, &region_block, &region_len);
len = min(region_len * info.block_size, backing_len);
sparse_file_add_data(ext4_sparse_file, ptr, len, region_block);
ptr += len;
backing_len -= len;
}
return data;
}
/* Queues each chunk of a file to be written to contiguous data block
regions */
static void extent_create_backing_file(struct block_allocation *alloc,
u64 backing_len, const char *filename)
{
off64_t offset = 0;
for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
u32 region_block;
u32 region_len;
u32 len;
get_region(alloc, &region_block, &region_len);
len = min(region_len * info.block_size, backing_len);
sparse_file_add_file(ext4_sparse_file, filename, offset, len,
region_block);
offset += len;
backing_len -= len;
}
}
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");
sparse_file_add_data(ext4_sparse_file, 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 %"PRIu64" 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, &region_block, &region_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;
}
/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
buffer, and connects them to an inode. Returns a pointer to the data
buffer. */
u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len,
u64 backing_len)
{
struct block_allocation *alloc;
u8 *data = NULL;
alloc = do_inode_allocate_extents(inode, len);
if (alloc == NULL) {
error("failed to allocate extents for %"PRIu64" bytes", len);
return NULL;
}
if (backing_len) {
data = extent_create_backing(alloc, backing_len);
if (!data)
error("failed to create backing for %"PRIu64" bytes", backing_len);
}
free_alloc(alloc);
return data;
}
/* Allocates enough blocks to hold len bytes, queues them to be written
from a file, and connects them to an inode. */
struct block_allocation* inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
const char *filename)
{
struct block_allocation *alloc;
alloc = do_inode_allocate_extents(inode, len);
if (alloc == NULL) {
error("failed to allocate extents for %"PRIu64" bytes", len);
return NULL;
}
extent_create_backing_file(alloc, len, filename);
return alloc;
}
/* Allocates enough blocks to hold len bytes and connects them to an inode */
void inode_allocate_extents(struct ext4_inode *inode, u64 len)
{
struct block_allocation *alloc;
alloc = do_inode_allocate_extents(inode, len);
if (alloc == NULL) {
error("failed to allocate extents for %"PRIu64" bytes", len);
return;
}
free_alloc(alloc);
}

30
extent.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXTENT_H_
#define _EXTENT_H_
#include "allocate.h"
#include "ext4_utils.h"
void inode_allocate_extents(struct ext4_inode *inode, u64 len);
struct block_allocation* inode_allocate_file_extents(
struct ext4_inode *inode, u64 len, const char *filename);
u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len,
u64 backing_len);
void free_extent_blocks();
#endif

514
indirect.c Normal file
View File

@ -0,0 +1,514 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "indirect.h"
#include "allocate.h"
#include <sparse/sparse.h>
#include <stdlib.h>
#include <stdio.h>
/* 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, &region_block, &region_len);
len = min(region_len * info.block_size, backing_len);
sparse_file_add_data(ext4_sparse_file, ptr, len, region_block);
ptr += len;
backing_len -= len;
}
return data;
}
static void reserve_indirect_block(struct block_allocation *alloc, int len)
{
if (reserve_oob_blocks(alloc, 1)) {
error("failed to reserve oob block");
return;
}
if (advance_blocks(alloc, len)) {
error("failed to advance %d blocks", len);
return;
}
}
static void reserve_dindirect_block(struct block_allocation *alloc, int len)
{
if (reserve_oob_blocks(alloc, 1)) {
error("failed to reserve oob block");
return;
}
while (len > 0) {
int ind_block_len = min((int)aux_info.blocks_per_ind, len);
reserve_indirect_block(alloc, ind_block_len);
len -= ind_block_len;
}
}
static void reserve_tindirect_block(struct block_allocation *alloc, int len)
{
if (reserve_oob_blocks(alloc, 1)) {
error("failed to reserve oob block");
return;
}
while (len > 0) {
int dind_block_len = min((int)aux_info.blocks_per_dind, len);
reserve_dindirect_block(alloc, dind_block_len);
len -= dind_block_len;
}
}
static void fill_indirect_block(u32 *ind_block, int len, struct block_allocation *alloc)
{
int i;
for (i = 0; i < len; i++) {
ind_block[i] = get_block(alloc, i);
}
}
static void fill_dindirect_block(u32 *dind_block, int len, struct block_allocation *alloc)
{
int i;
u32 ind_block;
for (i = 0; len > 0; i++) {
ind_block = get_oob_block(alloc, 0);
if (advance_oob_blocks(alloc, 1)) {
error("failed to reserve oob block");
return;
}
dind_block[i] = ind_block;
u32 *ind_block_data = calloc(info.block_size, 1);
sparse_file_add_data(ext4_sparse_file, ind_block_data, info.block_size,
ind_block);
int ind_block_len = min((int)aux_info.blocks_per_ind, len);
fill_indirect_block(ind_block_data, ind_block_len, alloc);
if (advance_blocks(alloc, ind_block_len)) {
error("failed to advance %d blocks", ind_block_len);
return;
}
len -= ind_block_len;
}
}
static void fill_tindirect_block(u32 *tind_block, int len, struct block_allocation *alloc)
{
int i;
u32 dind_block;
for (i = 0; len > 0; i++) {
dind_block = get_oob_block(alloc, 0);
if (advance_oob_blocks(alloc, 1)) {
error("failed to reserve oob block");
return;
}
tind_block[i] = dind_block;
u32 *dind_block_data = calloc(info.block_size, 1);
sparse_file_add_data(ext4_sparse_file, dind_block_data, info.block_size,
dind_block);
int dind_block_len = min((int)aux_info.blocks_per_dind, len);
fill_dindirect_block(dind_block_data, dind_block_len, alloc);
len -= dind_block_len;
}
}
/* Given an allocation, attach as many blocks as possible to direct inode
blocks, and return the rest */
static int inode_attach_direct_blocks(struct ext4_inode *inode,
struct block_allocation *alloc, u32 *block_len)
{
int len = min(*block_len, EXT4_NDIR_BLOCKS);
int i;
for (i = 0; i < len; i++) {
inode->i_block[i] = get_block(alloc, i);
}
if (advance_blocks(alloc, len)) {
error("failed to advance %d blocks", len);
return -1;
}
*block_len -= len;
return 0;
}
/* Given an allocation, attach as many blocks as possible to indirect blocks,
and return the rest
Assumes that the blocks necessary to hold the indirect blocks were included
as part of the allocation */
static int inode_attach_indirect_blocks(struct ext4_inode *inode,
struct block_allocation *alloc, u32 *block_len)
{
int len = min(*block_len, aux_info.blocks_per_ind);
int ind_block = get_oob_block(alloc, 0);
inode->i_block[EXT4_IND_BLOCK] = ind_block;
if (advance_oob_blocks(alloc, 1)) {
error("failed to advance oob block");
return -1;
}
u32 *ind_block_data = calloc(info.block_size, 1);
sparse_file_add_data(ext4_sparse_file, ind_block_data, info.block_size,
ind_block);
fill_indirect_block(ind_block_data, len, alloc);
if (advance_blocks(alloc, len)) {
error("failed to advance %d blocks", len);
return -1;
}
*block_len -= len;
return 0;
}
/* Given an allocation, attach as many blocks as possible to doubly indirect
blocks, and return the rest.
Assumes that the blocks necessary to hold the indirect and doubly indirect
blocks were included as part of the allocation */
static int inode_attach_dindirect_blocks(struct ext4_inode *inode,
struct block_allocation *alloc, u32 *block_len)
{
int len = min(*block_len, aux_info.blocks_per_dind);
int dind_block = get_oob_block(alloc, 0);
inode->i_block[EXT4_DIND_BLOCK] = dind_block;
if (advance_oob_blocks(alloc, 1)) {
error("failed to advance oob block");
return -1;
}
u32 *dind_block_data = calloc(info.block_size, 1);
sparse_file_add_data(ext4_sparse_file, dind_block_data, info.block_size,
dind_block);
fill_dindirect_block(dind_block_data, len, alloc);
if (advance_blocks(alloc, len)) {
error("failed to advance %d blocks", len);
return -1;
}
*block_len -= len;
return 0;
}
/* Given an allocation, attach as many blocks as possible to triply indirect
blocks, and return the rest.
Assumes that the blocks necessary to hold the indirect, doubly indirect and
triply indirect blocks were included as part of the allocation */
static int inode_attach_tindirect_blocks(struct ext4_inode *inode,
struct block_allocation *alloc, u32 *block_len)
{
int len = min(*block_len, aux_info.blocks_per_tind);
int tind_block = get_oob_block(alloc, 0);
inode->i_block[EXT4_TIND_BLOCK] = tind_block;
if (advance_oob_blocks(alloc, 1)) {
error("failed to advance oob block");
return -1;
}
u32 *tind_block_data = calloc(info.block_size, 1);
sparse_file_add_data(ext4_sparse_file, tind_block_data, info.block_size,
tind_block);
fill_tindirect_block(tind_block_data, len, alloc);
if (advance_blocks(alloc, len)) {
error("failed to advance %d blocks", len);
return -1;
}
*block_len -= len;
return 0;
}
static void reserve_all_indirect_blocks(struct block_allocation *alloc, u32 len)
{
if (len <= EXT4_NDIR_BLOCKS)
return;
len -= EXT4_NDIR_BLOCKS;
advance_blocks(alloc, EXT4_NDIR_BLOCKS);
u32 ind_block_len = min(aux_info.blocks_per_ind, len);
reserve_indirect_block(alloc, ind_block_len);
len -= ind_block_len;
if (len == 0)
return;
u32 dind_block_len = min(aux_info.blocks_per_dind, len);
reserve_dindirect_block(alloc, dind_block_len);
len -= dind_block_len;
if (len == 0)
return;
u32 tind_block_len = min(aux_info.blocks_per_tind, len);
reserve_tindirect_block(alloc, tind_block_len);
len -= tind_block_len;
if (len == 0)
return;
error("%d blocks remaining", len);
}
static u32 indirect_blocks_needed(u32 len)
{
u32 ind = 0;
if (len <= EXT4_NDIR_BLOCKS)
return ind;
len -= EXT4_NDIR_BLOCKS;
/* We will need an indirect block for the rest of the blocks */
ind += DIV_ROUND_UP(len, aux_info.blocks_per_ind);
if (len <= aux_info.blocks_per_ind)
return ind;
len -= aux_info.blocks_per_ind;
ind += DIV_ROUND_UP(len, aux_info.blocks_per_dind);
if (len <= aux_info.blocks_per_dind)
return ind;
len -= aux_info.blocks_per_dind;
ind += DIV_ROUND_UP(len, aux_info.blocks_per_tind);
if (len <= aux_info.blocks_per_tind)
return ind;
critical_error("request too large");
return 0;
}
static int do_inode_attach_indirect(struct ext4_inode *inode,
struct block_allocation *alloc, u32 block_len)
{
u32 count = block_len;
if (inode_attach_direct_blocks(inode, alloc, &count)) {
error("failed to attach direct blocks to inode");
return -1;
}
if (count > 0) {
if (inode_attach_indirect_blocks(inode, alloc, &count)) {
error("failed to attach indirect blocks to inode");
return -1;
}
}
if (count > 0) {
if (inode_attach_dindirect_blocks(inode, alloc, &count)) {
error("failed to attach dindirect blocks to inode");
return -1;
}
}
if (count > 0) {
if (inode_attach_tindirect_blocks(inode, alloc, &count)) {
error("failed to attach tindirect blocks to inode");
return -1;
}
}
if (count) {
error("blocks left after triply-indirect allocation");
return -1;
}
rewind_alloc(alloc);
return 0;
}
static struct block_allocation *do_inode_allocate_indirect(
u32 block_len)
{
u32 indirect_len = indirect_blocks_needed(block_len);
struct block_allocation *alloc = allocate_blocks(block_len + indirect_len);
if (alloc == NULL) {
error("Failed to allocate %d blocks", block_len + indirect_len);
return NULL;
}
return alloc;
}
/* Allocates enough blocks to hold len bytes and connects them to an inode */
void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len)
{
struct block_allocation *alloc;
u32 block_len = DIV_ROUND_UP(len, info.block_size);
u32 indirect_len = indirect_blocks_needed(block_len);
alloc = do_inode_allocate_indirect(block_len);
if (alloc == NULL) {
error("failed to allocate extents for %lu bytes", len);
return;
}
reserve_all_indirect_blocks(alloc, block_len);
rewind_alloc(alloc);
if (do_inode_attach_indirect(inode, alloc, block_len))
error("failed to attach blocks to indirect inode");
inode->i_flags = 0;
inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512;
inode->i_size_lo = len;
free_alloc(alloc);
}
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");
sparse_file_add_data(ext4_sparse_file, 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");
sparse_file_add_data(ext4_sparse_file, 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;
}
/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
buffer, and connects them to an inode. Returns a pointer to the data
buffer. */
u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
unsigned long backing_len)
{
struct block_allocation *alloc;
u32 block_len = DIV_ROUND_UP(len, info.block_size);
u8 *data = NULL;
alloc = do_inode_allocate_indirect(block_len);
if (alloc == NULL) {
error("failed to allocate extents for %lu bytes", len);
return NULL;
}
if (backing_len) {
data = create_backing(alloc, backing_len);
if (!data)
error("failed to create backing for %lu bytes", backing_len);
}
rewind_alloc(alloc);
if (do_inode_attach_indirect(inode, alloc, block_len))
error("failed to attach blocks to indirect inode");
free_alloc(alloc);
return data;
}

29
indirect.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _INDIRECT_H_
#define _INDIRECT_H_
#include "allocate.h"
void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len);
u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
unsigned long backing_len);
void inode_attach_resize(struct ext4_inode *inode,
struct block_allocation *alloc);
void free_indirect_blocks();
#endif

141
jbd2.h Normal file
View File

@ -0,0 +1,141 @@
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
****************************************************************************
****************************************************************************/
#ifndef _LINUX_JBD2_H
#define _LINUX_JBD2_H
#define JBD2_DEBUG
#define jfs_debug jbd_debug
#define journal_oom_retry 1
#undef JBD2_PARANOID_IOFAIL
#define JBD2_DEFAULT_MAX_COMMIT_AGE 5
#define jbd_debug(f, a...)
#define JBD2_MIN_JOURNAL_BLOCKS 1024
#define JBD2_MAGIC_NUMBER 0xc03b3998U
#define JBD2_DESCRIPTOR_BLOCK 1
#define JBD2_COMMIT_BLOCK 2
#define JBD2_SUPERBLOCK_V1 3
#define JBD2_SUPERBLOCK_V2 4
#define JBD2_REVOKE_BLOCK 5
typedef struct journal_header_s
{
__be32 h_magic;
__be32 h_blocktype;
__be32 h_sequence;
} journal_header_t;
#define JBD2_CRC32_CHKSUM 1
#define JBD2_MD5_CHKSUM 2
#define JBD2_SHA1_CHKSUM 3
#define JBD2_CRC32_CHKSUM_SIZE 4
#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32))
struct commit_header {
__be32 h_magic;
__be32 h_blocktype;
__be32 h_sequence;
unsigned char h_chksum_type;
unsigned char h_chksum_size;
unsigned char h_padding[2];
__be32 h_chksum[JBD2_CHECKSUM_BYTES];
__be64 h_commit_sec;
__be32 h_commit_nsec;
};
typedef struct journal_block_tag_s
{
__be32 t_blocknr;
__be32 t_flags;
__be32 t_blocknr_high;
} journal_block_tag_t;
#define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high))
#define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t))
typedef struct jbd2_journal_revoke_header_s
{
journal_header_t r_header;
__be32 r_count;
} jbd2_journal_revoke_header_t;
#define JBD2_FLAG_ESCAPE 1
#define JBD2_FLAG_SAME_UUID 2
#define JBD2_FLAG_DELETED 4
#define JBD2_FLAG_LAST_TAG 8
typedef struct journal_superblock_s
{
journal_header_t s_header;
__be32 s_blocksize;
__be32 s_maxlen;
__be32 s_first;
__be32 s_sequence;
__be32 s_start;
__be32 s_errno;
__be32 s_feature_compat;
__be32 s_feature_incompat;
__be32 s_feature_ro_compat;
__u8 s_uuid[16];
__be32 s_nr_users;
__be32 s_dynsuper;
__be32 s_max_transaction;
__be32 s_max_trans_data;
__u32 s_padding[44];
__u8 s_users[16*48];
} journal_superblock_t;
#define JBD2_HAS_COMPAT_FEATURE(j,mask) ((j)->j_format_version >= 2 && ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
#define JBD2_HAS_RO_COMPAT_FEATURE(j,mask) ((j)->j_format_version >= 2 && ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
#define JBD2_HAS_INCOMPAT_FEATURE(j,mask) ((j)->j_format_version >= 2 && ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
#define JBD2_FEATURE_COMPAT_CHECKSUM 0x00000001
#define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001
#define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002
#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
#define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM
#define JBD2_KNOWN_ROCOMPAT_FEATURES 0
#define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE | JBD2_FEATURE_INCOMPAT_64BIT | JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
#define BJ_None 0
#define BJ_Metadata 1
#define BJ_Forget 2
#define BJ_IO 3
#define BJ_Shadow 4
#define BJ_LogCtl 5
#define BJ_Reserved 6
#define BJ_Types 7
#endif

671
make_ext4fs.c Normal file
View File

@ -0,0 +1,671 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "make_ext4fs.h"
#include "ext4_utils.h"
#include "allocate.h"
#include "contents.h"
#include "uuid.h"
#include "wipe.h"
#include <sparse/sparse.h>
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef USE_MINGW
#include <winsock2.h>
/* These match the Linux definitions of these flags.
L_xx is defined to avoid conflicting with the win32 versions.
*/
#define L_S_IRUSR 00400
#define L_S_IWUSR 00200
#define L_S_IXUSR 00100
#define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000
#else
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
#define O_BINARY 0
#endif
/* TODO: Not implemented:
Allocating blocks in the same block group as the file inode
Hash or binary tree directories
Special files: sockets, devices, fifos
*/
static int filter_dot(const struct dirent *d)
{
return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
}
static u32 build_default_directory_structure(const char *dir_path,
struct selabel_handle *sehnd)
{
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);
#ifndef USE_MINGW
if (sehnd) {
char *path = NULL;
char *secontext = NULL;
asprintf(&path, "%slost+found", dir_path);
if (selabel_lookup(sehnd, &secontext, path, S_IFDIR) < 0) {
error("cannot lookup security context for %s", path);
} else {
inode_set_selinux(inode, secontext);
freecon(secontext);
}
free(path);
}
#endif
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, 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;
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) {
#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, 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 {
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;
}
#endif
static u32 compute_block_size()
{
return 4096;
}
static u32 compute_journal_blocks()
{
u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
if (journal_blocks < 1024)
journal_blocks = 1024;
if (journal_blocks > 32768)
journal_blocks = 32768;
return journal_blocks;
}
static u32 compute_blocks_per_group()
{
return info.block_size * 8;
}
static u32 compute_inodes()
{
return DIV_ROUND_UP(info.len, info.block_size) / 4;
}
static u32 compute_inodes_per_group()
{
u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
inodes = EXT4_ALIGN(inodes, (info.block_size / info.inode_size));
/* After properly rounding up the number of inodes/group,
* make sure to update the total inodes field in the info struct.
*/
info.inodes = inodes * block_groups;
return inodes;
}
static u32 compute_bg_desc_reserve_blocks()
{
u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
info.block_size);
u32 bg_desc_reserve_blocks =
DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
info.block_size) - bg_desc_blocks;
if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
bg_desc_reserve_blocks = info.block_size / sizeof(u32);
return bg_desc_reserve_blocks;
}
void reset_ext4fs_info() {
// Reset all the global data structures used by make_ext4fs so it
// can be called again.
memset(&info, 0, sizeof(info));
memset(&aux_info, 0, sizeof(aux_info));
if (ext4_sparse_file) {
sparse_file_destroy(ext4_sparse_file);
ext4_sparse_file = NULL;
}
}
int make_ext4fs_sparse_fd(int fd, long long len,
const char *mountpoint, struct selabel_handle *sehnd)
{
reset_ext4fs_info();
info.len = len;
return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0, -1, NULL);
}
int make_ext4fs(const char *filename, long long len,
const char *mountpoint, struct selabel_handle *sehnd)
{
int fd;
int status;
reset_ext4fs_info();
info.len = len;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
error_errno("open");
return EXIT_FAILURE;
}
status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0, -1, NULL);
close(fd);
return status;
}
/* return a newly-malloc'd string that is a copy of str. The new string
is guaranteed to have a trailing slash. If absolute is true, the new string
is also guaranteed to have a leading slash.
*/
static char *canonicalize_slashes(const char *str, bool absolute)
{
char *ret;
int len = strlen(str);
int newlen = len;
char *ptr;
if (len == 0) {
if (absolute)
return strdup("/");
else
return strdup("");
}
if (str[0] != '/' && absolute) {
newlen++;
}
if (str[len - 1] != '/') {
newlen++;
}
ret = malloc(newlen + 1);
if (!ret) {
critical_error("malloc");
}
ptr = ret;
if (str[0] != '/' && absolute) {
*ptr++ = '/';
}
strcpy(ptr, str);
ptr += len;
if (str[len - 1] != '/') {
*ptr++ = '/';
}
if (ptr != ret + newlen) {
critical_error("assertion failed\n");
}
*ptr = '\0';
return ret;
}
static char *canonicalize_abs_slashes(const char *str)
{
return canonicalize_slashes(str, true);
}
static char *canonicalize_rel_slashes(const char *str)
{
return canonicalize_slashes(str, false);
}
int make_ext4fs_internal(int fd, const char *_directory,
const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
int sparse, int crc, int wipe,
struct selabel_handle *sehnd, int verbose, time_t fixed_time,
FILE* block_list_file)
{
u32 root_inode_num;
u16 root_mode;
char *mountpoint;
char *directory = NULL;
if (setjmp(setjmp_env))
return EXIT_FAILURE; /* Handle a call to longjmp() */
if (_mountpoint == NULL) {
mountpoint = strdup("");
} else {
mountpoint = canonicalize_abs_slashes(_mountpoint);
}
if (_directory) {
directory = canonicalize_rel_slashes(_directory);
}
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 |
EXT4_FEATURE_COMPAT_EXT_ATTR;
info.feat_ro_compat |=
EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
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: %"PRIu64"\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: %"PRIu64"\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);
ext4_sparse_file = sparse_file_new(info.block_size, info.len);
block_allocator_init();
ext4_fill_in_sb();
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(mountpoint, sehnd);
#else
if (directory)
root_inode_num = build_directory_structure(directory, mountpoint, 0,
fs_config_func, sehnd, verbose, fixed_time);
else
root_inode_num = build_default_directory_structure(mountpoint, sehnd);
#endif
root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
#ifndef USE_MINGW
if (sehnd) {
char *secontext = NULL;
if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
error("cannot lookup security context for %s", mountpoint);
}
if (secontext) {
if (verbose) {
printf("Labeling %s as %s\n", mountpoint, secontext);
}
inode_set_selinux(root_inode_num, secontext);
}
freecon(secontext);
}
#endif
ext4_update_free();
ext4_queue_sb();
if (block_list_file) {
size_t dirlen = directory ? strlen(directory) : 0;
struct block_allocation* p = get_saved_allocation_chain();
while (p) {
if (directory && strncmp(p->filename, directory, dirlen) == 0) {
// substitute mountpoint for the leading directory in the filename, in the output file
fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
} else {
fprintf(block_list_file, "%s", p->filename);
}
print_blocks(block_list_file, p);
struct block_allocation* pn = p->next;
free_alloc(p);
p = pn;
}
}
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_IS_SUPPORTED) {
wipe_block_device(fd, info.len);
}
write_ext4_image(fd, gzip, sparse, crc);
sparse_file_destroy(ext4_sparse_file);
ext4_sparse_file = NULL;
free(mountpoint);
free(directory);
return 0;
}

35
make_ext4fs.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MAKE_EXT4FS_H_
#define _MAKE_EXT4FS_H_
#ifdef __cplusplus
extern "C" {
#endif
struct selabel_handle;
int make_ext4fs(const char *filename, long long len,
const char *mountpoint, struct selabel_handle *sehnd);
int make_ext4fs_sparse_fd(int fd, long long len,
const char *mountpoint, struct selabel_handle *sehnd);
#ifdef __cplusplus
}
#endif
#endif

237
make_ext4fs_main.c Normal file
View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <unistd.h>
#if defined(__linux__)
#include <linux/fs.h>
#elif defined(__APPLE__) && defined(__MACH__)
#include <sys/disk.h>
#endif
#ifdef ANDROID
#include <private/android_filesystem_config.h>
#endif
#ifndef USE_MINGW
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
#else
struct selabel_handle;
#endif
#include "make_ext4fs.h"
#include "ext4_utils.h"
#include "canned_fs_config.h"
#ifndef USE_MINGW /* O_BINARY is windows-specific flag */
#define O_BINARY 0
#endif
extern struct fs_info info;
static void usage(char *path)
{
fprintf(stderr, "%s [ -l <len> ] [ -j <journal size> ] [ -b <block_size> ]\n", basename(path));
fprintf(stderr, " [ -g <blocks per group> ] [ -i <inodes> ] [ -I <inode size> ]\n");
fprintf(stderr, " [ -L <label> ] [ -f ] [ -a <android mountpoint> ]\n");
fprintf(stderr, " [ -S file_contexts ] [ -C fs_config ] [ -T timestamp ]\n");
fprintf(stderr, " [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ] [ -B <block_list_file> ]\n");
fprintf(stderr, " <filename> [<directory>]\n");
}
int main(int argc, char **argv)
{
int opt;
const char *filename = NULL;
const char *directory = NULL;
char *mountpoint = NULL;
fs_config_func_t fs_config_func = NULL;
const char *fs_config_file = NULL;
int gzip = 0;
int sparse = 0;
int crc = 0;
int wipe = 0;
int fd;
int exitcode;
int verbose = 0;
time_t fixed_time = -1;
struct selabel_handle *sehnd = NULL;
FILE* block_list_file = NULL;
#ifndef USE_MINGW
struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
#endif
while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:T:C:B:fwzJsctv")) != -1) {
switch (opt) {
case 'l':
info.len = parse_num(optarg);
break;
case 'j':
info.journal_blocks = parse_num(optarg);
break;
case 'b':
info.block_size = parse_num(optarg);
break;
case 'g':
info.blocks_per_group = parse_num(optarg);
break;
case 'i':
info.inodes = parse_num(optarg);
break;
case 'I':
info.inode_size = parse_num(optarg);
break;
case 'L':
info.label = optarg;
break;
case 'f':
force = 1;
break;
case 'a':
#ifdef ANDROID
mountpoint = optarg;
#else
fprintf(stderr, "can't set android permissions - built without android support\n");
usage(argv[0]);
exit(EXIT_FAILURE);
#endif
break;
case 'w':
wipe = 1;
break;
case 'z':
gzip = 1;
break;
case 'J':
info.no_journal = 1;
break;
case 'c':
crc = 1;
break;
case 's':
sparse = 1;
break;
case 't':
fprintf(stderr, "Warning: -t (initialize inode tables) is deprecated\n");
break;
case 'S':
#ifndef USE_MINGW
seopts[0].value = optarg;
sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
if (!sehnd) {
perror(optarg);
exit(EXIT_FAILURE);
}
#endif
break;
case 'v':
verbose = 1;
break;
case 'T':
fixed_time = strtoll(optarg, NULL, 0);
break;
case 'C':
fs_config_file = optarg;
break;
case 'B':
block_list_file = fopen(optarg, "w");
if (block_list_file == NULL) {
fprintf(stderr, "failed to open block_list_file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
break;
default: /* '?' */
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
#if !defined(HOST)
// Use only if -S option not requested
if (!sehnd && mountpoint) {
sehnd = selinux_android_file_context_handle();
if (!sehnd) {
perror(optarg);
exit(EXIT_FAILURE);
}
}
#endif
if (fs_config_file) {
if (load_canned_fs_config(fs_config_file) < 0) {
fprintf(stderr, "failed to load %s\n", fs_config_file);
exit(EXIT_FAILURE);
}
fs_config_func = canned_fs_config;
} else if (mountpoint) {
fs_config_func = fs_config;
}
if (wipe && sparse) {
fprintf(stderr, "Cannot specifiy both wipe and sparse\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (wipe && gzip) {
fprintf(stderr, "Cannot specifiy both wipe and gzip\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (optind >= argc) {
fprintf(stderr, "Expected filename after options\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
filename = argv[optind++];
if (optind < argc)
directory = argv[optind++];
if (optind < argc) {
fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (strcmp(filename, "-")) {
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
} else {
fd = STDOUT_FILENO;
}
exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
sparse, crc, wipe, sehnd, verbose, fixed_time, block_list_file);
close(fd);
if (block_list_file)
fclose(block_list_file);
if (exitcode && strcmp(filename, "-"))
unlink(filename);
return exitcode;
}

106
mkuserimg.sh Executable file
View File

@ -0,0 +1,106 @@
#!/bin/bash
#
# To call this script, make sure make_ext4fs is somewhere in PATH
function usage() {
cat<<EOT
Usage:
mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [-j <journal_size>]
[-T TIMESTAMP] [-C FS_CONFIG] [-B BLOCK_LIST_FILE] [-L LABEL] [FILE_CONTEXTS]
EOT
}
ENABLE_SPARSE_IMAGE=
if [ "$1" = "-s" ]; then
ENABLE_SPARSE_IMAGE="-s"
shift
fi
if [ $# -lt 5 ]; then
usage
exit 1
fi
SRC_DIR=$1
if [ ! -d $SRC_DIR ]; then
echo "Can not find directory $SRC_DIR!"
exit 2
fi
OUTPUT_FILE=$2
EXT_VARIANT=$3
MOUNT_POINT=$4
SIZE=$5
shift; shift; shift; shift; shift
JOURNAL_FLAGS=
if [ "$1" = "-j" ]; then
if [ "$2" = "0" ]; then
JOURNAL_FLAGS="-J"
else
JOURNAL_FLAGS="-j $2"
fi
shift; shift
fi
TIMESTAMP=-1
if [[ "$1" == "-T" ]]; then
TIMESTAMP=$2
shift; shift
fi
FS_CONFIG=
if [[ "$1" == "-C" ]]; then
FS_CONFIG=$2
shift; shift
fi
BLOCK_LIST=
if [[ "$1" == "-B" ]]; then
BLOCK_LIST=$2
shift; shift
fi
LABEL=
if [[ "$1" == "-L" ]]; then
LABEL=$2
shift; shift
fi
FC=$1
case $EXT_VARIANT in
ext4) ;;
*) echo "Only ext4 is supported!"; exit 3 ;;
esac
if [ -z $MOUNT_POINT ]; then
echo "Mount point is required"
exit 2
fi
if [ -z $SIZE ]; then
echo "Need size of filesystem"
exit 2
fi
OPT=""
if [ -n "$FC" ]; then
OPT="$OPT -S $FC"
fi
if [ -n "$FS_CONFIG" ]; then
OPT="$OPT -C $FS_CONFIG"
fi
if [ -n "$BLOCK_LIST" ]; then
OPT="$OPT -B $BLOCK_LIST"
fi
if [ -n "$LABEL" ]; then
OPT="$OPT -L $LABEL"
fi
MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE -T $TIMESTAMP $OPT -l $SIZE $JOURNAL_FLAGS -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
echo $MAKE_EXT4FS_CMD
$MAKE_EXT4FS_CMD
if [ $? -ne 0 ]; then
exit 4
fi

85
setup_fs.c Normal file
View File

@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#include <cutils/android_reboot.h>
#include <cutils/partition_utils.h>
const char *mkfs = "/system/bin/make_ext4fs";
int setup_fs(const char *blockdev)
{
char buf[256], path[128];
pid_t child;
int status, n;
pid_t pid;
/* we might be looking at an indirect reference */
n = readlink(blockdev, path, sizeof(path) - 1);
if (n > 0) {
path[n] = 0;
if (!memcmp(path, "/dev/block/", 11))
blockdev = path + 11;
}
if (strchr(blockdev,'/')) {
fprintf(stderr,"not a block device name: %s\n", blockdev);
return 0;
}
snprintf(buf, sizeof(buf), "/sys/fs/ext4/%s", blockdev);
if (access(buf, F_OK) == 0) {
fprintf(stderr,"device %s already has a filesystem\n", blockdev);
return 0;
}
snprintf(buf, sizeof(buf), "/dev/block/%s", blockdev);
if (!partition_wiped(buf)) {
fprintf(stderr,"device %s not wiped, probably encrypted, not wiping\n", blockdev);
return 0;
}
fprintf(stderr,"+++\n");
child = fork();
if (child < 0) {
fprintf(stderr,"error: setup_fs: fork failed\n");
return 0;
}
if (child == 0) {
execl(mkfs, mkfs, buf, NULL);
exit(-1);
}
while ((pid=waitpid(-1, &status, 0)) != child) {
if (pid == -1) {
fprintf(stderr, "error: setup_fs: waitpid failed!\n");
return 1;
}
}
fprintf(stderr,"---\n");
return 1;
}
int main(int argc, char **argv)
{
int need_reboot = 0;
while (argc > 1) {
if (strlen(argv[1]) < 128)
need_reboot |= setup_fs(argv[1]);
argv++;
argc--;
}
if (need_reboot) {
fprintf(stderr,"REBOOT!\n");
android_reboot(ANDROID_RB_RESTART, 0, 0);
exit(-1);
}
return 0;
}

273
sha1.c Normal file
View File

@ -0,0 +1,273 @@
/* $NetBSD: sha1.c,v 1.1 2005/12/20 20:29:40 christos Exp $ */
/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */
/*
* SHA-1 in C
* By Steve Reid <steve@edmweb.com>
* 100% Public Domain
*
* Test Vectors (from FIPS PUB 180-1)
* "abc"
* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
* A million repetitions of "a"
* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
#define SHA1HANDSOFF /* Copies data before messing with it. */
#ifndef USE_MINGW
#include <sys/cdefs.h>
#endif
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include "sha1.h"
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#if !HAVE_SHA1_H
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/*
* blk0() and blk() perform the initial expand.
* I got the idea of expanding during the round function from SSLeay
*/
#if BYTE_ORDER == LITTLE_ENDIAN
# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#else
# define blk0(i) block->l[i]
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/*
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
*/
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
typedef union {
u_char c[64];
u_int l[16];
} CHAR64LONG16;
/* old sparc64 gcc could not compile this */
#undef SPARC64_GCC_WORKAROUND
#if defined(__sparc64__) && defined(__GNUC__) && __GNUC__ < 3
#define SPARC64_GCC_WORKAROUND
#endif
#ifdef SPARC64_GCC_WORKAROUND
void do_R01(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
void do_R2(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
void do_R3(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
void do_R4(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
#define nR0(v,w,x,y,z,i) R0(*v,*w,*x,*y,*z,i)
#define nR1(v,w,x,y,z,i) R1(*v,*w,*x,*y,*z,i)
#define nR2(v,w,x,y,z,i) R2(*v,*w,*x,*y,*z,i)
#define nR3(v,w,x,y,z,i) R3(*v,*w,*x,*y,*z,i)
#define nR4(v,w,x,y,z,i) R4(*v,*w,*x,*y,*z,i)
void
do_R01(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
{
nR0(a,b,c,d,e, 0); nR0(e,a,b,c,d, 1); nR0(d,e,a,b,c, 2); nR0(c,d,e,a,b, 3);
nR0(b,c,d,e,a, 4); nR0(a,b,c,d,e, 5); nR0(e,a,b,c,d, 6); nR0(d,e,a,b,c, 7);
nR0(c,d,e,a,b, 8); nR0(b,c,d,e,a, 9); nR0(a,b,c,d,e,10); nR0(e,a,b,c,d,11);
nR0(d,e,a,b,c,12); nR0(c,d,e,a,b,13); nR0(b,c,d,e,a,14); nR0(a,b,c,d,e,15);
nR1(e,a,b,c,d,16); nR1(d,e,a,b,c,17); nR1(c,d,e,a,b,18); nR1(b,c,d,e,a,19);
}
void
do_R2(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
{
nR2(a,b,c,d,e,20); nR2(e,a,b,c,d,21); nR2(d,e,a,b,c,22); nR2(c,d,e,a,b,23);
nR2(b,c,d,e,a,24); nR2(a,b,c,d,e,25); nR2(e,a,b,c,d,26); nR2(d,e,a,b,c,27);
nR2(c,d,e,a,b,28); nR2(b,c,d,e,a,29); nR2(a,b,c,d,e,30); nR2(e,a,b,c,d,31);
nR2(d,e,a,b,c,32); nR2(c,d,e,a,b,33); nR2(b,c,d,e,a,34); nR2(a,b,c,d,e,35);
nR2(e,a,b,c,d,36); nR2(d,e,a,b,c,37); nR2(c,d,e,a,b,38); nR2(b,c,d,e,a,39);
}
void
do_R3(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
{
nR3(a,b,c,d,e,40); nR3(e,a,b,c,d,41); nR3(d,e,a,b,c,42); nR3(c,d,e,a,b,43);
nR3(b,c,d,e,a,44); nR3(a,b,c,d,e,45); nR3(e,a,b,c,d,46); nR3(d,e,a,b,c,47);
nR3(c,d,e,a,b,48); nR3(b,c,d,e,a,49); nR3(a,b,c,d,e,50); nR3(e,a,b,c,d,51);
nR3(d,e,a,b,c,52); nR3(c,d,e,a,b,53); nR3(b,c,d,e,a,54); nR3(a,b,c,d,e,55);
nR3(e,a,b,c,d,56); nR3(d,e,a,b,c,57); nR3(c,d,e,a,b,58); nR3(b,c,d,e,a,59);
}
void
do_R4(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
{
nR4(a,b,c,d,e,60); nR4(e,a,b,c,d,61); nR4(d,e,a,b,c,62); nR4(c,d,e,a,b,63);
nR4(b,c,d,e,a,64); nR4(a,b,c,d,e,65); nR4(e,a,b,c,d,66); nR4(d,e,a,b,c,67);
nR4(c,d,e,a,b,68); nR4(b,c,d,e,a,69); nR4(a,b,c,d,e,70); nR4(e,a,b,c,d,71);
nR4(d,e,a,b,c,72); nR4(c,d,e,a,b,73); nR4(b,c,d,e,a,74); nR4(a,b,c,d,e,75);
nR4(e,a,b,c,d,76); nR4(d,e,a,b,c,77); nR4(c,d,e,a,b,78); nR4(b,c,d,e,a,79);
}
#endif
/*
* Hash a single 512-bit block. This is the core of the algorithm.
*/
void SHA1Transform(state, buffer)
u_int32_t state[5];
const u_char buffer[64];
{
u_int32_t a, b, c, d, e;
CHAR64LONG16 *block;
#ifdef SHA1HANDSOFF
CHAR64LONG16 workspace;
#endif
assert(buffer != 0);
assert(state != 0);
#ifdef SHA1HANDSOFF
block = &workspace;
(void)memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16 *)(void *)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
#ifdef SPARC64_GCC_WORKAROUND
do_R01(&a, &b, &c, &d, &e, block);
do_R2(&a, &b, &c, &d, &e, block);
do_R3(&a, &b, &c, &d, &e, block);
do_R4(&a, &b, &c, &d, &e, block);
#else
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
#endif
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
}
/*
* SHA1Init - Initialize new context
*/
void SHA1Init(context)
SHA1_CTX *context;
{
assert(context != 0);
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/*
* Run your data through this.
*/
void SHA1Update(context, data, len)
SHA1_CTX *context;
const u_char *data;
u_int len;
{
u_int i, j;
assert(context != 0);
assert(data != 0);
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1] += (len>>29)+1;
j = (j >> 3) & 63;
if ((j + len) > 63) {
(void)memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64)
SHA1Transform(context->state, &data[i]);
j = 0;
} else {
i = 0;
}
(void)memcpy(&context->buffer[j], &data[i], len - i);
}
/*
* Add padding and return the message digest.
*/
void SHA1Final(digest, context)
u_char digest[20];
SHA1_CTX* context;
{
u_int i;
u_char finalcount[8];
assert(digest != 0);
assert(context != 0);
for (i = 0; i < 8; i++) {
finalcount[i] = (u_char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
SHA1Update(context, (const u_char *)"\200", 1);
while ((context->count[0] & 504) != 448)
SHA1Update(context, (const u_char *)"\0", 1);
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
if (digest) {
for (i = 0; i < 20; i++)
digest[i] = (u_char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
}
#endif /* HAVE_SHA1_H */

43
sha1.h Normal file
View File

@ -0,0 +1,43 @@
/* $NetBSD: sha1.h,v 1.13 2005/12/26 18:41:36 perry Exp $ */
/*
* SHA-1 in C
* By Steve Reid <steve@edmweb.com>
* 100% Public Domain
*/
#ifndef _SYS_SHA1_H_
#define _SYS_SHA1_H_
#include <sys/types.h>
#include <stdint.h>
#ifdef USE_MINGW
typedef unsigned char u_char;
typedef unsigned int uint32_t;
typedef unsigned int u_int32_t;
typedef unsigned int u_int;
#define __BEGIN_DECLS
#define __END_DECLS
#else
#include <sys/cdefs.h>
#endif
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_STRING_LENGTH 41
typedef struct {
uint32_t state[5];
uint32_t count[2];
u_char buffer[64];
} SHA1_CTX;
__BEGIN_DECLS
void SHA1Transform(uint32_t[5], const u_char[64]);
void SHA1Init(SHA1_CTX *);
void SHA1Update(SHA1_CTX *, const u_char *, u_int);
void SHA1Final(u_char[SHA1_DIGEST_LENGTH], SHA1_CTX *);
__END_DECLS
#endif /* _SYS_SHA1_H_ */

71
test_ext4fixup Executable file
View File

@ -0,0 +1,71 @@
#!/bin/bash
typeset -i I ITERATIONS PHASE LOC COUNT MAXCOUNT
ME=`basename $0`
if [ "$#" -ne 3 ]
then
echo "$ME: Usage: $ME <iterations> <maxcount> <filesystem_image>" >&2
exit 1;
fi
ITERATIONS="$1"
MAXCOUNT="$2"
ORIG_FS_IMAGE="$3"
FIXED_FS_IMAGE="/tmp/fixedfsimage.$$"
NEW_FS_IMAGE="/tmp/newfsimage.$$"
if [ ! -f "$ORIG_FS_IMAGE" ]
then
echo "$ME: Filesystem image $NEW_FS_IMAGE does not exist" >&2
exit 1
fi
trap "rm -f $NEW_FS_IMAGE $FIXED_FS_IMAGE" 0 1 2 3 15
rm -f "$NEW_FS_IMAGE" "$FIXED_FS_IMAGE"
# Create the fixed image to compare against
cp "$ORIG_FS_IMAGE" "$FIXED_FS_IMAGE"
ext4fixup "$FIXED_FS_IMAGE"
if [ "$?" -ne 0 ]
then
echo "$ME: ext4fixup failed!\n"
exit 1
fi
I=0
while [ "$I" -lt "$ITERATIONS" ]
do
# There is also a phase 4, which is writing out the updated superblocks and
# block group descriptors. Test the with a separate script.
let PHASE="$RANDOM"%3 # 0 to 2
let PHASE++ # 1 to 3
let LOC="$RANDOM"%2 # 0 to 1
let LOC++ # 1 to 2
let COUNT="$RANDOM"%"$MAXCOUNT"
# Make a copy of the original image to fixup
cp "$ORIG_FS_IMAGE" "$NEW_FS_IMAGE"
# Run the fixup tool, but die partway through to see if we can recover
ext4fixup -d "$PHASE,$LOC,$COUNT" "$NEW_FS_IMAGE" 2>/dev/null
# run it again without -d to have it finish the job
ext4fixup "$NEW_FS_IMAGE"
if cmp "$FIXED_FS_IMAGE" "$NEW_FS_IMAGE"
then
:
else
echo "$ME: test failed with parameters $PHASE, $LOC, $COUNT"
exit 1
fi
rm -f "$NEW_FS_IMAGE"
let I++
done

View File

@ -0,0 +1,86 @@
#include "unencrypted_properties.h"
#include <sys/stat.h>
namespace properties {
const char* key = "key";
const char* ref = "ref";
const char* type = "type";
const char* password = "password";
}
namespace
{
const char* unencrypted_folder = "unencrypted";
}
UnencryptedProperties::UnencryptedProperties(const char* device)
: folder_(std::string() + device + "/" + unencrypted_folder)
{
}
UnencryptedProperties::UnencryptedProperties()
{
}
template<> std::string UnencryptedProperties::Get(const char* name,
std::string default_value)
{
if (!OK()) return default_value;
std::ifstream i(folder_ + "/" + name, std::ios::binary);
if (!i) {
return default_value;
}
i.seekg(0, std::ios::end);
int length = i.tellg();
i.seekg(0, std::ios::beg);
if (length == -1) {
return default_value;
}
std::string s(length, 0);
i.read(&s[0], length);
if (!i) {
return default_value;
}
return s;
}
template<> bool UnencryptedProperties::Set(const char* name, std::string const& value)
{
if (!OK()) return false;
std::ofstream o(folder_ + "/" + name, std::ios::binary);
o << value;
return !o.fail();
}
UnencryptedProperties UnencryptedProperties::GetChild(const char* name)
{
UnencryptedProperties e4p;
if (!OK()) return e4p;
std::string directory(folder_ + "/" + name);
if (mkdir(directory.c_str(), 700) == -1 && errno != EEXIST) {
return e4p;
}
e4p.folder_ = directory;
return e4p;
}
bool UnencryptedProperties::Remove(const char* name)
{
if (remove((folder_ + "/" + name).c_str())
&& errno != ENOENT) {
return false;
}
return true;
}
bool UnencryptedProperties::OK() const
{
return !folder_.empty();
}

70
unencrypted_properties.h Normal file
View File

@ -0,0 +1,70 @@
#include <string>
#include <fstream>
// key names for properties we use
namespace properties {
extern const char* key;
extern const char* ref;
extern const char* type;
extern const char* password;
}
/**
* Class to store data on the unencrypted folder of a device.
* Note that the folder must exist before this class is constructed.
* All names must be valid single level (no '/') file or directory names
* Data is organized hierarchically so we can get a child folder
*/
class UnencryptedProperties
{
public:
// Opens properties folder on named device.
// If folder does not exist, construction will succeed, but all
// getters will return default properties and setters will fail.
UnencryptedProperties(const char* device);
// Get named object. Return default if object does not exist or error.
template<typename t> t Get(const char* name, t default_value = t());
// Set named object. Return true if success, false otherwise
template<typename t> bool Set(const char* name, t const& value);
// Get child properties
UnencryptedProperties GetChild(const char* name);
// Remove named object
bool Remove(const char* name);
// Get path of folder
std::string const& GetPath() const {return folder_;}
private:
UnencryptedProperties();
bool OK() const;
std::string folder_;
};
template<typename t> t UnencryptedProperties::Get(const char* name,
t default_value)
{
if (!OK()) return default_value;
t value = default_value;
std::ifstream(folder_ + "/" + name) >> value;
return value;
}
template<typename t> bool UnencryptedProperties::Set(const char* name,
t const& value)
{
if (!OK()) return false;
std::ofstream o(folder_ + "/" + name);
o << value;
return !o.fail();
}
// Specialized getters/setters for strings
template<> std::string UnencryptedProperties::Get(const char* name,
std::string default_value);
template<> bool UnencryptedProperties::Set(const char* name,
std::string const& value);

65
uuid.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#ifdef USE_MINGW
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include "ext4_utils.h"
#include "sha1.h"
#include "uuid.h"
/* Definition from RFC-4122 */
struct uuid {
u32 time_low;
u16 time_mid;
u16 time_hi_and_version;
u8 clk_seq_hi_res;
u8 clk_seq_low;
u16 node0_1;
u32 node2_5;
};
static void sha1_hash(const char *namespace, const char *name,
unsigned char sha1[SHA1_DIGEST_LENGTH])
{
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, (const u8*)namespace, strlen(namespace));
SHA1Update(&ctx, (const u8*)name, strlen(name));
SHA1Final(sha1, &ctx);
}
void generate_uuid(const char *namespace, const char *name, u8 result[16])
{
unsigned char sha1[SHA1_DIGEST_LENGTH];
struct uuid *uuid = (struct uuid *)result;
sha1_hash(namespace, name, (unsigned char*)sha1);
memcpy(uuid, sha1, sizeof(struct uuid));
uuid->time_low = ntohl(uuid->time_low);
uuid->time_mid = ntohs(uuid->time_mid);
uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version);
uuid->time_hi_and_version &= 0x0FFF;
uuid->time_hi_and_version |= (5 << 12);
uuid->clk_seq_hi_res &= ~(1 << 6);
uuid->clk_seq_hi_res |= 1 << 7;
}

24
uuid.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _UUID_H_
#define _UUID_H_
#include "ext4_utils.h"
void generate_uuid(const char *namespace, const char *name, u8 result[16]);
#endif

76
wipe.c Normal file
View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ext4_utils.h"
#include "wipe.h"
#if WIPE_IS_SUPPORTED
#if defined(__linux__)
#include <linux/fs.h>
#include <sys/ioctl.h>
#ifndef BLKDISCARD
#define BLKDISCARD _IO(0x12,119)
#endif
#ifndef BLKSECDISCARD
#define BLKSECDISCARD _IO(0x12,125)
#endif
int wipe_block_device(int fd, s64 len)
{
u64 range[2];
int ret;
if (!is_block_device_fd(fd)) {
// Wiping only makes sense on a block device.
return 0;
}
range[0] = 0;
range[1] = len;
ret = ioctl(fd, BLKSECDISCARD, &range);
if (ret < 0) {
range[0] = 0;
range[1] = len;
ret = ioctl(fd, BLKDISCARD, &range);
if (ret < 0) {
warn("Discard failed\n");
return 1;
} else {
warn("Wipe via secure discard failed, used discard instead\n");
return 0;
}
}
return 0;
}
#else /* __linux__ */
#error "Missing block device wiping implementation for this platform!"
#endif
#else /* WIPE_IS_SUPPORTED */
int wipe_block_device(int fd, s64 len)
{
/* Wiping is not supported on this platform. */
return 1;
}
#endif /* WIPE_IS_SUPPORTED */

33
wipe.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIPE_H_
#define _WIPE_H_
#include "ext4_utils.h"
/* Set WIPE_IS_SUPPORTED to 1 if the current platform supports
* wiping of block devices. 0 otherwise. For now, only Linux does.
*/
#ifdef __linux__
# define WIPE_IS_SUPPORTED 1
#else
# define WIPE_IS_SUPPORTED 0
#endif
int wipe_block_device(int fd, s64 len);
#endif

45
xattr.h Normal file
View File

@ -0,0 +1,45 @@
#include <sys/types.h>
#ifndef _SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H
#define _SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H 1
#define EXT4_XATTR_MAGIC 0xEA020000
#define EXT4_XATTR_INDEX_SECURITY 6
struct ext4_xattr_header {
__le32 h_magic;
__le32 h_refcount;
__le32 h_blocks;
__le32 h_hash;
__le32 h_checksum;
__u32 h_reserved[3];
};
struct ext4_xattr_ibody_header {
__le32 h_magic;
};
struct ext4_xattr_entry {
__u8 e_name_len;
__u8 e_name_index;
__le16 e_value_offs;
__le32 e_value_block;
__le32 e_value_size;
__le32 e_hash;
char e_name[0];
};
#define EXT4_XATTR_PAD_BITS 2
#define EXT4_XATTR_PAD (1<<EXT4_XATTR_PAD_BITS)
#define EXT4_XATTR_ROUND (EXT4_XATTR_PAD-1)
#define EXT4_XATTR_LEN(name_len) \
(((name_len) + EXT4_XATTR_ROUND + \
sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
#define EXT4_XATTR_NEXT(entry) \
((struct ext4_xattr_entry *)( \
(char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
#define EXT4_XATTR_SIZE(size) \
(((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
#define IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0)
#endif /* !_SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H */