mirror of
https://git.openwrt.org/project/make_ext4fs.git
synced 2024-11-22 23:20:41 -05:00
Import make_ext4fs sources
Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
This commit is contained in:
commit
fb5c011b49
154
Android.mk
Normal file
154
Android.mk
Normal 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
0
MODULE_LICENSE_APACHE2
Normal file
190
NOTICE
Normal file
190
NOTICE
Normal 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
781
allocate.c
Normal 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
74
allocate.h
Normal 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
109
canned_fs_config.c
Normal 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
26
canned_fs_config.h
Normal 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
490
contents.c
Normal 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
45
contents.h
Normal 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
58
crc16.c
Normal 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
147
e4crypt_static.c
Normal 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
195
ext2simg.c
Normal 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
577
ext4.h
Normal 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
120
ext4_crypt.cpp
Normal 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
50
ext4_crypt.h
Normal 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
|
269
ext4_crypt_init_extensions.cpp
Normal file
269
ext4_crypt_init_extensions.cpp
Normal 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
88
ext4_extents.h
Normal 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
59
ext4_kernel_headers.h
Normal 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
47
ext4_sb.c
Normal 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
52
ext4_sb.h
Normal 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
544
ext4_utils.c
Normal 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
170
ext4_utils.h
Normal 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
811
ext4fixup.c
Normal 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
20
ext4fixup.h
Normal 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
68
ext4fixup_main.c
Normal 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
232
extent.c
Normal 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, ®ion_block, ®ion_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, ®ion_block, ®ion_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, ®ion_block, ®ion_len);
|
||||||
|
extent->ee_block = file_block;
|
||||||
|
extent->ee_len = region_len;
|
||||||
|
extent->ee_start_hi = 0;
|
||||||
|
extent->ee_start_lo = region_block;
|
||||||
|
file_block += region_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extent_block)
|
||||||
|
block_len += 1;
|
||||||
|
|
||||||
|
blocks = (u64)block_len * info.block_size / 512;
|
||||||
|
|
||||||
|
inode->i_flags |= EXT4_EXTENTS_FL;
|
||||||
|
inode->i_size_lo = len;
|
||||||
|
inode->i_size_high = len >> 32;
|
||||||
|
inode->i_blocks_lo = blocks;
|
||||||
|
inode->osd2.linux2.l_i_blocks_high = blocks >> 32;
|
||||||
|
|
||||||
|
rewind_alloc(alloc);
|
||||||
|
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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
30
extent.h
Normal 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
514
indirect.c
Normal 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, ®ion_block, ®ion_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
29
indirect.h
Normal 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
141
jbd2.h
Normal 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
671
make_ext4fs.c
Normal 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
35
make_ext4fs.h
Normal 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
237
make_ext4fs_main.c
Normal 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
106
mkuserimg.sh
Executable 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
85
setup_fs.c
Normal 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
273
sha1.c
Normal 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
43
sha1.h
Normal 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
71
test_ext4fixup
Executable 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
|
||||||
|
|
86
unencrypted_properties.cpp
Normal file
86
unencrypted_properties.cpp
Normal 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
70
unencrypted_properties.h
Normal 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
65
uuid.c
Normal 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
24
uuid.h
Normal 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
76
wipe.c
Normal 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
33
wipe.h
Normal 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
45
xattr.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user