Project import generated by Copybara. GitOrigin-RevId: 32b8332674f60706a7a5effc4b1721e22f487ca7
diff --git a/extras/ext4_utils/Android.mk b/extras/ext4_utils/Android.mk new file mode 100644 index 0000000..a9fb5dd --- /dev/null +++ b/extras/ext4_utils/Android.mk
@@ -0,0 +1,190 @@ +# 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 + +# +# -- All host/targets including windows +# + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libext4_utils_src_files) +LOCAL_MODULE := libext4_utils_host +LOCAL_C_INCLUDES += external/zlib +LOCAL_STATIC_LIBRARIES += libsparse_host +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +include $(BUILD_HOST_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := make_ext4fs_main.c +LOCAL_MODULE := make_ext4fs +LOCAL_STATIC_LIBRARIES += \ + libext4_utils_host \ + libsparse_host \ + libfsconfig \ + libz +ifeq ($(HOST_OS),windows) + LOCAL_LDLIBS += -lws2_32 +else + ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h + endif # BUILD_EUREKA + ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX + endif # HAVE_SELINUX +endif +include $(BUILD_HOST_EXECUTABLE) + + +# +# -- All host/targets excluding windows +# + +ifneq ($(HOST_OS),windows) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libext4_utils_src_files) +LOCAL_MODULE := libext4_utils +LOCAL_C_INCLUDES += external/zlib +LOCAL_SHARED_LIBRARIES := \ + libsparse \ + libz +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_SHARED_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +include $(BUILD_SHARED_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libext4_utils_src_files) +LOCAL_MODULE := libext4_utils_static +LOCAL_C_INCLUDES += external/zlib +LOCAL_STATIC_LIBRARIES += \ + libsparse_static +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +include $(BUILD_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := make_ext4fs_main.c +LOCAL_MODULE := make_ext4fs +LOCAL_STATIC_LIBRARIES := libfsconfig +LOCAL_SHARED_LIBRARIES += libext4_utils libz +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_SHARED_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := ext2simg.c +LOCAL_MODULE := ext2simg +LOCAL_SHARED_LIBRARIES += \ + libext4_utils \ + libsparse \ + libz +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_SHARED_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := ext2simg.c +LOCAL_MODULE := ext2simg +LOCAL_STATIC_LIBRARIES += \ + libext4_utils_host \ + libsparse_host \ + libz +ifeq ($(BUILD_EUREKA),true) + LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX), true) + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif # HAVE_SELINUX +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
diff --git a/extras/ext4_utils/MODULE_LICENSE_APACHE2 b/extras/ext4_utils/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/extras/ext4_utils/MODULE_LICENSE_APACHE2
diff --git a/extras/ext4_utils/NOTICE b/extras/ext4_utils/NOTICE new file mode 100644 index 0000000..5d14293 --- /dev/null +++ b/extras/ext4_utils/NOTICE
@@ -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 +
diff --git a/extras/ext4_utils/allocate.c b/extras/ext4_utils/allocate.c new file mode 100644 index 0000000..3fd39c6 --- /dev/null +++ b/extras/ext4_utils/allocate.c
@@ -0,0 +1,733 @@ +/* + * 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 "ext4.h" + +#include <sparse/sparse.h> + +#include <stdio.h> +#include <stdlib.h> + +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; +}; + +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 used_dirs; +}; + +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; + return alloc; +} + +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->bg * info.blocks_per_group + reg->block, + reg->bg * info.blocks_per_group + 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 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(info.sparse_file, bg->inode_table, + aux_info.inode_table_blocks * info.block_size, block); +} + +void init_unused_inode_tables(void) +{ + unsigned int i; + u32 block; + struct block_group_info *bg; + + for (i = 0; i < aux_info.groups; i++) { + if (!aux_info.bgs[i].inode_table) { + bg = &aux_info.bgs[i]; + block = bg->first_block + 2; + if (bg->has_superblock) + block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1; + sparse_file_add_fill(info.sparse_file, 0, + aux_info.inode_table_blocks * info.block_size, block); + } + } +} + +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(info.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; + + 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); + + u32 overrun = bg->first_block + info.blocks_per_group - aux_info.len_blocks; + if (overrun > 0) + 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); +} + +/* 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; +} + +/* 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); +}
diff --git a/extras/ext4_utils/allocate.h b/extras/ext4_utils/allocate.h new file mode 100644 index 0000000..fb6e0f7 --- /dev/null +++ b/extras/ext4_utils/allocate.h
@@ -0,0 +1,57 @@ +/* + * 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" +#include "ext4.h" + +struct block_allocation; + +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); +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); +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); + +#endif
diff --git a/extras/ext4_utils/contents.c b/extras/ext4_utils/contents.c new file mode 100644 index 0000000..aeed31e --- /dev/null +++ b/extras/ext4_utils/contents.c
@@ -0,0 +1,292 @@ +/* + * 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> + +#include "ext4_utils.h" +#include "ext4.h" +#include "make_ext4fs.h" +#include "allocate.h" +#include "contents.h" +#include "extent.h" +#include "indirect.h" +#include "xattr.h" + +#ifdef USE_MINGW +#define S_IFLNK 0 /* used by make_link, not needed under mingw */ +#endif + +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 + 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 + 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) + inode_allocate_file_extents(inode, len, filename); + + 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 *filename, 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; +} + +#ifdef HAVE_SELINUX +#define XATTR_SELINUX_SUFFIX "selinux" + +/* XXX */ +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) + +int inode_set_selinux(u32 inode_num, const char *secon) +{ + struct ext4_inode *inode = get_inode(inode_num); + u32 *hdr; + struct ext4_xattr_entry *entry; + size_t name_len = strlen(XATTR_SELINUX_SUFFIX); + size_t value_len; + size_t size, min_offs; + char *val; + + if (!secon) + return 0; + + if (!inode) + return -1; + + hdr = (u32 *) (inode + 1); + *hdr = cpu_to_le32(EXT4_XATTR_MAGIC); + entry = (struct ext4_xattr_entry *) (hdr+1); + memset(entry, 0, EXT4_XATTR_LEN(name_len)); + entry->e_name_index = EXT4_XATTR_INDEX_SECURITY; + entry->e_name_len = name_len; + memcpy(entry->e_name, XATTR_SELINUX_SUFFIX, name_len); + value_len = strlen(secon)+1; + entry->e_value_size = cpu_to_le32(value_len); + min_offs = (char *)inode + info.inode_size - (char*) entry; + size = EXT4_XATTR_SIZE(value_len); + val = (char *)entry + min_offs - size; + entry->e_value_offs = cpu_to_le16(min_offs - size); + memset(val + size - EXT4_XATTR_PAD, 0, EXT4_XATTR_PAD); + memcpy(val, secon, value_len); + inode->i_extra_isize = cpu_to_le16(sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE); + + return 0; +} +#else +int inode_set_selinux(u32 inode_num, const char *secon) +{ + return 0; +} +#endif
diff --git a/extras/ext4_utils/contents.h b/extras/ext4_utils/contents.h new file mode 100644 index 0000000..35867fd --- /dev/null +++ b/extras/ext4_utils/contents.h
@@ -0,0 +1,41 @@ +/* + * 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; +}; + +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 *filename, 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); +#endif
diff --git a/extras/ext4_utils/ext2simg.c b/extras/ext4_utils/ext2simg.c new file mode 100644 index 0000000..f4b6c93 --- /dev/null +++ b/extras/ext4_utils/ext2simg.c
@@ -0,0 +1,256 @@ +/* + * 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 read_ext(int fd) +{ + off64_t ret; + struct ext4_super_block sb; + unsigned int i; + + ret = lseek64(fd, 1024, SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to superblock"); + + ret = read(fd, &sb, sizeof(sb)); + if (ret < 0) + critical_error_errno("failed to read superblock"); + if (ret != sizeof(sb)) + critical_error("failed to read all of superblock"); + + ext4_parse_sb(&sb); + + ret = lseek64(fd, info.len, SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to end of input image"); + + ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to block group descriptors"); + + ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks); + if (ret < 0) + critical_error_errno("failed to read block group descriptors"); + if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks) + critical_error("failed to read all of block group descriptors"); + + if (verbose) { + printf("Found filesystem with parameters:\n"); + printf(" Size: %llu\n", info.len); + printf(" Block size: %d\n", info.block_size); + printf(" Blocks per group: %d\n", info.blocks_per_group); + printf(" Inodes per group: %d\n", info.inodes_per_group); + printf(" Inode size: %d\n", info.inode_size); + printf(" Label: %s\n", info.label); + printf(" Blocks: %llu\n", aux_info.len_blocks); + printf(" Block groups: %d\n", aux_info.groups); + printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); + printf(" Used %d/%d inodes and %d/%d blocks\n", + aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, + aux_info.sb->s_inodes_count, + aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, + aux_info.sb->s_blocks_count_lo); + } + + return 0; +} + +static int bitmap_get_bit(u8 *bitmap, u32 bit) +{ + if (bitmap[bit / 8] & 1 << (bit % 8)) + return 1; + + return 0; +} + +static int build_sparse_ext(int fd, const char *filename) +{ + unsigned int i; + unsigned int block; + int start_contiguous_block; + u8 *block_bitmap; + off64_t ret; + + block_bitmap = malloc(info.block_size); + if (!block_bitmap) + critical_error("failed to allocate block bitmap"); + + if (aux_info.first_data_block > 0) + sparse_file_add_file(info.sparse_file, filename, 0, + info.block_size * aux_info.first_data_block, 0); + + for (i = 0; i < aux_info.groups; i++) { + u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; + u32 last_block = min(info.blocks_per_group, aux_info.len_blocks - first_block); + + ret = lseek64(fd, (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap, + SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to block group bitmap %d", i); + + ret = read(fd, block_bitmap, info.block_size); + if (ret < 0) + critical_error_errno("failed to read block group bitmap %d", i); + if (ret != (int)info.block_size) + critical_error("failed to read all of block group bitmap %d", i); + + start_contiguous_block = -1; + for (block = 0; block < last_block; block++) { + if (start_contiguous_block >= 0) { + if (!bitmap_get_bit(block_bitmap, block)) { + u32 start_block = first_block + start_contiguous_block; + u32 len_blocks = block - start_contiguous_block; + + sparse_file_add_file(info.sparse_file, filename, + (u64)info.block_size * start_block, + info.block_size * len_blocks, start_block); + start_contiguous_block = -1; + } + } else { + if (bitmap_get_bit(block_bitmap, block)) + start_contiguous_block = block; + } + } + + if (start_contiguous_block >= 0) { + u32 start_block = first_block + start_contiguous_block; + u32 len_blocks = last_block - start_contiguous_block; + sparse_file_add_file(info.sparse_file, filename, + (u64)info.block_size * start_block, + info.block_size * len_blocks, start_block); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + int opt; + const char *in = NULL; + const char *out = NULL; + int gzip = 0; + int sparse = 1; + int infd, outfd; + int crc = 0; + + while ((opt = getopt(argc, argv, "cvzS")) != -1) { + switch (opt) { + case 'c': + crc = 1; + break; + case 'v': + verbose = 1; + break; + case 'z': + gzip = 1; + break; + case 'S': + sparse = 0; + break; + } + } + + if (optind >= argc) { + fprintf(stderr, "Expected image or block device after options\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + in = argv[optind++]; + + if (optind >= argc) { + fprintf(stderr, "Expected output image after input image\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + out = argv[optind++]; + + if (optind < argc) { + fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + infd = open(in, O_RDONLY); + + if (infd < 0) + critical_error_errno("failed to open input image"); + + read_ext(infd); + + info.sparse_file = sparse_file_new(info.block_size, info.len); + + build_sparse_ext(infd, in); + + close(infd); + + if (strcmp(out, "-")) { + outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + if (outfd < 0) { + error_errno("open"); + return EXIT_FAILURE; + } + } else { + outfd = STDOUT_FILENO; + } + + write_ext4_image(outfd, gzip, sparse, crc); + close(outfd); + + sparse_file_destroy(info.sparse_file); + + return 0; +}
diff --git a/extras/ext4_utils/ext4.h b/extras/ext4_utils/ext4.h new file mode 100644 index 0000000..68b5233 --- /dev/null +++ b/extras/ext4_utils/ext4.h
@@ -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) 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 +
diff --git a/extras/ext4_utils/ext4_extents.h b/extras/ext4_utils/ext4_extents.h new file mode 100644 index 0000000..b1290f4 --- /dev/null +++ b/extras/ext4_utils/ext4_extents.h
@@ -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 + +
diff --git a/extras/ext4_utils/ext4_jbd2.h b/extras/ext4_utils/ext4_jbd2.h new file mode 100644 index 0000000..d937f5c --- /dev/null +++ b/extras/ext4_utils/ext4_jbd2.h
@@ -0,0 +1,53 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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_JBD2_H +#define _EXT4_JBD2_H + +#include "ext4.h" + +#define EXT4_JOURNAL(inode) (EXT4_SB((inode)->i_sb)->s_journal) + +#define EXT4_SINGLEDATA_TRANS_BLOCKS(sb) (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS) ? 27U : 8U) + +#define EXT4_XATTR_TRANS_BLOCKS 6U + +#define EXT4_DATA_TRANS_BLOCKS(sb) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + EXT4_XATTR_TRANS_BLOCKS - 2 + EXT4_MAXQUOTAS_TRANS_BLOCKS(sb)) + +#define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS + EXT4_MAXQUOTAS_TRANS_BLOCKS(sb)) + +#define EXT4_DELETE_TRANS_BLOCKS(sb) (2 * EXT4_DATA_TRANS_BLOCKS(sb) + 64) + +#define EXT4_MAX_TRANS_DATA 64U + +#define EXT4_RESERVE_TRANS_BLOCKS 12U + +#define EXT4_INDEX_EXTRA_TRANS_BLOCKS 8 + +#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0 +#define EXT4_QUOTA_INIT_BLOCKS(sb) 0 +#define EXT4_QUOTA_DEL_BLOCKS(sb) 0 +#define EXT4_MAXQUOTAS_TRANS_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_TRANS_BLOCKS(sb)) +#define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb)) +#define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb)) + +#define ext4_journal_get_undo_access(handle, bh) __ext4_journal_get_undo_access(__func__, (handle), (bh)) +#define ext4_journal_get_write_access(handle, bh) __ext4_journal_get_write_access(__func__, (handle), (bh)) +#define ext4_forget(handle, is_metadata, inode, bh, block_nr) __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh), (block_nr)) +#define ext4_journal_get_create_access(handle, bh) __ext4_journal_get_create_access(__func__, (handle), (bh)) +#define ext4_handle_dirty_metadata(handle, inode, bh) __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh)) + +#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096) + +#define ext4_journal_stop(handle) __ext4_journal_stop(__func__, (handle)) + +#endif +
diff --git a/extras/ext4_utils/ext4_utils.c b/extras/ext4_utils/ext4_utils.c new file mode 100644 index 0000000..43b4480 --- /dev/null +++ b/extras/ext4_utils/ext4_utils.c
@@ -0,0 +1,442 @@ +/* + * 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 <sys/stat.h> +#include <sys/types.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 + +#include "ext4.h" +#include "jbd2.h" + +int force = 0; +struct fs_info info; +struct fs_aux_info aux_info; + +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; +} + +/* 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; +} + +/* Write the filesystem image to a file */ +void write_ext4_image(int fd, int gz, int sparse, int crc) +{ + sparse_file_write(info.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"); +} + +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 = 0; + 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(info.sparse_file, aux_info.backup_sb[i], + info.block_size, group_start_block); + } + sparse_file_add_data(info.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(info.sparse_file, buf, info.block_size, 0); + } else { + sparse_file_add_data(info.sparse_file, aux_info.sb, 1024, 1); + } +} + +void ext4_parse_sb(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"); + + 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; + + aux_info.len_blocks = ((u64)sb->s_blocks_count_hi << 32) + + sb->s_blocks_count_lo; + info.len = (u64)info.block_size * aux_info.len_blocks; + + 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() +{ + unsigned int 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); + + 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); + } +} + +static 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; +} + +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; +}
diff --git a/extras/ext4_utils/ext4_utils.h b/extras/ext4_utils/ext4_utils.h new file mode 100644 index 0000000..cad2eae --- /dev/null +++ b/extras/ext4_utils/ext4_utils.h
@@ -0,0 +1,174 @@ +/* + * 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> + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define ftruncate64 ftruncate +#define mmap64 mmap +#define off64_t off_t +#endif + +#ifdef __BIONIC__ +extern void* __mmap2(void *, size_t, int, int, int, off_t); +static inline void *mmap64(void *addr, size_t length, int prot, int flags, + int fd, off64_t offset) +{ + return __mmap2(addr, length, prot, flags, fd, offset >> 12); +} +#endif + +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_SUPER_MAGIC 0xEF53 +#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 ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y))) + +#define __le64 u64 +#define __le32 u32 +#define __le16 u16 + +#define __be64 u64 +#define __be32 u32 +#define __be16 u16 + +#define __u64 u64 +#define __u32 u32 +#define __u16 u16 +#define __u8 u8 + +typedef unsigned long long u64; +typedef signed long long s64; +typedef unsigned int u32; +typedef unsigned short int u16; +typedef unsigned char u8; + +struct block_group_info; + +struct ext2_group_desc { + __le32 bg_block_bitmap; + __le32 bg_inode_bitmap; + __le32 bg_inode_table; + __le16 bg_free_blocks_count; + __le16 bg_free_inodes_count; + __le16 bg_used_dirs_count; + __le16 bg_pad; + __le32 bg_reserved[3]; +}; + +struct fs_info { + s64 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. */ + u32 block_size; + u32 blocks_per_group; + u32 inodes_per_group; + u32 inode_size; + u32 inodes; + u32 journal_blocks; + u16 feat_ro_compat; + u16 feat_compat; + u16 feat_incompat; + u32 bg_desc_reserve_blocks; + const char *label; + u8 no_journal; + + struct sparse_file *sparse_file; +}; + +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; + 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 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 ext4_bg_has_super_block(int bg); +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_file_size(int fd); +u64 parse_num(const char *arg); +void ext4_parse_sb(struct ext4_super_block *sb); + +#ifdef __cplusplus +} +#endif + +#endif
diff --git a/extras/ext4_utils/ext4fixup.c b/extras/ext4_utils/ext4fixup.c new file mode 100644 index 0000000..f0124f8 --- /dev/null +++ b/extras/ext4_utils/ext4fixup.c
@@ -0,0 +1,898 @@ +/* + * 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; +} + +/* Function to read the primary superblock */ +static 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 */ +static void write_sb(int fd, unsigned long long offset, struct ext4_super_block *sb) +{ + off64_t ret; + + if (no_write) { + return; + } + + 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"); +} + +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; + } + write_sb(fd, 1024, &sb); + + return 0; +} + +static int read_ext(int fd) +{ + off64_t ret; + struct ext4_super_block sb; + unsigned int i; + + read_sb(fd, &sb); + + ext4_parse_sb(&sb); + + 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 (((sb.s_desc_size & ~1) != sizeof(struct ext2_group_desc)) && + ((sb.s_desc_size & ~1) != 0)) + critical_error("error: bg_desc_size != sizeof(struct ext2_group_desc)\n"); + + ret = lseek64(fd, info.len, SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to end of input image"); + + ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET); + if (ret < 0) + critical_error_errno("failed to seek to block group descriptors"); + + ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks); + if (ret < 0) + critical_error_errno("failed to read block group descriptors"); + if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks) + critical_error("failed to read all of block group descriptors"); + + if (verbose) { + printf("Found filesystem with parameters:\n"); + printf(" Size: %llu\n", info.len); + printf(" Block size: %d\n", info.block_size); + printf(" Blocks per group: %d\n", info.blocks_per_group); + printf(" Inodes per group: %d\n", info.inodes_per_group); + printf(" Inode size: %d\n", info.inode_size); + printf(" Label: %s\n", info.label); + printf(" Blocks: %llu\n", aux_info.len_blocks); + printf(" Block groups: %d\n", aux_info.groups); + printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); + printf(" Used %d/%d inodes and %d/%d blocks\n", + aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, + aux_info.sb->s_inodes_count, + aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, + aux_info.sb->s_blocks_count_lo); + } + + return 0; +} + +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 int bitmap_get_bit(u8 *bitmap, u32 bit) +{ + if (bitmap[bit / 8] & (1 << (bit % 8))) + return 1; + + return 0; +} + +static void bitmap_clear_bit(u8 *bitmap, u32 bit) +{ + bitmap[bit / 8] &= ~(1 << (bit % 8)); + + return; +} + +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; + } + + 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(int fd, 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(fd, 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(fd, (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); + + 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=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; +}
diff --git a/extras/ext4_utils/ext4fixup.h b/extras/ext4_utils/ext4fixup.h new file mode 100644 index 0000000..6ea2113 --- /dev/null +++ b/extras/ext4_utils/ext4fixup.h
@@ -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); +
diff --git a/extras/ext4_utils/ext4fixup_main.c b/extras/ext4_utils/ext4fixup_main.c new file mode 100644 index 0000000..47c7e65 --- /dev/null +++ b/extras/ext4_utils/ext4fixup_main.c
@@ -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); +}
diff --git a/extras/ext4_utils/extent.c b/extras/ext4_utils/extent.c new file mode 100644 index 0000000..948bf41 --- /dev/null +++ b/extras/ext4_utils/extent.c
@@ -0,0 +1,234 @@ +/* + * 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 "ext4.h" +#include "ext4_extents.h" +#include "extent.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 *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(info.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(info.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(info.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 %llu is too big to fit in a single extent block\n", + len); + return NULL; + } + + hdr = (struct ext4_extent_header *)data; + hdr->eh_magic = EXT4_EXT_MAGIC; + hdr->eh_entries = allocation_len; + hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); + hdr->eh_generation = 0; + hdr->eh_depth = 0; + + extent = (struct ext4_extent *)(data + + sizeof(struct ext4_extent_header)); + } + + for (; !last_region(alloc); extent++, get_next_region(alloc)) { + u32 region_block; + u32 region_len; + + get_region(alloc, ®ion_block, ®ion_len); + extent->ee_block = file_block; + extent->ee_len = region_len; + extent->ee_start_hi = 0; + extent->ee_start_lo = region_block; + file_block += region_len; + } + + if (extent_block) + block_len += 1; + + blocks = (u64)block_len * info.block_size / 512; + + inode->i_flags |= EXT4_EXTENTS_FL; + inode->i_size_lo = len; + inode->i_size_high = len >> 32; + inode->i_blocks_lo = blocks; + inode->osd2.linux2.l_i_blocks_high = blocks >> 32; + + rewind_alloc(alloc); + + return alloc; +} + +/* 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 %llu bytes", len); + return NULL; + } + + if (backing_len) { + data = extent_create_backing(alloc, backing_len); + if (!data) + error("failed to create backing for %llu 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. */ +void 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 %llu bytes", len); + return; + } + + extent_create_backing_file(alloc, len, filename); + + free_alloc(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 %llu bytes", len); + return; + } + + free_alloc(alloc); +}
diff --git a/extras/ext4_utils/extent.h b/extras/ext4_utils/extent.h new file mode 100644 index 0000000..a1ddeb1 --- /dev/null +++ b/extras/ext4_utils/extent.h
@@ -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); +void 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
diff --git a/extras/ext4_utils/indirect.c b/extras/ext4_utils/indirect.c new file mode 100644 index 0000000..3d97ec8 --- /dev/null +++ b/extras/ext4_utils/indirect.c
@@ -0,0 +1,516 @@ +/* + * 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 "ext4.h" +#include "ext4_extents.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(info.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(info.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(info.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(info.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(info.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(info.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( + struct ext4_inode *inode, 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(inode, 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(info.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(info.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(inode, 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; +}
diff --git a/extras/ext4_utils/indirect.h b/extras/ext4_utils/indirect.h new file mode 100644 index 0000000..cee8979 --- /dev/null +++ b/extras/ext4_utils/indirect.h
@@ -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
diff --git a/extras/ext4_utils/jbd2.h b/extras/ext4_utils/jbd2.h new file mode 100644 index 0000000..1c81641 --- /dev/null +++ b/extras/ext4_utils/jbd2.h
@@ -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 +
diff --git a/extras/ext4_utils/make_ext4fs.c b/extras/ext4_utils/make_ext4fs.c new file mode 100644 index 0000000..b22355a --- /dev/null +++ b/extras/ext4_utils/make_ext4fs.c
@@ -0,0 +1,547 @@ +/* + * 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 <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 + +#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() +{ + u32 inode; + u32 root_inode; + struct dentry dentries = { + .filename = "lost+found", + .file_type = EXT4_FT_DIR, + .mode = S_IRWXU, + .uid = 0, + .gid = 0, + .mtime = 0, + }; + root_inode = make_directory(0, 1, &dentries, 1); + inode = make_directory(root_inode, 0, NULL, 0); + *dentries.inode = inode; + inode_set_permissions(inode, dentries.mode, + dentries.uid, dentries.gid, dentries.mtime); + + return root_inode; +} + +#ifndef USE_MINGW +/* Read a local directory and create the same tree in the generated filesystem. + Calls itself recursively with each directory in the given directory. + full_path is an absolute or relative path, with a trailing slash, to the + directory on disk that should be copied, or NULL if this is a directory + that does not exist on disk (e.g. lost+found). + dir_path is an absolute path, with trailing slash, to the same directory + if the image were mounted at the specified mount point */ +static u32 build_directory_structure(const char *full_path, const char *dir_path, + u32 dir_inode, fs_config_func_t fs_config_func, + struct selabel_handle *sehnd) +{ + int entries = 0; + struct dentry *dentries; + struct dirent **namelist = NULL; + struct stat stat; + int ret; + int i; + u32 inode; + u32 entry_inode; + u32 dirs = 0; + bool needs_lost_and_found = false; + + if (full_path) { + entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); + if (entries < 0) { + error_errno("scandir"); + return EXT4_ALLOCATE_FAILED; + } + } + + if (dir_inode == 0) { + /* root directory, check if lost+found already exists */ + for (i = 0; i < entries; i++) + if (strcmp(namelist[i]->d_name, "lost+found") == 0) + break; + if (i == entries) + needs_lost_and_found = true; + } + + dentries = calloc(entries, sizeof(struct dentry)); + if (dentries == NULL) + critical_error_errno("malloc"); + + for (i = 0; i < entries; i++) { + dentries[i].filename = strdup(namelist[i]->d_name); + if (dentries[i].filename == NULL) + critical_error_errno("strdup"); + + asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); + asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name); + + free(namelist[i]); + + ret = lstat(dentries[i].full_path, &stat); + if (ret < 0) { + error_errno("lstat"); + i--; + entries--; + continue; + } + + dentries[i].size = stat.st_size; + dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); + dentries[i].mtime = stat.st_mtime; + if (fs_config_func != NULL) { +#ifdef ANDROID + unsigned int mode = 0; + unsigned int uid = 0; + unsigned int gid = 0; + uint64_t capabilities = 0; + int dir = S_ISDIR(stat.st_mode); + fs_config_func(dentries[i].path, dir, /*target_out_path=*/NULL, &uid, &gid, &mode, &capabilities); + dentries[i].mode = mode; + dentries[i].uid = uid; + dentries[i].gid = gid; +#else + error("can't set android permissions - built without android support"); +#endif + } + +#ifdef HAVE_SELINUX + if (sehnd) { + 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) + 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; +#ifdef HAVE_SELINUX + 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); + } +#endif + entries++; + dirs++; + } + + inode = make_directory(dir_inode, entries, dentries, dirs); + + for (i = 0; i < entries; i++) { + if (dentries[i].file_type == EXT4_FT_REG_FILE) { + entry_inode = make_file(dentries[i].full_path, dentries[i].size); + } else if (dentries[i].file_type == EXT4_FT_DIR) { + 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); + free(subdir_full_path); + free(subdir_dir_path); + } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { + entry_inode = make_link(dentries[i].full_path, dentries[i].link); + } else { + error("unknown file type on %s", dentries[i].path); + entry_inode = 0; + } + *dentries[i].inode = entry_inode; + + ret = inode_set_permissions(entry_inode, dentries[i].mode, + dentries[i].uid, dentries[i].gid, + dentries[i].mtime); + if (ret) + error("failed to set permissions on %s\n", dentries[i].path); + ret = inode_set_selinux(entry_inode, dentries[i].secon); + if (ret) + error("failed to set SELinux context on %s\n", dentries[i].path); + + free(dentries[i].path); + free(dentries[i].full_path); + free(dentries[i].link); + free((void *)dentries[i].filename); + free(dentries[i].secon); + } + + free(dentries); + return inode; +} +#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 = 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 (info.sparse_file) { + sparse_file_destroy(info.sparse_file); + info.sparse_file = NULL; + } +} + +int make_ext4fs(const char *filename, s64 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, 0, sehnd); + close(fd); + + return status; +} + +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, int init_itabs, struct selabel_handle *sehnd) +{ + u32 root_inode_num; + u16 root_mode; + char *mountpoint; + char *directory = NULL; + int ret; + + if (setjmp(setjmp_env)) + return EXIT_FAILURE; /* Handle a call to longjmp() */ + + if (_mountpoint == NULL) { + mountpoint = strdup(""); + } else if (_mountpoint[0] == '\0') { + mountpoint = strdup("/"); + } else { + ret = asprintf(&mountpoint, "%s%s%s", + _mountpoint[0] == '/' ? "" : "/", + _mountpoint, + _mountpoint[strlen(_mountpoint) - 1] == '/' ? "" : "/"); + if (ret < 0) + critical_error_errno("asprintf"); + } + + if (_directory) { + ret = asprintf(&directory, "%s%s", + _directory, + _directory[strlen(_directory) - 1] == '/' ? "" : "/"); + if (ret < 0) + critical_error_errno("asprintf"); + } + + if (info.len <= 0) + info.len = get_file_size(fd); + + if (info.len <= 0) { + fprintf(stderr, "Need size of filesystem\n"); + return EXIT_FAILURE; + } + + if (info.block_size <= 0) + info.block_size = compute_block_size(); + + /* Round down the filesystem length to be a multiple of the block size */ + info.len &= ~((u64)info.block_size - 1); + + if (info.journal_blocks == 0) + info.journal_blocks = compute_journal_blocks(); + + if (info.no_journal == 0) + info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; + else + info.journal_blocks = 0; + + if (info.blocks_per_group <= 0) + info.blocks_per_group = compute_blocks_per_group(); + + if (info.inodes <= 0) + info.inodes = compute_inodes(); + + if (info.inode_size <= 0) + info.inode_size = 256; + + if (info.label == NULL) + info.label = ""; + + info.inodes_per_group = compute_inodes_per_group(); + + info.feat_compat |= + EXT4_FEATURE_COMPAT_RESIZE_INODE; + + info.feat_ro_compat |= + EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | + EXT4_FEATURE_RO_COMPAT_LARGE_FILE; + + info.feat_incompat |= + EXT4_FEATURE_INCOMPAT_EXTENTS | + EXT4_FEATURE_INCOMPAT_FILETYPE; + + + info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); + + printf("Creating filesystem with parameters:\n"); + printf(" Size: %llu\n", info.len); + printf(" Block size: %d\n", info.block_size); + printf(" Blocks per group: %d\n", info.blocks_per_group); + printf(" Inodes per group: %d\n", info.inodes_per_group); + printf(" Inode size: %d\n", info.inode_size); + printf(" Journal blocks: %d\n", info.journal_blocks); + printf(" Label: %s\n", info.label); + + ext4_create_fs_aux_info(); + + printf(" Blocks: %llu\n", aux_info.len_blocks); + printf(" Block groups: %d\n", aux_info.groups); + printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); + + info.sparse_file = sparse_file_new(info.block_size, info.len); + + block_allocator_init(); + + ext4_fill_in_sb(); + + if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) + error("failed to reserve first 10 inodes"); + + if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) + ext4_create_journal_inode(); + + if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) + ext4_create_resize_inode(); + +#ifdef USE_MINGW + // Windows needs only 'create an empty fs image' functionality + assert(!directory); + root_inode_num = build_default_directory_structure(); +#else + if (directory) + root_inode_num = build_directory_structure(directory, mountpoint, 0, + fs_config_func, sehnd); + else + root_inode_num = build_default_directory_structure(); +#endif + + root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); + +#ifdef HAVE_SELINUX + if (sehnd) { + char *secontext = NULL; + + if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) { + error("cannot lookup security context for %s", mountpoint); + } + if (secontext) { + printf("Labeling %s as %s\n", mountpoint, secontext); + inode_set_selinux(root_inode_num, secontext); + } + freecon(secontext); + } +#endif + + ext4_update_free(); + + if (init_itabs) + init_unused_inode_tables(); + + ext4_queue_sb(); + + printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", + aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, + aux_info.sb->s_inodes_count, + aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, + aux_info.sb->s_blocks_count_lo); + + if (wipe) + wipe_block_device(fd, info.len); + + write_ext4_image(fd, gzip, sparse, crc); + + sparse_file_destroy(info.sparse_file); + info.sparse_file = NULL; + + free(mountpoint); + free(directory); + + return 0; +}
diff --git a/extras/ext4_utils/make_ext4fs.h b/extras/ext4_utils/make_ext4fs.h new file mode 100644 index 0000000..fb44485 --- /dev/null +++ b/extras/ext4_utils/make_ext4fs.h
@@ -0,0 +1,50 @@ +/* + * 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_ + +#include <stdint.h> + +#include "ext4_utils.h" +#include "ext4.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#include <selinux/label.h> +#else +struct selabel_handle; +#endif + +typedef void (*fs_config_func_t)(const char *path, int dir, const char* target_out_path, unsigned *uid, unsigned *gid, + unsigned *mode, uint64_t *capabilities); + +void reset_ext4fs_info(); +int make_ext4fs(const char *filename, s64 len, + const char *mountpoint, struct selabel_handle *sehnd); +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, int init_itabs, struct selabel_handle *sehnd); + +#ifdef __cplusplus +} +#endif + +#endif
diff --git a/extras/ext4_utils/make_ext4fs_main.c b/extras/ext4_utils/make_ext4fs_main.c new file mode 100644 index 0000000..48563f4 --- /dev/null +++ b/extras/ext4_utils/make_ext4fs_main.c
@@ -0,0 +1,196 @@ +/* + * 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 <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 +#include <private/fs_config.h> + +#include "make_ext4fs.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 ]\n"); + fprintf(stderr, " [ -z | -s ] [ -t ] [ -w ] [ -c ] [ -J ] [ -A ]\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; + int gzip = 0; + int sparse = 0; + int crc = 0; + int wipe = 0; + int init_itabs = 0; + int set_config = 0; + int fd; + int exitcode; + struct selabel_handle *sehnd = NULL; +#ifdef HAVE_SELINUX + struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } }; +#endif + + while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fwzJsctS:A")) != -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 mountpoint - 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': + init_itabs = 1; + break; + case 'S': +#ifdef HAVE_SELINUX + seopts[0].value = optarg; + sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); + if (!sehnd) { + perror(optarg); + exit(EXIT_FAILURE); + } +#endif + break; + case 'A': +#if defined(ANDROID) || defined(BUILD_EUREKA) + set_config = 1; /* use android_filesystem_config */ +#else + fprintf(stderr, "can't set android permissions - built without android support\n"); + usage(argv[0]); + exit(EXIT_FAILURE); +#endif + break; + default: /* '?' */ + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (mountpoint || set_config) + 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) { + error_errno("open"); + return EXIT_FAILURE; + } + } else { + fd = STDOUT_FILENO; + } + + exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip, + sparse, crc, wipe, init_itabs, sehnd); + close(fd); + + return exitcode; +}
diff --git a/extras/ext4_utils/mkuserimg.sh b/extras/ext4_utils/mkuserimg.sh new file mode 100755 index 0000000..1136a9e --- /dev/null +++ b/extras/ext4_utils/mkuserimg.sh
@@ -0,0 +1,60 @@ +#!/bin/bash -x +# +# 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 [FILE_CONTEXTS] +EOT +} + +echo "in mkuserimg.sh PATH=$PATH" + +ENABLE_SPARSE_IMAGE= +if [ "$1" = "-s" ]; then + ENABLE_SPARSE_IMAGE="-s" + shift +fi + +if [ $# -ne 4 -a $# -ne 5 -a $# -ne 6 ]; 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 +FC=$6 + +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 + SIZE=128M +fi + +if [ -n "$FC" ]; then + FCOPT="-S $FC" +fi + +MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE $FCOPT -l $SIZE -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR" +echo $MAKE_EXT4FS_CMD +$MAKE_EXT4FS_CMD +if [ $? -ne 0 ]; then + exit 4 +fi
diff --git a/extras/ext4_utils/setup_fs.c b/extras/ext4_utils/setup_fs.c new file mode 100644 index 0000000..56c563f --- /dev/null +++ b/extras/ext4_utils/setup_fs.c
@@ -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; +}
diff --git a/extras/ext4_utils/sha1.c b/extras/ext4_utils/sha1.c new file mode 100644 index 0000000..463ec38 --- /dev/null +++ b/extras/ext4_utils/sha1.c
@@ -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 */
diff --git a/extras/ext4_utils/sha1.h b/extras/ext4_utils/sha1.h new file mode 100644 index 0000000..9a8f7e3 --- /dev/null +++ b/extras/ext4_utils/sha1.h
@@ -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_ */
diff --git a/extras/ext4_utils/test_ext4fixup b/extras/ext4_utils/test_ext4fixup new file mode 100755 index 0000000..a920a59 --- /dev/null +++ b/extras/ext4_utils/test_ext4fixup
@@ -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 +
diff --git a/extras/ext4_utils/uuid.c b/extras/ext4_utils/uuid.c new file mode 100644 index 0000000..33d2494 --- /dev/null +++ b/extras/ext4_utils/uuid.c
@@ -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; +}
diff --git a/extras/ext4_utils/uuid.h b/extras/ext4_utils/uuid.h new file mode 100644 index 0000000..ff1b438 --- /dev/null +++ b/extras/ext4_utils/uuid.h
@@ -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
diff --git a/extras/ext4_utils/wipe.c b/extras/ext4_utils/wipe.c new file mode 100644 index 0000000..2b111d6 --- /dev/null +++ b/extras/ext4_utils/wipe.c
@@ -0,0 +1,62 @@ +/* + * 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 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; + +#ifndef BUILD_EUREKA + range[0] = 0; + range[1] = len; + ret = ioctl(fd, BLKSECDISCARD, &range); + if (ret >= 0) + return 0; + warn("Wipe via secure discard failed, try discard instead\n"); +#endif + range[0] = 0; + range[1] = len; + ret = ioctl(fd, BLKDISCARD, &range); + if (ret < 0) { + warn("Discard failed\n"); + return 1; + } + return 0; +} +#else +int wipe_block_device(int fd, s64 len) +{ + error("wipe not supported on non-linux platforms"); + return 1; +} +#endif +
diff --git a/extras/ext4_utils/wipe.h b/extras/ext4_utils/wipe.h new file mode 100644 index 0000000..814877d --- /dev/null +++ b/extras/ext4_utils/wipe.h
@@ -0,0 +1,22 @@ +/* + * 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_ + +int wipe_block_device(int fd, s64 len); + +#endif
diff --git a/extras/ext4_utils/xattr.h b/extras/ext4_utils/xattr.h new file mode 100644 index 0000000..2c6d9cc --- /dev/null +++ b/extras/ext4_utils/xattr.h
@@ -0,0 +1,23 @@ +#include <sys/types.h> + +#define EXT4_XATTR_MAGIC 0xEA020000 +#define EXT4_XATTR_INDEX_SECURITY 6 + +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_SIZE(size) \ + (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
diff --git a/toolbox/Android.mk b/toolbox/Android.mk new file mode 100644 index 0000000..7cb2620 --- /dev/null +++ b/toolbox/Android.mk
@@ -0,0 +1,225 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +TOOLS := \ + ls \ + mount \ + cat \ + ps \ + kill \ + ln \ + insmod \ + rmmod \ + lsmod \ + setconsole \ + mkdir \ + rmdir \ + reboot \ + sendevent \ + date \ + wipe \ + sync \ + umount \ + start \ + stop \ + notify \ + cmp \ + route \ + hd \ + getprop \ + setprop \ + watchprops \ + log \ + sleep \ + renice \ + printenv \ + smd \ + chmod \ + chown \ + newfs_msdos \ + netstat \ + mv \ + schedtop \ + top \ + iftop \ + id \ + uptime \ + vmstat \ + nandread \ + ionice \ + touch \ + lsof \ + mknod \ + sntpd \ + exists \ + sudo \ + devmem \ + getevent + +ifeq ($(HAVE_SELINUX),true) + +TOOLS += \ + getenforce \ + setenforce \ + chcon \ + restorecon \ + runcon \ + getsebool \ + setsebool \ + load_policy + +endif + + +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +TOOLS += r +endif + +LOCAL_SRC_FILES:= \ + dynarray.c \ + toolbox.c \ + $(patsubst %,%.c,$(TOOLS)) + +ifeq ($(TARGET_USE_SYSTEM_LIBS),true) +LOCAL_CFLAGS += -Dlint +endif + +LOCAL_SHARED_LIBRARIES := libcutils + +ifeq ($(BUILD_EUREKA),true) +LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX),true) +LOCAL_CFLAGS += -DHAVE_SELINUX +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_C_INCLUDES += external/libselinux/include +endif + +LOCAL_MODULE := toolbox +ifdef SBIN_TOOLBOX +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +endif + +# Including this will define $(intermediates). +# +include $(BUILD_EXECUTABLE) + +$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h +$(intermediates)/tools.h: $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h + +TOOLS_H := $(intermediates)/tools.h +$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS) +$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done +$(TOOLS_H): $(LOCAL_PATH)/Android.mk +$(TOOLS_H): + $(transform-generated-source) + +# Make #!/system/bin/toolbox launchers for each tool. +# +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS)) +ifdef SBIN_TOOLBOX +$(SYMLINKS): TOOLBOX_BINARY := /sbin/$(LOCAL_MODULE) +else +$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) +endif +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(TOOLBOX_BINARY) $@ + +# Create one module that will wrap all the symlinks. This module requires the +# toolbox module, then adds an explicit dependency on all symlinks. +include $(CLEAR_VARS) +ifeq ($(BUILD_EUREKA),true) +LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +LOCAL_MODULE := toolbox_symlinks +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := toolbox +include $(BUILD_PHONY_PACKAGE) +$(LOCAL_BUILT_MODULE): $(SYMLINKS) + +# create root only toolbox_root +include $(CLEAR_VARS) + +TOOLS_ROOT := \ + dd + +LOCAL_SRC_FILES:= \ + dynarray.c \ + toolbox.c \ + $(patsubst %,%.c,$(TOOLS_ROOT)) + +LOCAL_MODULE := toolbox_root +ifdef SBIN_TOOLBOX +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +endif + +ifeq ($(TARGET_USE_SYSTEM_LIBS),true) +LOCAL_CFLAGS += -Dlint +endif + +LOCAL_SHARED_LIBRARIES := libcutils + +ifeq ($(BUILD_EUREKA),true) +LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +ifeq ($(HAVE_SELINUX),true) +LOCAL_CFLAGS += -DHAVE_SELINUX +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_C_INCLUDES += external/libselinux/include +endif + +include $(BUILD_EXECUTABLE) + +$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h +$(intermediates)/tools.h: $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h + +TOOLS_H := $(intermediates)/tools.h +$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS_ROOT) +$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done +$(TOOLS_H): $(LOCAL_PATH)/Android.mk +$(TOOLS_H): + $(transform-generated-source) + +SYMLINKS_ROOT := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS_ROOT)) +ifdef SBIN_TOOLBOX +$(SYMLINKS_ROOT): TOOLBOX_BINARY := /sbin/$(LOCAL_MODULE) +else +$(SYMLINKS_ROOT): TOOLBOX_BINARY := $(LOCAL_MODULE) +endif +$(SYMLINKS_ROOT): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(TOOLBOX_BINARY) $@ + +# Create one module that will wrap all the symlinks. This module requires the +# toolbox module, then adds an explicit dependency on all symlinks. +include $(CLEAR_VARS) +ifeq ($(BUILD_EUREKA),true) +LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h +endif # BUILD_EUREKA +LOCAL_MODULE := toolbox_root_symlinks +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := toolbox_root +include $(BUILD_PHONY_PACKAGE) +$(LOCAL_BUILT_MODULE): $(SYMLINKS_ROOT) + +ALL_DEFAULT_INSTALLED_MODULES += toolbox_symlinks toolbox_root_symlinks + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) toolbox_symlinks toolbox_root_symlinks + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := sntpd_unittest.c +LOCAL_CFLAGS += -UNDEBUG +LOCAL_MODULE := sntpd_unittest +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) +
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/toolbox/MODULE_LICENSE_BSD
diff --git a/toolbox/NOTICE b/toolbox/NOTICE new file mode 100644 index 0000000..895b49a --- /dev/null +++ b/toolbox/NOTICE
@@ -0,0 +1,193 @@ + +Copyright (c) 2010, The Android Open Source Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 2009, The Android Open Source Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 2008, The Android Open Source Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 1998 Robert Nordier +Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Kevin Fall. +This code is derived from software contributed to Berkeley by +Keith Muller of the University of California, San Diego and Lance +Visser of Convex Computer Corporation. +This code is derived from software contributed to Berkeley by +Mike Muuss. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + + This code is derived from software contributed to Berkeley by + Kevin Fall. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + + Copyright (c) 1991, 1993, 1994 + The Regents of the University of California. All rights reserved. + + This code is derived from software contributed to Berkeley by + Keith Muller of the University of California, San Diego and Lance + Visser of Convex Computer Corporation. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +
diff --git a/toolbox/alarm.c b/toolbox/alarm.c new file mode 100644 index 0000000..9bd58aa --- /dev/null +++ b/toolbox/alarm.c
@@ -0,0 +1,190 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <asm/ioctl.h> +//#include <linux/rtc.h> +#include <linux/android_alarm.h> + +int alarm_main(int argc, char *argv[]) +{ + int c; + int res; + struct tm tm; + time_t t; + struct timespec ts; +// struct rtc_time rtc_time; + char strbuf[26]; + int afd; + int nfd; +// struct timeval timeout = { 0, 0 }; + int wait = 0; + fd_set rfds; + const char wake_lock_id[] = "alarm_test"; + int waitalarmmask = 0; + + int useutc = 0; + android_alarm_type_t alarmtype_low = ANDROID_ALARM_RTC_WAKEUP; + android_alarm_type_t alarmtype_high = ANDROID_ALARM_RTC_WAKEUP; + android_alarm_type_t alarmtype = 0; + + do { + //c = getopt(argc, argv, "uw:"); + c = getopt(argc, argv, "uwat:"); + if (c == EOF) + break; + switch (c) { + case 'u': + useutc = 1; + break; + case 't': + alarmtype_low = alarmtype_high = strtol(optarg, NULL, 0); + break; + case 'a': + alarmtype_low = ANDROID_ALARM_RTC_WAKEUP; + alarmtype_high = ANDROID_ALARM_TYPE_COUNT - 1; + break; + case 'w': + //timeout.tv_sec = strtol(optarg, NULL, 0); + wait = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(optind + 2 < argc) { + fprintf(stderr,"%s [-uwa] [-t type] [seconds]\n", argv[0]); + return 1; + } + + afd = open("/dev/alarm", O_RDWR); + if(afd < 0) { + fprintf(stderr, "Unable to open rtc: %s\n", strerror(errno)); + return 1; + } + + if(optind == argc) { + for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) { + waitalarmmask |= 1U << alarmtype; + } +#if 0 + res = ioctl(fd, RTC_ALM_READ, &tm); + if(res < 0) { + fprintf(stderr, "Unable to read alarm: %s\n", strerror(errno)); + return 1; + } +#endif +#if 0 + t = timegm(&tm); + if(useutc) + gmtime_r(&t, &tm); + else + localtime_r(&t, &tm); +#endif +#if 0 + asctime_r(&tm, strbuf); + printf("%s", strbuf); +#endif + } + else if(optind + 1 == argc) { +#if 0 + res = ioctl(fd, RTC_RD_TIME, &tm); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + asctime_r(&tm, strbuf); + printf("Now: %s", strbuf); + time(&tv.tv_sec); +#endif +#if 0 + time(&ts.tv_sec); + ts.tv_nsec = 0; + + //strptime(argv[optind], NULL, &tm); + //tv.tv_sec = mktime(&tm); + //tv.tv_usec = 0; +#endif + for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) { + waitalarmmask |= 1U << alarmtype; + res = ioctl(afd, ANDROID_ALARM_GET_TIME(alarmtype), &ts); + if(res < 0) { + fprintf(stderr, "Unable to get current time: %s\n", strerror(errno)); + return 1; + } + ts.tv_sec += strtol(argv[optind], NULL, 0); + //strtotimeval(argv[optind], &tv); + gmtime_r(&ts.tv_sec, &tm); + printf("time %s -> %ld.%09ld\n", argv[optind], ts.tv_sec, ts.tv_nsec); + asctime_r(&tm, strbuf); + printf("Requested %s", strbuf); + + res = ioctl(afd, ANDROID_ALARM_SET(alarmtype), &ts); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + } +#if 0 + res = ioctl(fd, RTC_ALM_SET, &tm); + if(res < 0) { + fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno)); + return 1; + } + res = ioctl(fd, RTC_AIE_ON); + if(res < 0) { + fprintf(stderr, "Unable to enable alarm: %s\n", strerror(errno)); + return 1; + } +#endif + } + else { + fprintf(stderr,"%s [-u] [date]\n", argv[0]); + return 1; + } + + if(wait) { + while(waitalarmmask) { + printf("wait for alarm %x\n", waitalarmmask); + res = ioctl(afd, ANDROID_ALARM_WAIT); + if(res < 0) { + fprintf(stderr, "alarm wait failed\n"); + } + printf("got alarm %x\n", res); + waitalarmmask &= ~res; + nfd = open("/sys/android_power/acquire_full_wake_lock", O_RDWR); + write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1); + close(nfd); + //sleep(5); + nfd = open("/sys/android_power/release_wake_lock", O_RDWR); + write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1); + close(nfd); + } + printf("done\n"); + } +#if 0 + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + res = select(fd + 1, &rfds, NULL, NULL, &timeout); + if(res < 0) { + fprintf(stderr, "select failed: %s\n", strerror(errno)); + return 1; + } + if(res > 0) { + int event; + read(fd, &event, sizeof(event)); + fprintf(stderr, "got %x\n", event); + } + else { + fprintf(stderr, "timeout waiting for alarm\n"); + } +#endif + + close(afd); + + return 0; +}
diff --git a/toolbox/cat.c b/toolbox/cat.c new file mode 100644 index 0000000..6ac31f8 --- /dev/null +++ b/toolbox/cat.c
@@ -0,0 +1,291 @@ +/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define CAT_BUFSIZ (4096) + +static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag; +static int rval; +static const char *filename; + +static void +cook_buf(FILE *fp) +{ + int ch, gobble, line, prev; + int stdout_err = 0; + + line = gobble = 0; + for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { + if (prev == '\n') { + if (ch == '\n') { + if (sflag) { + if (!gobble && putchar(ch) == EOF) + break; + gobble = 1; + continue; + } + if (nflag) { + if (!bflag) { + if (fprintf(stdout, + "%6d\t", ++line) < 0) { + stdout_err++; + break; + } + } else if (eflag) { + if (fprintf(stdout, + "%6s\t", "") < 0) { + stdout_err++; + break; + } + } + } + } else if (nflag) { + if (fprintf(stdout, "%6d\t", ++line) < 0) { + stdout_err++; + break; + } + } + } + gobble = 0; + if (ch == '\n') { + if (eflag) + if (putchar('$') == EOF) + break; + } else if (ch == '\t') { + if (tflag) { + if (putchar('^') == EOF || putchar('I') == EOF) + break; + continue; + } + } else if (vflag) { + if (!isascii(ch)) { + if (putchar('M') == EOF || putchar('-') == EOF) + break; + ch = (ch) & 0x7f; + } + if (iscntrl(ch)) { + if (putchar('^') == EOF || + putchar(ch == '\177' ? '?' : + ch | 0100) == EOF) + break; + continue; + } + } + if (putchar(ch) == EOF) + break; + } + if (stdout_err) { + perror(filename); + rval = 1; + } +} + +static void +cook_args(char **argv) +{ + FILE *fp; + + fp = stdin; + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fp = stdin; + else if ((fp = fopen(*argv, + fflag ? "rf" : "r")) == NULL) { + perror("fopen"); + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + cook_buf(fp); + if (fp != stdin) + fclose(fp); + } while (*argv); +} + +static void +raw_cat(int rfd) +{ + static char *buf; + static char fb_buf[CAT_BUFSIZ]; + static size_t bsize; + + struct stat sbuf; + ssize_t nr, nw, off; + int wfd; + + wfd = fileno(stdout); + if (buf == NULL) { + if (fstat(wfd, &sbuf) == 0) { + bsize = sbuf.st_blksize > CAT_BUFSIZ ? + sbuf.st_blksize : CAT_BUFSIZ; + buf = malloc(bsize); + } + if (buf == NULL) { + buf = fb_buf; + bsize = CAT_BUFSIZ; + } + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) + { + perror("write"); + exit(EXIT_FAILURE); + } + if (nr < 0) { + fprintf(stderr,"%s: invalid length\n", filename); + rval = 1; + } +} + +static void +raw_args(char **argv) +{ + int fd; + + fd = fileno(stdin); + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fd = fileno(stdin); + else if (fflag) { + struct stat st; + fd = open(*argv, O_RDONLY|O_NONBLOCK, 0); + if (fd < 0) + goto skip; + + if (fstat(fd, &st) == -1) { + close(fd); + goto skip; + } + if (!S_ISREG(st.st_mode)) { + close(fd); + errno = EINVAL; + goto skipnomsg; + } + } + else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { +skip: + perror(*argv); +skipnomsg: + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + raw_cat(fd); + if (fd != fileno(stdin)) + close(fd); + } while (*argv); +} + +int +cat_main(int argc, char *argv[]) +{ + int ch; + struct flock stdout_lock; + + while ((ch = getopt(argc, argv, "beflnstv")) != -1) + switch (ch) { + case 'b': + bflag = nflag = 1; /* -b implies -n */ + break; + case 'e': + eflag = vflag = 1; /* -e implies -v */ + break; + case 'f': + fflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = vflag = 1; /* -t implies -v */ + break; + case 'v': + vflag = 1; + break; + default: + case '?': + fprintf(stderr, + "usage: cat [-beflnstv] [-] [file ...]\n"); + exit(EXIT_FAILURE); + } + argv += optind; + + if (lflag) { + stdout_lock.l_len = 0; + stdout_lock.l_start = 0; + stdout_lock.l_type = F_WRLCK; + stdout_lock.l_whence = SEEK_SET; + if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) + { + perror("fcntl"); + exit(EXIT_FAILURE); + } + } + + if (bflag || eflag || nflag || sflag || tflag || vflag) + cook_args(argv); + else + raw_args(argv); + if (fclose(stdout)) + { + perror("fclose"); + exit(EXIT_FAILURE); + } + exit(rval); +}
diff --git a/toolbox/chcon.c b/toolbox/chcon.c new file mode 100644 index 0000000..d594b9b --- /dev/null +++ b/toolbox/chcon.c
@@ -0,0 +1,25 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <selinux/selinux.h> + +int chcon_main(int argc, char **argv) +{ + int rc, i; + + if (argc < 3) { + fprintf(stderr, "usage: %s context path...\n", argv[0]); + exit(1); + } + + for (i = 2; i < argc; i++) { + rc = setfilecon(argv[i], argv[1]); + if (rc < 0) { + fprintf(stderr, "%s: Could not label %s with %s: %s\n", + argv[0], argv[i], argv[1], strerror(errno)); + exit(2); + } + } + exit(0); +}
diff --git a/toolbox/chmod.c b/toolbox/chmod.c new file mode 100644 index 0000000..460ccdd --- /dev/null +++ b/toolbox/chmod.c
@@ -0,0 +1,104 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#if HAVE_GLIBC +#include <limits.h> +#else +#include <sys/limits.h> +#endif +#include <sys/stat.h> + +#include <unistd.h> +#include <time.h> + +void recurse_chmod(char* path, int mode) +{ + struct dirent *dp; + DIR *dir = opendir(path); + if (dir == NULL) { + // not a directory, carry on + return; + } + char *subpath = malloc(sizeof(char)*PATH_MAX); + int pathlen = strlen(path); + + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) continue; + + if (strlen(dp->d_name) + pathlen + 2/*NUL and slash*/ > PATH_MAX) { + fprintf(stderr, "Invalid path specified: too long\n"); + exit(1); + } + + strcpy(subpath, path); + strcat(subpath, "/"); + strcat(subpath, dp->d_name); + + if (chmod(subpath, mode) < 0) { + fprintf(stderr, "Unable to chmod %s: %s\n", subpath, strerror(errno)); + exit(1); + } + + recurse_chmod(subpath, mode); + } + free(subpath); + closedir(dir); +} + +static int usage() +{ + fprintf(stderr, "Usage: chmod [OPTION] <MODE> <FILE>\n"); + fprintf(stderr, " -R, --recursive change files and directories recursively\n"); + fprintf(stderr, " --help display this help and exit\n"); + + return 10; +} + +int chmod_main(int argc, char **argv) +{ + int i; + + if (argc < 3 || strcmp(argv[1], "--help") == 0) { + return usage(); + } + + int recursive = (strcmp(argv[1], "-R") == 0 || + strcmp(argv[1], "--recursive") == 0) ? 1 : 0; + + if (recursive && argc < 4) { + return usage(); + } + + if (recursive) { + argc--; + argv++; + } + + int mode = 0; + const char* s = argv[1]; + while (*s) { + if (*s >= '0' && *s <= '7') { + mode = (mode<<3) | (*s-'0'); + } + else { + fprintf(stderr, "Bad mode\n"); + return 10; + } + s++; + } + + for (i = 2; i < argc; i++) { + if (chmod(argv[i], mode) < 0) { + fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno)); + return 10; + } + if (recursive) { + recurse_chmod(argv[i], mode); + } + } + return 0; +}
diff --git a/toolbox/chown.c b/toolbox/chown.c new file mode 100644 index 0000000..92efee6 --- /dev/null +++ b/toolbox/chown.c
@@ -0,0 +1,73 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> + +#include <unistd.h> +#include <time.h> + +int chown_main(int argc, char **argv) +{ + int i; + + if (argc < 3) { + fprintf(stderr, "Usage: chown <USER>[:GROUP] <FILE1> [FILE2] ...\n"); + return 10; + } + + // Copy argv[1] to 'user' so we can truncate it at the period + // if a group id specified. + char user[32]; + char *group = NULL; + strncpy(user, argv[1], sizeof(user)); + if ((group = strchr(user, ':')) != NULL) { + *group++ = '\0'; + } else if ((group = strchr(user, '.')) != NULL) { + *group++ = '\0'; + } + + // Lookup uid (and gid if specified) + struct passwd *pw; + struct group *grp = NULL; + uid_t uid; + gid_t gid = -1; // passing -1 to chown preserves current group + + pw = getpwnam(user); + if (pw != NULL) { + uid = pw->pw_uid; + } else { + char* endptr; + uid = (int) strtoul(user, &endptr, 0); + if (endptr == user) { // no conversion + fprintf(stderr, "No such user '%s'\n", user); + return 10; + } + } + + if (group != NULL) { + grp = getgrnam(group); + if (grp != NULL) { + gid = grp->gr_gid; + } else { + char* endptr; + gid = (int) strtoul(group, &endptr, 0); + if (endptr == group) { // no conversion + fprintf(stderr, "No such group '%s'\n", group); + return 10; + } + } + } + + for (i = 2; i < argc; i++) { + if (chown(argv[i], uid, gid) < 0) { + fprintf(stderr, "Unable to chown %s: %s\n", argv[i], strerror(errno)); + return 10; + } + } + + return 0; +}
diff --git a/toolbox/cmp.c b/toolbox/cmp.c new file mode 100644 index 0000000..80635ad --- /dev/null +++ b/toolbox/cmp.c
@@ -0,0 +1,91 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +int cmp_main(int argc, char *argv[]) +{ + int c; + int fd1, fd2; + char buf1[4096], buf2[4096]; + int res, res1, res2; + int rv = 0; + int i; + int filepos = 0; + + int show_byte = 0; + int show_all = 0; + int limit = 0; + + do { + c = getopt(argc, argv, "bln:"); + if (c == EOF) + break; + switch (c) { + case 'b': + show_byte = 1; + break; + case 'l': + show_all = 1; + break; + case 'n': + limit = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (optind + 2 != argc) { + fprintf(stderr, "Usage: %s [-b] [-l] [-n count] file1 file2\n", argv[0]); + exit(1); + } + + fd1 = open(argv[optind], O_RDONLY); + if(fd1 < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + + fd2 = open(argv[optind+1], O_RDONLY); + if(fd2 < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind+1], strerror(errno)); + return 1; + } + + while(1) { + res1 = read(fd1, &buf1, sizeof(buf1)); + res2 = read(fd2, &buf2, sizeof(buf2)); + res = res1 < res2 ? res1 : res2; + if(res1 == 0 && res2 == 0) { + return rv; + } + for(i = 0; i < res; i++) { + if(buf1[i] != buf2[i]) { + printf("%s %s differ byte %d", argv[optind], argv[optind+1], filepos + i); + if(show_byte) + printf(" 0x%02x 0x%02x", buf1[i], buf2[i]); + printf("\n"); + if(!show_all) + return 1; + rv = 1; + } + if(limit) { + limit--; + if(limit == 0) + return rv; + } + } + if(res1 != res2 || res < 0) { + printf("%s on %s\n", res < 0 ? "Read error" : "EOF", res1 < res2 ? argv[optind] : argv[optind+1]); + return 1; + } + filepos += res; + } +}
diff --git a/toolbox/date.c b/toolbox/date.c new file mode 100644 index 0000000..da4ef4a --- /dev/null +++ b/toolbox/date.c
@@ -0,0 +1,152 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <linux/android_alarm.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +static void usage() { + fprintf(stdout, "Usage: date [OPTION]\n\n"); + fprintf(stdout, " -u display time in UTC\n"); + fprintf(stdout, " -s set current time, for example,\n"); + fprintf(stdout, " date -s 20140704:120000\n"); + exit(1); +} + +static void settime(char *s) { + struct tm tm; + int day = atoi(s); + int hour; + time_t t; + int fd; + struct timespec ts; + + while (*s && *s != '.') + s++; + + if (*s) + s++; + + hour = atoi(s); + + tm.tm_year = day / 10000 - 1900; + tm.tm_mon = (day % 10000) / 100 - 1; + tm.tm_mday = (day % 100); + tm.tm_hour = hour / 10000; + tm.tm_min = (hour % 10000) / 100; + tm.tm_sec = (hour % 100); + tm.tm_isdst = -1; + + t = mktime(&tm); + + fd = open("/dev/alarm", O_RDWR); + if (fd != -1) { + ts.tv_sec = t; + ts.tv_nsec = 0; + ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + close(fd); + return; + } + + // /dev/alarm is unavailable, use settimeofday(). + struct timeval tv = { + .tv_sec = t + }; + if (settimeofday(&tv, NULL)) { + fprintf(stderr,"settimeofday failed %s\n", strerror(errno)); + } +} + +int date_main(int argc, char *argv[]) +{ + int c; + int res; + struct tm tm; + time_t t; + struct timeval tv; + struct timespec ts; + char strbuf[260]; + int fd; + + int useutc = 0; + + tzset(); + + do { + c = getopt(argc, argv, "us:"); + if (c == EOF) + break; + switch (c) { + case 'u': + useutc = 1; + break; + case 's': + settime(optarg); + break; + case '?': + usage(); + } + } while (1); + if(optind + 2 < argc) { + usage(); + } + + int hasfmt = argc == optind + 1 && argv[optind][0] == '+'; + if(optind == argc || hasfmt) { + char buf[2000]; + time(&t); + if (useutc) { + gmtime_r(&t, &tm); + strftime(strbuf, sizeof(strbuf), + (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S GMT %Y"), + &tm); + } else { + localtime_r(&t, &tm); + strftime(strbuf, sizeof(strbuf), + (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S %Z %Y"), + &tm); + } + printf("%s\n", strbuf); + } + else if(optind + 1 == argc) { +#if 0 + struct tm *tmptr; + tmptr = getdate(argv[optind]); + if(tmptr == NULL) { + fprintf(stderr,"getdate_r failed\n"); + return 1; + } + tm = *tmptr; +#if 0 + if(getdate_r(argv[optind], &tm) < 0) { + fprintf(stderr,"getdate_r failed %s\n", strerror(errno)); + return 1; + } +#endif +#endif + //strptime(argv[optind], NULL, &tm); + //tv.tv_sec = mktime(&tm); + //tv.tv_usec = 0; + strtotimeval(argv[optind], &tv); + printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec); + fd = open("/dev/alarm", O_RDWR); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + //res = settimeofday(&tv, NULL); + if(res < 0) { + fprintf(stderr,"settimeofday failed %s\n", strerror(errno)); + return 1; + } + } + else { + fprintf(stderr,"%s [-s 20070325.123456] [-u] [date]\n", argv[0]); + return 1; + } + + return 0; +}
diff --git a/toolbox/dd.c b/toolbox/dd.c new file mode 100644 index 0000000..ba29083 --- /dev/null +++ b/toolbox/dd.c
@@ -0,0 +1,1356 @@ +/* $NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" + +#define NO_CONV + +//#include "extern.h" +void block(void); +void block_close(void); +void dd_out(int); +void def(void); +void def_close(void); +void jcl(char **); +void pos_in(void); +void pos_out(void); +void summary(void); +void summaryx(int); +void terminate(int); +void unblock(void); +void unblock_close(void); +ssize_t bwrite(int, const void *, size_t); + +extern IO in, out; +extern STAT st; +extern void (*cfunc)(void); +extern uint64_t cpy_cnt; +extern uint64_t cbsz; +extern u_int ddflags; +extern u_int files_cnt; +extern int progress; +extern const u_char *ctab; +extern const u_char a2e_32V[], a2e_POSIX[]; +extern const u_char e2a_32V[], e2a_POSIX[]; +extern const u_char a2ibm_32V[], a2ibm_POSIX[]; +extern u_char casetab[]; + + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static void dd_close(void); +static void dd_in(void); +static void getfdtype(IO *); +static int redup_clean_fd(int); +static void setup(void); + + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc)(void); /* conversion function */ +uint64_t cpy_cnt; /* # of blocks to copy */ +static off_t pending = 0; /* pending seek if sparse */ +u_int ddflags; /* conversion options */ +uint64_t cbsz; /* conversion block size */ +u_int files_cnt = 1; /* # of files to copy */ +int progress = 0; /* display sign of life */ +const u_char *ctab; /* conversion table */ +sigset_t infoset; /* a set blocking SIGINFO */ + +int +dd_main(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + fprintf(stderr, "usage: dd [operand ...]\n"); + exit(1); + /* NOTREACHED */ + } + } + argc -= (optind - 1); + argv += (optind - 1); + + jcl(argv); + setup(); + +// (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + (void)sigemptyset(&infoset); +// (void)sigaddset(&infoset, SIGINFO); + + (void)atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); + /* NOTREACHED */ +} + +static void +setup(void) +{ + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd < 0) { + fprintf(stderr, "%s: cannot open for read: %s\n", + in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* Ensure in.fd is outside the stdio descriptor range */ + in.fd = redup_clean_fd(in.fd); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) { + fprintf(stderr, + "files is not supported for non-tape devices\n"); + exit(1); + /* NOTREACHED */ + } + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd < 0) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd < 0) { + fprintf(stderr, "%s: cannot open for write: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* Ensure out.fd is outside the stdio descriptor range */ + out.fd = redup_clean_fd(out.fd); + } + + getfdtype(&out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) { + exit(1); + /* NOTREACHED */ + } + out.db = in.db; + } else if ((in.db = + malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || + (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) { + exit(1); + /* NOTREACHED */ + } + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file; ignore errors because it fails on some + * kinds of output files, tapes, for example. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) + (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE|C_UCASE)) { +#ifdef NO_CONV + /* Should not get here, but just in case... */ + fprintf(stderr, "case conv and -DNO_CONV\n"); + exit(1); + /* NOTREACHED */ +#else /* NO_CONV */ + u_int cnt; + + if (ddflags & C_ASCII || ddflags & C_EBCDIC) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(cnt); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(cnt); + } + } + + ctab = casetab; +#endif /* NO_CONV */ + } + + (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */ +} + +static void +getfdtype(IO *io) +{ +// struct mtget mt; + struct stat sb; + + if (fstat(io->fd, &sb)) { + fprintf(stderr, "%s: cannot fstat: %s\n", + io->name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + if (S_ISCHR(sb.st_mode)) + io->flags |= /*ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; */ ISCHR; + else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ +} + +/* + * Move the parameter file descriptor to a descriptor that is outside the + * stdio descriptor range, if necessary. This is required to avoid + * accidentally outputting completion or error messages into the + * output file that were intended for the tty. + */ +static int +redup_clean_fd(int fd) +{ + int newfd; + + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && + fd != STDERR_FILENO) + /* File descriptor is ok, return immediately. */ + return fd; + + /* + * 3 is the first descriptor greater than STD*_FILENO. Any + * free descriptor valued 3 or above is acceptable... + */ + newfd = fcntl(fd, F_DUPFD, 3); + if (newfd < 0) { + fprintf(stderr, "dupfd IO: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + close(fd); + + return newfd; +} + +static void +dd_in(void) +{ + int flags; + int64_t n; + + for (flags = ddflags;;) { + if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) + return; + + /* + * Clear the buffer first if doing "sync" on input. + * If doing block operations use spaces. This will + * affect not only the C_NOERROR case, but also the + * last partial input block which should be padded + * with zero and not garbage. + */ + if (flags & C_SYNC) { + if (flags & (C_BLOCK|C_UNBLOCK)) + (void)memset(in.dbp, ' ', in.dbsz); + else + (void)memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n < 0) { + + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + fprintf(stderr, "%s: read error: %s\n", + in.name, strerror(errno)); + if (!(flags & C_NOERROR)) { + exit(1); + /* NOTREACHED */ + } + summary(); + + /* + * If it's not a tape drive or a pipe, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (!(in.flags & (ISPIPE|ISTAPE)) && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + fprintf(stderr, "%s: seek error: %s\n", + in.name, strerror(errno)); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if (n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + +/* if (ddflags & C_SWAB) { + if ((n = in.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, n); + } +*/ + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Cleanup any remaining I/O and flush output. If necesssary, output file + * is truncated. + */ +static void +dd_close(void) +{ + + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { + (void)memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + /* If there are pending sparse blocks, make sure + * to write out the final block un-sparse + */ + if ((out.dbcnt == 0) && pending) { + memset(out.db, 0, out.dbsz); + out.dbcnt = out.dbsz; + out.dbp = out.db + out.dbcnt; + pending -= out.dbsz; + } + if (out.dbcnt) + dd_out(1); + + /* + * Reporting nfs write error may be defered until next + * write(2) or close(2) system call. So, we need to do an + * extra check. If an output is stdout, the file structure + * may be shared among with other processes and close(2) just + * decreases the reference count. + */ + if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) { + fprintf(stderr, "fsync stdout: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } + if (close(out.fd) == -1) { + fprintf(stderr, "close: %s\n", strerror(errno)); + exit(1); + /* NOTREACHED */ + } +} + +void +dd_out(int force) +{ + static int warned; + int64_t cnt, n, nw; + u_char *outp; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + + if (!force && ddflags & C_SPARSE) { + int sparse, i; + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + if (sparse) { + pending += cnt; + outp += cnt; + nw = 0; + break; + } + } + if (pending != 0) { + if (lseek(out.fd, pending, SEEK_CUR) == + -1) { + fprintf(stderr, + "%s: seek error creating " + "sparse file: %s\n", + out.name, strerror(errno)); + exit(1); + } + } + nw = bwrite(out.fd, outp, cnt); + if (nw <= 0) { + if (nw == 0) { + fprintf(stderr, "%s: end of device\n", + out.name); + exit(1); + /* NOTREACHED */ + } + if (errno != EINTR) { + fprintf(stderr, "%s: write error: %s\n", + out.name, strerror(errno)); + /* NOTREACHED */ + exit(1); + } + nw = 0; + } + if (pending) { + st.bytes += pending; + st.sparse += pending/out.dbsz; + st.out_full += pending/out.dbsz; + pending = 0; + } + outp += nw; + st.bytes += nw; + if (nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if (nw == cnt) + break; + if (out.flags & ISCHR && !warned) { + warned = 1; + fprintf(stderr, "%s: short write on character " + "device\n", out.name); + } + if (out.flags & ISTAPE) { + fprintf(stderr, + "%s: short write on tape device", + out.name); + exit(1); + /* NOTREACHED */ + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; + + if (progress) + (void)write(STDERR_FILENO, ".", 1); +} + +/* + * A protected against SIGINFO write + */ +ssize_t +bwrite(int fd, const void *buf, size_t len) +{ + sigset_t oset; + ssize_t rv; + int oerrno; + + (void)sigprocmask(SIG_BLOCK, &infoset, &oset); + rv = write(fd, buf, len); + oerrno = errno; + (void)sigprocmask(SIG_SETMASK, &oset, NULL); + errno = oerrno; + return (rv); +} + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in(void) +{ + int bcnt, cnt, nr, warned; + + /* If not a pipe or tape device, try to seek on it. */ + if (!(in.flags & (ISPIPE|ISTAPE))) { + if (lseek(in.fd, + (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) { + fprintf(stderr, "%s: seek error: %s", + in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + return; + /* NOTREACHED */ + } + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + fprintf(stderr, "skip reached end of input\n"); + exit(1); + /* NOTREACHED */ + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + + fprintf(stderr, "%s: error occurred\n", + in.name); + warned = 1; + summary(); + } + continue; + } + fprintf(stderr, "%s: read error: %s", in.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } +} + +void +pos_out(void) +{ +// struct mtop t_op; + int cnt, n; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (!(out.flags & ISTAPE)) { + if (lseek(out.fd, + (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) { + fprintf(stderr, "%s: seek error: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + return; + } + + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { +/* t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)*/ + fprintf(stderr, "%s: cannot read", out.name); + exit(1); + /* NOTREACHED */ + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n < 0) { + fprintf(stderr, "%s: cannot position by reading: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ +/* t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) */ { + fprintf(stderr, "%s: cannot position\n", out.name); + exit(1); + /* NOTREACHED */ + } + + while (cnt++ < out.offset) + if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) { + fprintf(stderr, "%s: cannot position " + "by writing: %s\n", + out.name, strerror(errno)); + exit(1); + /* NOTREACHED */ + } + break; + } +} + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def(void) +{ + uint64_t cnt; + u_char *inp; + const u_char *t; + + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close(void) +{ + + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +#ifdef NO_CONV +/* Build a smaller version (i.e. for a miniroot) */ +/* These can not be called, but just in case... */ +static const char no_block[] = "unblock and -DNO_CONV?\n"; +void block(void) { fprintf(stderr, "%s", no_block + 2); exit(1); } +void block_close(void) { fprintf(stderr, "%s", no_block + 2); exit(1); } +void unblock(void) { fprintf(stderr, "%s", no_block); exit(1); } +void unblock_close(void) { fprintf(stderr, "%s", no_block); exit(1); } +#else /* NO_CONV */ + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block(void) +{ + static int intrunc; + int ch = 0; /* pacify gcc */ + uint64_t cnt, maxlen; + u_char *inp, *outp; + const u_char *t; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; + cnt && *inp++ != '\n'; --cnt); + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close(void) +{ + + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, + ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock(void) +{ + uint64_t cnt; + u_char *inp; + const u_char *t; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--) + *inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); + if (t >= inp) { + cnt = t - inp + 1; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + if (out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close(void) +{ + uint64_t cnt; + u_char *t; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); + if (t >= in.db) { + cnt = t - in.db + 1; + (void)memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} + +#endif /* NO_CONV */ + +#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000) + +void +summary(void) +{ + char buf[100]; + int64_t mS; + struct timeval tv; + + if (progress) + (void)write(STDERR_FILENO, "\n", 1); + + (void)gettimeofday(&tv, NULL); + mS = tv2mS(tv) - tv2mS(st.start); + if (mS == 0) + mS = 1; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%llu+%llu records in\n%llu+%llu records out\n", + (unsigned long long)st.in_full, (unsigned long long)st.in_part, + (unsigned long long)st.out_full, (unsigned long long)st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n", + (unsigned long long)st.swab, + (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n", + (unsigned long long)st.trunc, + (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.sparse) { + (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n", + (unsigned long long)st.sparse, + (st.sparse == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n", + (unsigned long long) st.bytes, + (long) (mS / 1000), + (int) (mS % 1000), + (unsigned long long) (st.bytes * 1000LL / mS)); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +void +terminate(int notused) +{ + + exit(0); + /* NOTREACHED */ +} + +static int c_arg(const void *, const void *); +#ifndef NO_CONV +static int c_conv(const void *, const void *); +#endif +static void f_bs(char *); +static void f_cbs(char *); +static void f_conv(char *); +static void f_count(char *); +static void f_files(char *); +static void f_ibs(char *); +static void f_if(char *); +static void f_obs(char *); +static void f_of(char *); +static void f_seek(char *); +static void f_skip(char *); +static void f_progress(char *); + +static const struct arg { + const char *name; + void (*f)(char *); + u_int set, noset; +} args[] = { + /* the array needs to be sorted by the first column so + bsearch() can be used to find commands quickly */ + { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, + { "cbs", f_cbs, C_CBS, C_CBS }, + { "conv", f_conv, 0, 0 }, + { "count", f_count, C_COUNT, C_COUNT }, + { "files", f_files, C_FILES, C_FILES }, + { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, + { "if", f_if, C_IF, C_IF }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "progress", f_progress, 0, 0 }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(char **argv) +{ + struct arg *ap, tmp; + char *oper, *arg; + + in.dbsz = out.dbsz = 512; + + while ((oper = *++argv) != NULL) { + if ((arg = strchr(oper, '=')) == NULL) { + fprintf(stderr, "unknown operand %s\n", oper); + exit(1); + /* NOTREACHED */ + } + *arg++ = '\0'; + if (!*arg) { + fprintf(stderr, "no value specified for %s\n", oper); + exit(1); + /* NOTREACHED */ + } + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) { + fprintf(stderr, "unknown operand %s\n", tmp.name); + exit(1); + /* NOTREACHED */ + } + if (ddflags & ap->noset) { + fprintf(stderr, + "%s: illegal argument combination or already set\n", + tmp.name); + exit(1); + /* NOTREACHED */ + } + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | + C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) { + ddflags &= ~C_BS; + ddflags |= C_IBS|C_OBS; + } + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) + fprintf(stderr, "bs supersedes ibs and obs\n"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK|C_UNBLOCK)) { + if (!(ddflags & C_CBS)) { + fprintf(stderr, "record operations require cbs\n"); + exit(1); + /* NOTREACHED */ + } + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII|C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else { + fprintf(stderr, + "cbs meaningless if not doing record operations\n"); + exit(1); + /* NOTREACHED */ + } + } else + cfunc = def; + + /* Read, write and seek calls take off_t as arguments. + * + * The following check is not done because an off_t is a quad + * for current NetBSD implementations. + * + * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz) + * errx(1, "seek offsets cannot be larger than %d", INT_MAX); + */ +} + +static int +c_arg(const void *a, const void *b) +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max) +{ + long long result; + + if (sscanf(arg, "%lld", &result) == 0) + result = def; + return result; +} + +static void +f_bs(char *arg) +{ + + in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX); +} + +static void +f_cbs(char *arg) +{ + + cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX); +} + +static void +f_count(char *arg) +{ + + cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX); + if (!cpy_cnt) + terminate(0); +} + +static void +f_files(char *arg) +{ + + files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX); + if (!files_cnt) + terminate(0); +} + +static void +f_ibs(char *arg) +{ + + if (!(ddflags & C_BS)) + in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX); +} + +static void +f_if(char *arg) +{ + + in.name = arg; +} + +static void +f_obs(char *arg) +{ + + if (!(ddflags & C_BS)) + out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX); +} + +static void +f_of(char *arg) +{ + + out.name = arg; +} + +static void +f_seek(char *arg) +{ + + out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX); +} + +static void +f_skip(char *arg) +{ + + in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX); +} + +static void +f_progress(char *arg) +{ + + if (*arg != '0') + progress = 1; +} + +#ifdef NO_CONV +/* Build a small version (i.e. for a ramdisk root) */ +static void +f_conv(char *arg) +{ + + fprintf(stderr, "conv option disabled\n"); + exit(1); + /* NOTREACHED */ +} +#else /* NO_CONV */ + +static const struct conv { + const char *name; + u_int set, noset; + const u_char *ctab; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "sparse", C_SPARSE, 0, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, + /* If you add items to this table, be sure to add the + * conversions to the C_BS check in the jcl routine above. + */ +}; + +static void +f_conv(char *arg) +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + if (!(cp = (struct conv *)bsearch(&tmp, clist, + sizeof(clist)/sizeof(struct conv), sizeof(struct conv), + c_conv))) { + errx(EXIT_FAILURE, "unknown conversion %s", tmp.name); + /* NOTREACHED */ + } + if (ddflags & cp->noset) { + errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name); + /* NOTREACHED */ + } + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(const void *a, const void *b) +{ + + return (strcmp(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +#endif /* NO_CONV */
diff --git a/toolbox/dd.h b/toolbox/dd.h new file mode 100644 index 0000000..cca1024 --- /dev/null +++ b/toolbox/dd.h
@@ -0,0 +1,93 @@ +/* $NetBSD: dd.h,v 1.12 2004/01/17 20:48:57 dbj Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + */ + +#include <stdint.h> + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + uint64_t dbcnt; /* current buffer byte count */ + int64_t dbrcnt; /* last read byte count */ + uint64_t dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe (not truncatable) */ +#define ISTAPE 0x04 /* tape (not seekable) */ +#define NOREAD 0x08 /* not readable */ + u_int flags; + + const char *name; /* name */ + int fd; /* file descriptor */ + uint64_t offset; /* # of blocks to skip */ +} IO; + +typedef struct { + uint64_t in_full; /* # of full input blocks */ + uint64_t in_part; /* # of partial input blocks */ + uint64_t out_full; /* # of full output blocks */ + uint64_t out_part; /* # of partial output blocks */ + uint64_t trunc; /* # of truncated records */ + uint64_t swab; /* # of odd-length swab blocks */ + uint64_t sparse; /* # of sparse output blocks */ + uint64_t bytes; /* # of bytes written */ + struct timeval start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 +#define C_OSYNC 0x100000 +#define C_SPARSE 0x200000
diff --git a/toolbox/devmem.c b/toolbox/devmem.c new file mode 100644 index 0000000..9f3780b --- /dev/null +++ b/toolbox/devmem.c
@@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 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 <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +static int usage() { + fprintf(stdout, "Usage: devmem ADDRESS [WIDTH [VALUE]]\n"); + fprintf(stdout, " read from or write to device memory.\n"); + fprintf(stdout, "\n"); + fprintf(stdout, " ADDRESS -- address of device memory\n"); + fprintf(stdout, " WIDTH -- 8, 16, or 32(default). required for write\n"); + fprintf(stdout, " VALUE -- value to write. read if none is specified\n"); + return -1; +} + +int devmem_main(int argc, char *argv[]) +{ + if(argc < 2 || argc > 4) { + return usage(); + } + + int is_write = (argc == 4); + unsigned long addr = 0, width = 32, value = 0; + addr = strtoul(argv[1], NULL, 0); + if (argc > 2) { + width = strtoul(argv[2], NULL, 0); + if (argc > 3) { + value = strtoul(argv[3], NULL, 0); + } + } + + if (width != 8 && width != 16 && width != 32) { + fprintf(stderr, "width has to be 8, 16 or 32\n"); + return usage(); + } + + if (addr % (width >> 3)) { + fprintf(stderr, "address 0x%lx is not %ldbit-aligned\n", addr, width); + return usage(); + } + + int fd = open("/dev/mem", is_write ? (O_RDWR | O_SYNC) : + (O_RDONLY | O_SYNC)); + if (fd == -1) { + fprintf(stderr, "cannot open /dev/mem, error %s\n", strerror(errno)); + return -1; + } + + int page_size = getpagesize(); + void *base = mmap(NULL, page_size, + is_write ? (PROT_READ | PROT_WRITE) : PROT_READ, + MAP_SHARED, fd, addr & ~(page_size - 1)); + if (base == MAP_FAILED) { + fprintf(stderr, "mmap failed, error %s\n", strerror(errno)); + close(fd); + return -1; + } + + void *virt = (char*)base + (addr & (page_size - 1)); + if (is_write) { + switch (width) { + case 8: + *(uint8_t*)virt = (uint8_t)value; + break; + case 16: + *(uint16_t*)virt = (uint16_t)value; + break; + case 32: + *(uint32_t*)virt = (uint32_t)value; + break; + default: + fprintf(stderr, "not reached\n"); + break; + } + } else { + switch (width) { + case 8: + fprintf(stdout, "0x%02"PRIX8"\n", *(uint8_t*)virt); + break; + case 16: + fprintf(stdout, "0x%04"PRIX16"\n", *(uint16_t*)virt); + break; + case 32: + fprintf(stdout, "0x%08"PRIX32"\n", *(uint32_t*)virt); + break; + default: + fprintf(stderr, "not reached\n"); + break; + } + } + munmap(base, page_size); + close(fd); + return 0; +}
diff --git a/toolbox/df.c b/toolbox/df.c new file mode 100644 index 0000000..63940a1 --- /dev/null +++ b/toolbox/df.c
@@ -0,0 +1,79 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/statfs.h> + +static int ok = EXIT_SUCCESS; + +static void printsize(long long n) +{ + char unit = 'K'; + n /= 1024; + if (n > 1024) { + n /= 1024; + unit = 'M'; + } + if (n > 1024) { + n /= 1024; + unit = 'G'; + } + printf("%4lld%c", n, unit); +} + +static void df(char *s, int always) { + struct statfs st; + + if (statfs(s, &st) < 0) { + fprintf(stderr, "%s: %s\n", s, strerror(errno)); + ok = EXIT_FAILURE; + } else { + if (st.f_blocks == 0 && !always) + return; + printf("%-20s ", s); + printsize((long long)st.f_blocks * (long long)st.f_bsize); + printf(" "); + printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize); + printf(" "); + printsize((long long)st.f_bfree * (long long)st.f_bsize); + printf(" %d\n", (int) st.f_bsize); + } +} + +int df_main(int argc, char *argv[]) { + printf("Filesystem Size Used Free Blksize\n"); + if (argc == 1) { + char s[2000]; + FILE *f = fopen("/proc/mounts", "r"); + + while (fgets(s, 2000, f)) { + char *c, *e = s; + + for (c = s; *c; c++) { + if (*c == ' ') { + e = c + 1; + break; + } + } + + for (c = e; *c; c++) { + if (*c == ' ') { + *c = '\0'; + break; + } + } + + df(e, 0); + } + + fclose(f); + } else { + int i; + + for (i = 1; i < argc; i++) { + df(argv[i], 1); + } + } + + exit(ok); +}
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c new file mode 100644 index 0000000..cf29c6c --- /dev/null +++ b/toolbox/dmesg.c
@@ -0,0 +1,49 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <sys/klog.h> +#include <string.h> + +#if HAVE_GLIBC +/* GoogleTV: these constants are missing from glibc's sys/klog.h */ +#define KLOG_READ_ALL 3 +#define KLOG_READ_CLEAR 4 +#endif + +#define KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */ +#define KLOG_BUF_LEN (1 << KLOG_BUF_SHIFT) + +int dmesg_main(int argc, char **argv) +{ + char buffer[KLOG_BUF_LEN + 1]; + char *p = buffer; + ssize_t ret; + int n, op; + + if((argc == 2) && (!strcmp(argv[1],"-c"))) { + op = KLOG_READ_CLEAR; + } else { + op = KLOG_READ_ALL; + } + + n = klogctl(op, buffer, KLOG_BUF_LEN); + if (n < 0) { + perror("klogctl"); + return EXIT_FAILURE; + } + buffer[n] = '\0'; + + while((ret = write(STDOUT_FILENO, p, n))) { + if (ret == -1) { + if (errno == EINTR) + continue; + perror("write"); + return EXIT_FAILURE; + } + p += ret; + n -= ret; + } + + return 0; +}
diff --git a/toolbox/dynarray.c b/toolbox/dynarray.c new file mode 100644 index 0000000..e9b7b03 --- /dev/null +++ b/toolbox/dynarray.c
@@ -0,0 +1,103 @@ +#include "dynarray.h" +#include <stdlib.h> +#include <limits.h> + +void +dynarray_init( dynarray_t *a ) +{ + a->count = a->capacity = 0; + a->items = NULL; +} + + +static void +dynarray_reserve_more( dynarray_t *a, int count ) +{ + int old_cap = a->capacity; + int new_cap = old_cap; + const int max_cap = INT_MAX/sizeof(void*); + void** new_items; + int new_count = a->count + count; + + if (count <= 0) + return; + + if (count > max_cap - a->count) + abort(); + + new_count = a->count + count; + + while (new_cap < new_count) { + old_cap = new_cap; + new_cap += (new_cap >> 2) + 4; + if (new_cap < old_cap || new_cap > max_cap) { + new_cap = max_cap; + } + } + new_items = realloc(a->items, new_cap*sizeof(void*)); + if (new_items == NULL) + abort(); + + a->items = new_items; + a->capacity = new_cap; +} + +void +dynarray_append( dynarray_t *a, void* item ) +{ + if (a->count >= a->capacity) + dynarray_reserve_more(a, 1); + + a->items[a->count++] = item; +} + +void +dynarray_done( dynarray_t *a ) +{ + free(a->items); + a->items = NULL; + a->count = a->capacity = 0; +} + +// string arrays + +void strlist_init( strlist_t *list ) +{ + dynarray_init(list); +} + +void strlist_append_b( strlist_t *list, const void* str, size_t slen ) +{ + char *copy = malloc(slen+1); + memcpy(copy, str, slen); + copy[slen] = '\0'; + dynarray_append(list, copy); +} + +void strlist_append_dup( strlist_t *list, const char *str) +{ + strlist_append_b(list, str, strlen(str)); +} + +void strlist_done( strlist_t *list ) +{ + STRLIST_FOREACH(list, string, free(string)); + dynarray_done(list); +} + +static int strlist_compare_strings(const void* a, const void* b) +{ + const char *sa = *(const char **)a; + const char *sb = *(const char **)b; + return strcmp(sa, sb); +} + +void strlist_sort( strlist_t *list ) +{ + if (list->count > 0) { + qsort(list->items, + (size_t)list->count, + sizeof(void*), + strlist_compare_strings); + } +}
diff --git a/toolbox/dynarray.h b/toolbox/dynarray.h new file mode 100644 index 0000000..f73fb3b --- /dev/null +++ b/toolbox/dynarray.h
@@ -0,0 +1,80 @@ +#ifndef DYNARRAY_H +#define DYNARRAY_H + +#include <stddef.h> + +/* simple dynamic array of pointers */ +typedef struct { + int count; + int capacity; + void** items; +} dynarray_t; + +#define DYNARRAY_INITIALIZER { 0, 0, NULL } + +void dynarray_init( dynarray_t *a ); +void dynarray_done( dynarray_t *a ); + +void dynarray_append( dynarray_t *a, void* item ); + +/* Used to iterate over a dynarray_t + * _array :: pointer to the array + * _item_type :: type of objects pointed to by the array + * _item :: name of a local variable defined within the loop + * with type '_item_type' + * _stmnt :: C statement that will be executed in each iteration. + * + * You case use 'break' and 'continue' within _stmnt + * + * This macro is only intended for simple uses. I.e. do not add or + * remove items from the array during iteration. + */ +#define DYNARRAY_FOREACH_TYPE(_array,_item_type,_item,_stmnt) \ + do { \ + int _nn_##__LINE__ = 0; \ + for (;_nn_##__LINE__ < (_array)->count; ++ _nn_##__LINE__) { \ + _item_type _item = (_item_type)(_array)->items[_nn_##__LINE__]; \ + _stmnt; \ + } \ + } while (0) + +#define DYNARRAY_FOREACH(_array,_item,_stmnt) \ + DYNARRAY_FOREACH_TYPE(_array,void *,_item,_stmnt) + +/* Simple dynamic string arrays + * + * NOTE: A strlist_t owns the strings it references. + */ +typedef dynarray_t strlist_t; + +#define STRLIST_INITIALIZER DYNARRAY_INITIALIZER + +/* Used to iterate over a strlist_t + * _list :: pointer to strlist_t object + * _string :: name of local variable name defined within the loop with + * type 'char*' + * _stmnt :: C statement executed in each iteration + * + * This macro is only intended for simple uses. Do not add or remove items + * to/from the list during iteration. + */ +#define STRLIST_FOREACH(_list,_string,_stmnt) \ + DYNARRAY_FOREACH_TYPE(_list,char *,_string,_stmnt) + +void strlist_init( strlist_t *list ); + +/* note: strlist_done will free all the strings owned by the list */ +void strlist_done( strlist_t *list ); + +/* append a new string made of the first 'slen' characters from 'str' + * followed by a trailing zero. + */ +void strlist_append_b( strlist_t *list, const void* str, size_t slen ); + +/* append the copy of a given input string to a strlist_t */ +void strlist_append_dup( strlist_t *list, const char *str); + +/* sort the strings in a given list (using strcmp) */ +void strlist_sort( strlist_t *list ); + +#endif /* DYNARRAY_H */ \ No newline at end of file
diff --git a/toolbox/exists.c b/toolbox/exists.c new file mode 100644 index 0000000..e348668 --- /dev/null +++ b/toolbox/exists.c
@@ -0,0 +1,16 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +int exists_main(int argc, char *argv[]) +{ + struct stat s; + + if(argc < 2) return 1; + + if(stat(argv[1], &s)) { + return 1; + } else { + return 0; + } +}
diff --git a/toolbox/getenforce.c b/toolbox/getenforce.c new file mode 100644 index 0000000..9e7589a --- /dev/null +++ b/toolbox/getenforce.c
@@ -0,0 +1,30 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <selinux/selinux.h> + +int getenforce_main(int argc, char **argv) +{ + int rc; + + rc = is_selinux_enabled(); + if (rc <= 0) { + printf("Disabled\n"); + return 0; + } + + rc = security_getenforce(); + if (rc < 0) { + fprintf(stderr, "Could not get enforcing status: %s\n", + strerror(errno)); + return 2; + } + + if (rc) + printf("Enforcing\n"); + else + printf("Permissive\n"); + + return 0; +}
diff --git a/toolbox/getevent.c b/toolbox/getevent.c new file mode 100644 index 0000000..07b1af9 --- /dev/null +++ b/toolbox/getevent.c
@@ -0,0 +1,671 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#if HAVE_GLIBC +#include <limits.h> +#else +#include <sys/limits.h> +#endif +#include <sys/poll.h> +#include <linux/input.h> +#include <errno.h> + +#include "getevent.h" + +static struct pollfd *ufds; +static char **device_names; +static int nfds; + +enum { + PRINT_DEVICE_ERRORS = 1U << 0, + PRINT_DEVICE = 1U << 1, + PRINT_DEVICE_NAME = 1U << 2, + PRINT_DEVICE_INFO = 1U << 3, + PRINT_VERSION = 1U << 4, + PRINT_POSSIBLE_EVENTS = 1U << 5, + PRINT_INPUT_PROPS = 1U << 6, + PRINT_HID_DESCRIPTOR = 1U << 7, + + PRINT_ALL_INFO = (1U << 8) - 1, + + PRINT_LABELS = 1U << 16, +}; + +static const char *get_label(const struct label *labels, int value) +{ + while(labels->name && value != labels->value) { + labels++; + } + return labels->name; +} + +static int print_input_props(int fd) +{ + uint8_t bits[INPUT_PROP_CNT / 8]; + int i, j; + int res; + int count; + const char *bit_label; + + printf(" input props:\n"); + res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits); + if(res < 0) { + printf(" <not available\n"); + return 1; + } + count = 0; + for(i = 0; i < res; i++) { + for(j = 0; j < 8; j++) { + if (bits[i] & 1 << j) { + bit_label = get_label(input_prop_labels, i * 8 + j); + if(bit_label) + printf(" %s\n", bit_label); + else + printf(" %04x\n", i * 8 + j); + count++; + } + } + } + if (!count) + printf(" <none>\n"); + return 0; +} + +static int print_possible_events(int fd, int print_flags) +{ + uint8_t *bits = NULL; + ssize_t bits_size = 0; + const char* label; + int i, j, k; + int res, res2; + struct label* bit_labels; + const char *bit_label; + + printf(" events:\n"); + for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes + int count = 0; + while(1) { + res = ioctl(fd, EVIOCGBIT(i, bits_size), bits); + if(res < bits_size) + break; + bits_size = res + 16; + bits = realloc(bits, bits_size * 2); + if(bits == NULL) { + fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size); + return 1; + } + } + res2 = 0; + switch(i) { + case EV_KEY: + res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size); + label = "KEY"; + bit_labels = key_labels; + break; + case EV_REL: + label = "REL"; + bit_labels = rel_labels; + break; + case EV_ABS: + label = "ABS"; + bit_labels = abs_labels; + break; + case EV_MSC: + label = "MSC"; + bit_labels = msc_labels; + break; + case EV_LED: + res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size); + label = "LED"; + bit_labels = led_labels; + break; + case EV_SND: + res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size); + label = "SND"; + bit_labels = snd_labels; + break; + case EV_SW: + res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size); + label = "SW "; + bit_labels = sw_labels; + break; + case EV_REP: + label = "REP"; + bit_labels = rep_labels; + break; + case EV_FF: + label = "FF "; + bit_labels = ff_labels; + break; + case EV_PWR: + label = "PWR"; + bit_labels = NULL; + break; + case EV_FF_STATUS: + label = "FFS"; + bit_labels = ff_status_labels; + break; + default: + res2 = 0; + label = "???"; + bit_labels = NULL; + } + for(j = 0; j < res; j++) { + for(k = 0; k < 8; k++) + if(bits[j] & 1 << k) { + char down; + if(j < res2 && (bits[j + bits_size] & 1 << k)) + down = '*'; + else + down = ' '; + if(count == 0) + printf(" %s (%04x):", label, i); + else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS) + printf("\n "); + if(bit_labels && (print_flags & PRINT_LABELS)) { + bit_label = get_label(bit_labels, j * 8 + k); + if(bit_label) + printf(" %.20s%c%*s", bit_label, down, 20 - strlen(bit_label), ""); + else + printf(" %04x%c ", j * 8 + k, down); + } else { + printf(" %04x%c", j * 8 + k, down); + } + if(i == EV_ABS) { + struct input_absinfo abs; + if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) { + printf(" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d", + abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat, + abs.resolution); + } + } + count++; + } + } + if(count) + printf("\n"); + } + free(bits); + return 0; +} + +static void print_event(int type, int code, int value, int print_flags) +{ + const char *type_label, *code_label, *value_label; + + if (print_flags & PRINT_LABELS) { + type_label = get_label(ev_labels, type); + code_label = NULL; + value_label = NULL; + + switch(type) { + case EV_SYN: + code_label = get_label(syn_labels, code); + break; + case EV_KEY: + code_label = get_label(key_labels, code); + value_label = get_label(key_value_labels, value); + break; + case EV_REL: + code_label = get_label(rel_labels, code); + break; + case EV_ABS: + code_label = get_label(abs_labels, code); + switch(code) { + case ABS_MT_TOOL_TYPE: + value_label = get_label(mt_tool_labels, value); + } + break; + case EV_MSC: + code_label = get_label(msc_labels, code); + break; + case EV_LED: + code_label = get_label(led_labels, code); + break; + case EV_SND: + code_label = get_label(snd_labels, code); + break; + case EV_SW: + code_label = get_label(sw_labels, code); + break; + case EV_REP: + code_label = get_label(rep_labels, code); + break; + case EV_FF: + code_label = get_label(ff_labels, code); + break; + case EV_FF_STATUS: + code_label = get_label(ff_status_labels, code); + break; + } + + if (type_label) + printf("%-12.12s", type_label); + else + printf("%04x ", type); + if (code_label) + printf(" %-20.20s", code_label); + else + printf(" %04x ", code); + if (value_label) + printf(" %-20.20s", value_label); + else + printf(" %08x ", value); + } else { + printf("%04x %04x %08x", type, code, value); + } +} + +static void print_hid_descriptor(int bus, int vendor, int product) +{ + const char *dirname = "/sys/kernel/debug/hid"; + char prefix[16]; + DIR *dir; + struct dirent *de; + char filename[PATH_MAX]; + FILE *file; + char line[2048]; + + snprintf(prefix, sizeof(prefix), "%04X:%04X:%04X.", bus, vendor, product); + + dir = opendir(dirname); + if(dir == NULL) + return; + while((de = readdir(dir))) { + if (strstr(de->d_name, prefix) == de->d_name) { + snprintf(filename, sizeof(filename), "%s/%s/rdesc", dirname, de->d_name); + + file = fopen(filename, "r"); + if (file) { + printf(" HID descriptor: %s\n\n", de->d_name); + while (fgets(line, sizeof(line), file)) { + fputs(" ", stdout); + fputs(line, stdout); + } + fclose(file); + puts(""); + } + } + } + closedir(dir); +} + +static int open_device(const char *device, int print_flags) +{ + int version; + int fd; + struct pollfd *new_ufds; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if(fd < 0) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not open %s, %s\n", device, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno)); + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno)); + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); + idstr[0] = '\0'; + } + + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); + if(new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); + if(new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + + if(print_flags & PRINT_DEVICE) + printf("add device %d: %s\n", nfds, device); + if(print_flags & PRINT_DEVICE_INFO) + printf(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + id.bustype, id.vendor, id.product, id.version); + if(print_flags & PRINT_DEVICE_NAME) + printf(" name: \"%s\"\n", name); + if(print_flags & PRINT_DEVICE_INFO) + printf(" location: \"%s\"\n" + " id: \"%s\"\n", location, idstr); + if(print_flags & PRINT_VERSION) + printf(" version: %d.%d.%d\n", + version >> 16, (version >> 8) & 0xff, version & 0xff); + + if(print_flags & PRINT_POSSIBLE_EVENTS) { + print_possible_events(fd, print_flags); + } + + if(print_flags & PRINT_INPUT_PROPS) { + print_input_props(fd); + } + if(print_flags & PRINT_HID_DESCRIPTOR) { + print_hid_descriptor(id.bustype, id.vendor, id.product); + } + + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char *device, int print_flags) +{ + int i; + for(i = 1; i < nfds; i++) { + if(strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + if(print_flags & PRINT_DEVICE) + printf("remove device %d: %s\n", i, device); + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + if(print_flags & PRINT_DEVICE_ERRORS) + fprintf(stderr, "remote device: %s not found\n", device); + return -1; +} + +static int read_notify(const char *dirname, int nfd, int print_flags) +{ + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname, print_flags); + } + else { + close_device(devname, print_flags); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char *dirname, int print_flags) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname, print_flags); + } + closedir(dir); + return 0; +} + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]); + fprintf(stderr, " -t: show time stamps\n"); + fprintf(stderr, " -n: don't print newlines\n"); + fprintf(stderr, " -s: print switch states for given bits\n"); + fprintf(stderr, " -S: print all switch states\n"); + fprintf(stderr, " -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\n"); + fprintf(stderr, " -d: show HID descriptor, if available\n"); + fprintf(stderr, " -p: show possible events (errs, dev, name, pos. events)\n"); + fprintf(stderr, " -i: show all device info and possible events\n"); + fprintf(stderr, " -l: label event types and names in plain text\n"); + fprintf(stderr, " -q: quiet (clear verbosity mask)\n"); + fprintf(stderr, " -c: print given number of events then exit\n"); + fprintf(stderr, " -r: print rate events are received\n"); +} + +int getevent_main(int argc, char *argv[]) +{ + int c; + int i; + int res; + int pollres; + int get_time = 0; + int print_device = 0; + char *newline = "\n"; + uint16_t get_switch = 0; + struct input_event event; + int version; + int print_flags = 0; + int print_flags_set = 0; + int dont_block = -1; + int event_count = 0; + int sync_rate = 0; + int64_t last_sync_time = 0; + const char *device = NULL; + const char *device_path = "/dev/input"; + + opterr = 0; + do { + c = getopt(argc, argv, "tns:Sv::dpilqc:rh"); + if (c == EOF) + break; + switch (c) { + case 't': + get_time = 1; + break; + case 'n': + newline = ""; + break; + case 's': + get_switch = strtoul(optarg, NULL, 0); + if(dont_block == -1) + dont_block = 1; + break; + case 'S': + get_switch = ~0; + if(dont_block == -1) + dont_block = 1; + break; + case 'v': + if(optarg) + print_flags |= strtoul(optarg, NULL, 0); + else + print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION; + print_flags_set = 1; + break; + case 'd': + print_flags |= PRINT_HID_DESCRIPTOR; + break; + case 'p': + print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE + | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS; + print_flags_set = 1; + if(dont_block == -1) + dont_block = 1; + break; + case 'i': + print_flags |= PRINT_ALL_INFO; + print_flags_set = 1; + if(dont_block == -1) + dont_block = 1; + break; + case 'l': + print_flags |= PRINT_LABELS; + break; + case 'q': + print_flags_set = 1; + break; + case 'c': + event_count = atoi(optarg); + dont_block = 0; + break; + case 'r': + sync_rate = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + case 'h': + usage(argc, argv); + exit(1); + } + } while (1); + if(dont_block == -1) + dont_block = 0; + + if (optind + 1 == argc) { + device = argv[optind]; + optind++; + } + if (optind != argc) { + usage(argc, argv); + exit(1); + } + nfds = 1; + ufds = calloc(1, sizeof(ufds[0])); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + if(device) { + if(!print_flags_set) + print_flags |= PRINT_DEVICE_ERRORS; + res = open_device(device, print_flags); + if(res < 0) { + return 1; + } + } else { + if(!print_flags_set) + print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME; + print_device = 1; + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno)); + return 1; + } + res = scan_dir(device_path, print_flags); + if(res < 0) { + fprintf(stderr, "scan dir failed for %s\n", device_path); + return 1; + } + } + + if(get_switch) { + for(i = 1; i < nfds; i++) { + uint16_t sw; + res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw); + if(res < 0) { + fprintf(stderr, "could not get switch state, %s\n", strerror(errno)); + return 1; + } + sw &= get_switch; + printf("%04x%s", sw, newline); + } + } + + if(dont_block) + return 0; + + while(1) { + pollres = poll(ufds, nfds, -1); + //printf("poll %d, returned %d\n", nfds, pollres); + if(ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd, print_flags); + } + for(i = 1; i < nfds; i++) { + if(ufds[i].revents) { + if(ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, &event, sizeof(event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + return 1; + } + if(get_time) { + printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec); + } + if(print_device) + printf("%s: ", device_names[i]); + print_event(event.type, event.code, event.value, print_flags); + if(sync_rate && event.type == 0 && event.code == 0) { + int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec; + if(last_sync_time) + printf(" rate %lld", 1000000LL / (now - last_sync_time)); + last_sync_time = now; + } + printf("%s", newline); + if(event_count && --event_count == 0) + return 0; + } + } + } + } + + return 0; +}
diff --git a/toolbox/getevent.h b/toolbox/getevent.h new file mode 100644 index 0000000..2b76209 --- /dev/null +++ b/toolbox/getevent.h
@@ -0,0 +1,725 @@ +#include <linux/input.h> + +struct label { + const char *name; + int value; +}; + +#define LABEL(constant) { #constant, constant } +#define LABEL_END { NULL, -1 } + +static struct label input_prop_labels[] = { + LABEL(INPUT_PROP_POINTER), + LABEL(INPUT_PROP_DIRECT), + LABEL(INPUT_PROP_BUTTONPAD), + LABEL(INPUT_PROP_SEMI_MT), + LABEL_END, +}; + +static struct label ev_labels[] = { + LABEL(EV_SYN), + LABEL(EV_KEY), + LABEL(EV_REL), + LABEL(EV_ABS), + LABEL(EV_MSC), + LABEL(EV_SW), + LABEL(EV_LED), + LABEL(EV_SND), + LABEL(EV_REP), + LABEL(EV_FF), + LABEL(EV_PWR), + LABEL(EV_FF_STATUS), + LABEL_END, +}; + +static struct label syn_labels[] = { + LABEL(SYN_REPORT), + LABEL(SYN_CONFIG), + LABEL(SYN_MT_REPORT), + LABEL(SYN_DROPPED), + LABEL_END, +}; + +static struct label key_labels[] = { + LABEL(KEY_RESERVED), + LABEL(KEY_ESC), + LABEL(KEY_1), + LABEL(KEY_2), + LABEL(KEY_3), + LABEL(KEY_4), + LABEL(KEY_5), + LABEL(KEY_6), + LABEL(KEY_7), + LABEL(KEY_8), + LABEL(KEY_9), + LABEL(KEY_0), + LABEL(KEY_MINUS), + LABEL(KEY_EQUAL), + LABEL(KEY_BACKSPACE), + LABEL(KEY_TAB), + LABEL(KEY_Q), + LABEL(KEY_W), + LABEL(KEY_E), + LABEL(KEY_R), + LABEL(KEY_T), + LABEL(KEY_Y), + LABEL(KEY_U), + LABEL(KEY_I), + LABEL(KEY_O), + LABEL(KEY_P), + LABEL(KEY_LEFTBRACE), + LABEL(KEY_RIGHTBRACE), + LABEL(KEY_ENTER), + LABEL(KEY_LEFTCTRL), + LABEL(KEY_A), + LABEL(KEY_S), + LABEL(KEY_D), + LABEL(KEY_F), + LABEL(KEY_G), + LABEL(KEY_H), + LABEL(KEY_J), + LABEL(KEY_K), + LABEL(KEY_L), + LABEL(KEY_SEMICOLON), + LABEL(KEY_APOSTROPHE), + LABEL(KEY_GRAVE), + LABEL(KEY_LEFTSHIFT), + LABEL(KEY_BACKSLASH), + LABEL(KEY_Z), + LABEL(KEY_X), + LABEL(KEY_C), + LABEL(KEY_V), + LABEL(KEY_B), + LABEL(KEY_N), + LABEL(KEY_M), + LABEL(KEY_COMMA), + LABEL(KEY_DOT), + LABEL(KEY_SLASH), + LABEL(KEY_RIGHTSHIFT), + LABEL(KEY_KPASTERISK), + LABEL(KEY_LEFTALT), + LABEL(KEY_SPACE), + LABEL(KEY_CAPSLOCK), + LABEL(KEY_F1), + LABEL(KEY_F2), + LABEL(KEY_F3), + LABEL(KEY_F4), + LABEL(KEY_F5), + LABEL(KEY_F6), + LABEL(KEY_F7), + LABEL(KEY_F8), + LABEL(KEY_F9), + LABEL(KEY_F10), + LABEL(KEY_NUMLOCK), + LABEL(KEY_SCROLLLOCK), + LABEL(KEY_KP7), + LABEL(KEY_KP8), + LABEL(KEY_KP9), + LABEL(KEY_KPMINUS), + LABEL(KEY_KP4), + LABEL(KEY_KP5), + LABEL(KEY_KP6), + LABEL(KEY_KPPLUS), + LABEL(KEY_KP1), + LABEL(KEY_KP2), + LABEL(KEY_KP3), + LABEL(KEY_KP0), + LABEL(KEY_KPDOT), + LABEL(KEY_ZENKAKUHANKAKU), + LABEL(KEY_102ND), + LABEL(KEY_F11), + LABEL(KEY_F12), + LABEL(KEY_RO), + LABEL(KEY_KATAKANA), + LABEL(KEY_HIRAGANA), + LABEL(KEY_HENKAN), + LABEL(KEY_KATAKANAHIRAGANA), + LABEL(KEY_MUHENKAN), + LABEL(KEY_KPJPCOMMA), + LABEL(KEY_KPENTER), + LABEL(KEY_RIGHTCTRL), + LABEL(KEY_KPSLASH), + LABEL(KEY_SYSRQ), + LABEL(KEY_RIGHTALT), + LABEL(KEY_LINEFEED), + LABEL(KEY_HOME), + LABEL(KEY_UP), + LABEL(KEY_PAGEUP), + LABEL(KEY_LEFT), + LABEL(KEY_RIGHT), + LABEL(KEY_END), + LABEL(KEY_DOWN), + LABEL(KEY_PAGEDOWN), + LABEL(KEY_INSERT), + LABEL(KEY_DELETE), + LABEL(KEY_MACRO), + LABEL(KEY_MUTE), + LABEL(KEY_VOLUMEDOWN), + LABEL(KEY_VOLUMEUP), + LABEL(KEY_POWER), + LABEL(KEY_KPEQUAL), + LABEL(KEY_KPPLUSMINUS), + LABEL(KEY_PAUSE), + LABEL(KEY_SCALE), + LABEL(KEY_KPCOMMA), + LABEL(KEY_HANGEUL), + LABEL(KEY_HANGUEL), + LABEL(KEY_HANJA), + LABEL(KEY_YEN), + LABEL(KEY_LEFTMETA), + LABEL(KEY_RIGHTMETA), + LABEL(KEY_COMPOSE), + LABEL(KEY_STOP), + LABEL(KEY_AGAIN), + LABEL(KEY_PROPS), + LABEL(KEY_UNDO), + LABEL(KEY_FRONT), + LABEL(KEY_COPY), + LABEL(KEY_OPEN), + LABEL(KEY_PASTE), + LABEL(KEY_FIND), + LABEL(KEY_CUT), + LABEL(KEY_HELP), + LABEL(KEY_MENU), + LABEL(KEY_CALC), + LABEL(KEY_SETUP), + LABEL(KEY_SLEEP), + LABEL(KEY_WAKEUP), + LABEL(KEY_FILE), + LABEL(KEY_SENDFILE), + LABEL(KEY_DELETEFILE), + LABEL(KEY_XFER), + LABEL(KEY_PROG1), + LABEL(KEY_PROG2), + LABEL(KEY_WWW), + LABEL(KEY_MSDOS), + LABEL(KEY_COFFEE), + LABEL(KEY_SCREENLOCK), + LABEL(KEY_DIRECTION), + LABEL(KEY_CYCLEWINDOWS), + LABEL(KEY_MAIL), + LABEL(KEY_BOOKMARKS), + LABEL(KEY_COMPUTER), + LABEL(KEY_BACK), + LABEL(KEY_FORWARD), + LABEL(KEY_CLOSECD), + LABEL(KEY_EJECTCD), + LABEL(KEY_EJECTCLOSECD), + LABEL(KEY_NEXTSONG), + LABEL(KEY_PLAYPAUSE), + LABEL(KEY_PREVIOUSSONG), + LABEL(KEY_STOPCD), + LABEL(KEY_RECORD), + LABEL(KEY_REWIND), + LABEL(KEY_PHONE), + LABEL(KEY_ISO), + LABEL(KEY_CONFIG), + LABEL(KEY_HOMEPAGE), + LABEL(KEY_REFRESH), + LABEL(KEY_EXIT), + LABEL(KEY_MOVE), + LABEL(KEY_EDIT), + LABEL(KEY_SCROLLUP), + LABEL(KEY_SCROLLDOWN), + LABEL(KEY_KPLEFTPAREN), + LABEL(KEY_KPRIGHTPAREN), + LABEL(KEY_NEW), + LABEL(KEY_REDO), + LABEL(KEY_F13), + LABEL(KEY_F14), + LABEL(KEY_F15), + LABEL(KEY_F16), + LABEL(KEY_F17), + LABEL(KEY_F18), + LABEL(KEY_F19), + LABEL(KEY_F20), + LABEL(KEY_F21), + LABEL(KEY_F22), + LABEL(KEY_F23), + LABEL(KEY_F24), + LABEL(KEY_PLAYCD), + LABEL(KEY_PAUSECD), + LABEL(KEY_PROG3), + LABEL(KEY_PROG4), + LABEL(KEY_DASHBOARD), + LABEL(KEY_SUSPEND), + LABEL(KEY_CLOSE), + LABEL(KEY_PLAY), + LABEL(KEY_FASTFORWARD), + LABEL(KEY_BASSBOOST), + LABEL(KEY_PRINT), + LABEL(KEY_HP), + LABEL(KEY_CAMERA), + LABEL(KEY_SOUND), + LABEL(KEY_QUESTION), + LABEL(KEY_EMAIL), + LABEL(KEY_CHAT), + LABEL(KEY_SEARCH), + LABEL(KEY_CONNECT), + LABEL(KEY_FINANCE), + LABEL(KEY_SPORT), + LABEL(KEY_SHOP), + LABEL(KEY_ALTERASE), + LABEL(KEY_CANCEL), + LABEL(KEY_BRIGHTNESSDOWN), + LABEL(KEY_BRIGHTNESSUP), + LABEL(KEY_MEDIA), + LABEL(KEY_SWITCHVIDEOMODE), + LABEL(KEY_KBDILLUMTOGGLE), + LABEL(KEY_KBDILLUMDOWN), + LABEL(KEY_KBDILLUMUP), + LABEL(KEY_SEND), + LABEL(KEY_REPLY), + LABEL(KEY_FORWARDMAIL), + LABEL(KEY_SAVE), + LABEL(KEY_DOCUMENTS), + LABEL(KEY_BATTERY), + LABEL(KEY_BLUETOOTH), + LABEL(KEY_WLAN), + LABEL(KEY_UWB), + LABEL(KEY_UNKNOWN), + LABEL(KEY_VIDEO_NEXT), + LABEL(KEY_VIDEO_PREV), + LABEL(KEY_BRIGHTNESS_CYCLE), + LABEL(KEY_BRIGHTNESS_ZERO), + LABEL(KEY_DISPLAY_OFF), + LABEL(KEY_WIMAX), + LABEL(KEY_RFKILL), + LABEL(BTN_0), + LABEL(BTN_1), + LABEL(BTN_2), + LABEL(BTN_3), + LABEL(BTN_4), + LABEL(BTN_5), + LABEL(BTN_6), + LABEL(BTN_7), + LABEL(BTN_8), + LABEL(BTN_9), + LABEL(BTN_LEFT), + LABEL(BTN_RIGHT), + LABEL(BTN_MIDDLE), + LABEL(BTN_SIDE), + LABEL(BTN_EXTRA), + LABEL(BTN_FORWARD), + LABEL(BTN_BACK), + LABEL(BTN_TASK), + LABEL(BTN_JOYSTICK), + LABEL(BTN_TRIGGER), + LABEL(BTN_THUMB), + LABEL(BTN_THUMB2), + LABEL(BTN_TOP), + LABEL(BTN_TOP2), + LABEL(BTN_PINKIE), + LABEL(BTN_BASE), + LABEL(BTN_BASE2), + LABEL(BTN_BASE3), + LABEL(BTN_BASE4), + LABEL(BTN_BASE5), + LABEL(BTN_BASE6), + LABEL(BTN_DEAD), + LABEL(BTN_A), + LABEL(BTN_B), + LABEL(BTN_C), + LABEL(BTN_X), + LABEL(BTN_Y), + LABEL(BTN_Z), + LABEL(BTN_TL), + LABEL(BTN_TR), + LABEL(BTN_TL2), + LABEL(BTN_TR2), + LABEL(BTN_SELECT), + LABEL(BTN_START), + LABEL(BTN_MODE), + LABEL(BTN_THUMBL), + LABEL(BTN_THUMBR), + LABEL(BTN_TOOL_PEN), + LABEL(BTN_TOOL_RUBBER), + LABEL(BTN_TOOL_BRUSH), + LABEL(BTN_TOOL_PENCIL), + LABEL(BTN_TOOL_AIRBRUSH), + LABEL(BTN_TOOL_FINGER), + LABEL(BTN_TOOL_MOUSE), + LABEL(BTN_TOOL_LENS), + LABEL(BTN_TOUCH), + LABEL(BTN_STYLUS), + LABEL(BTN_STYLUS2), + LABEL(BTN_TOOL_DOUBLETAP), + LABEL(BTN_TOOL_TRIPLETAP), + LABEL(BTN_TOOL_QUADTAP), + LABEL(BTN_GEAR_DOWN), + LABEL(BTN_GEAR_UP), + LABEL(KEY_OK), + LABEL(KEY_SELECT), + LABEL(KEY_GOTO), + LABEL(KEY_CLEAR), + LABEL(KEY_POWER2), + LABEL(KEY_OPTION), + LABEL(KEY_INFO), + LABEL(KEY_TIME), + LABEL(KEY_VENDOR), + LABEL(KEY_ARCHIVE), + LABEL(KEY_PROGRAM), + LABEL(KEY_CHANNEL), + LABEL(KEY_FAVORITES), + LABEL(KEY_EPG), + LABEL(KEY_PVR), + LABEL(KEY_MHP), + LABEL(KEY_LANGUAGE), + LABEL(KEY_TITLE), + LABEL(KEY_SUBTITLE), + LABEL(KEY_ANGLE), + LABEL(KEY_ZOOM), + LABEL(KEY_MODE), + LABEL(KEY_KEYBOARD), + LABEL(KEY_SCREEN), + LABEL(KEY_PC), + LABEL(KEY_TV), + LABEL(KEY_TV2), + LABEL(KEY_VCR), + LABEL(KEY_VCR2), + LABEL(KEY_SAT), + LABEL(KEY_SAT2), + LABEL(KEY_CD), + LABEL(KEY_TAPE), + LABEL(KEY_RADIO), + LABEL(KEY_TUNER), + LABEL(KEY_PLAYER), + LABEL(KEY_TEXT), + LABEL(KEY_DVD), + LABEL(KEY_AUX), + LABEL(KEY_MP3), + LABEL(KEY_AUDIO), + LABEL(KEY_VIDEO), + LABEL(KEY_DIRECTORY), + LABEL(KEY_LIST), + LABEL(KEY_MEMO), + LABEL(KEY_CALENDAR), + LABEL(KEY_RED), + LABEL(KEY_GREEN), + LABEL(KEY_YELLOW), + LABEL(KEY_BLUE), + LABEL(KEY_CHANNELUP), + LABEL(KEY_CHANNELDOWN), + LABEL(KEY_FIRST), + LABEL(KEY_LAST), + LABEL(KEY_AB), + LABEL(KEY_NEXT), + LABEL(KEY_RESTART), + LABEL(KEY_SLOW), + LABEL(KEY_SHUFFLE), + LABEL(KEY_BREAK), + LABEL(KEY_PREVIOUS), + LABEL(KEY_DIGITS), + LABEL(KEY_TEEN), + LABEL(KEY_TWEN), + LABEL(KEY_VIDEOPHONE), + LABEL(KEY_GAMES), + LABEL(KEY_ZOOMIN), + LABEL(KEY_ZOOMOUT), + LABEL(KEY_ZOOMRESET), + LABEL(KEY_WORDPROCESSOR), + LABEL(KEY_EDITOR), + LABEL(KEY_SPREADSHEET), + LABEL(KEY_GRAPHICSEDITOR), + LABEL(KEY_PRESENTATION), + LABEL(KEY_DATABASE), + LABEL(KEY_NEWS), + LABEL(KEY_VOICEMAIL), + LABEL(KEY_ADDRESSBOOK), + LABEL(KEY_MESSENGER), + LABEL(KEY_DISPLAYTOGGLE), + LABEL(KEY_SPELLCHECK), + LABEL(KEY_LOGOFF), + LABEL(KEY_DOLLAR), + LABEL(KEY_EURO), + LABEL(KEY_FRAMEBACK), + LABEL(KEY_FRAMEFORWARD), + LABEL(KEY_CONTEXT_MENU), + LABEL(KEY_MEDIA_REPEAT), + LABEL(KEY_10CHANNELSUP), + LABEL(KEY_10CHANNELSDOWN), + LABEL(KEY_IMAGES), + LABEL(KEY_DEL_EOL), + LABEL(KEY_DEL_EOS), + LABEL(KEY_INS_LINE), + LABEL(KEY_DEL_LINE), + LABEL(KEY_FN), + LABEL(KEY_FN_ESC), + LABEL(KEY_FN_F1), + LABEL(KEY_FN_F2), + LABEL(KEY_FN_F3), + LABEL(KEY_FN_F4), + LABEL(KEY_FN_F5), + LABEL(KEY_FN_F6), + LABEL(KEY_FN_F7), + LABEL(KEY_FN_F8), + LABEL(KEY_FN_F9), + LABEL(KEY_FN_F10), + LABEL(KEY_FN_F11), + LABEL(KEY_FN_F12), + LABEL(KEY_FN_1), + LABEL(KEY_FN_2), + LABEL(KEY_FN_D), + LABEL(KEY_FN_E), + LABEL(KEY_FN_F), + LABEL(KEY_FN_S), + LABEL(KEY_FN_B), + LABEL(KEY_BRL_DOT1), + LABEL(KEY_BRL_DOT2), + LABEL(KEY_BRL_DOT3), + LABEL(KEY_BRL_DOT4), + LABEL(KEY_BRL_DOT5), + LABEL(KEY_BRL_DOT6), + LABEL(KEY_BRL_DOT7), + LABEL(KEY_BRL_DOT8), + LABEL(KEY_BRL_DOT9), + LABEL(KEY_BRL_DOT10), + LABEL(KEY_NUMERIC_0), + LABEL(KEY_NUMERIC_1), + LABEL(KEY_NUMERIC_2), + LABEL(KEY_NUMERIC_3), + LABEL(KEY_NUMERIC_4), + LABEL(KEY_NUMERIC_5), + LABEL(KEY_NUMERIC_6), + LABEL(KEY_NUMERIC_7), + LABEL(KEY_NUMERIC_8), + LABEL(KEY_NUMERIC_9), + LABEL(KEY_NUMERIC_STAR), + LABEL(KEY_NUMERIC_POUND), + LABEL(KEY_CAMERA_FOCUS), + LABEL(KEY_WPS_BUTTON), + LABEL(KEY_TOUCHPAD_TOGGLE), + LABEL(KEY_TOUCHPAD_ON), + LABEL(KEY_TOUCHPAD_OFF), + LABEL(KEY_CAMERA_ZOOMIN), + LABEL(KEY_CAMERA_ZOOMOUT), + LABEL(KEY_CAMERA_UP), + LABEL(KEY_CAMERA_DOWN), + LABEL(KEY_CAMERA_LEFT), + LABEL(KEY_CAMERA_RIGHT), + LABEL(BTN_TRIGGER_HAPPY1), + LABEL(BTN_TRIGGER_HAPPY2), + LABEL(BTN_TRIGGER_HAPPY3), + LABEL(BTN_TRIGGER_HAPPY4), + LABEL(BTN_TRIGGER_HAPPY5), + LABEL(BTN_TRIGGER_HAPPY6), + LABEL(BTN_TRIGGER_HAPPY7), + LABEL(BTN_TRIGGER_HAPPY8), + LABEL(BTN_TRIGGER_HAPPY9), + LABEL(BTN_TRIGGER_HAPPY10), + LABEL(BTN_TRIGGER_HAPPY11), + LABEL(BTN_TRIGGER_HAPPY12), + LABEL(BTN_TRIGGER_HAPPY13), + LABEL(BTN_TRIGGER_HAPPY14), + LABEL(BTN_TRIGGER_HAPPY15), + LABEL(BTN_TRIGGER_HAPPY16), + LABEL(BTN_TRIGGER_HAPPY17), + LABEL(BTN_TRIGGER_HAPPY18), + LABEL(BTN_TRIGGER_HAPPY19), + LABEL(BTN_TRIGGER_HAPPY20), + LABEL(BTN_TRIGGER_HAPPY21), + LABEL(BTN_TRIGGER_HAPPY22), + LABEL(BTN_TRIGGER_HAPPY23), + LABEL(BTN_TRIGGER_HAPPY24), + LABEL(BTN_TRIGGER_HAPPY25), + LABEL(BTN_TRIGGER_HAPPY26), + LABEL(BTN_TRIGGER_HAPPY27), + LABEL(BTN_TRIGGER_HAPPY28), + LABEL(BTN_TRIGGER_HAPPY29), + LABEL(BTN_TRIGGER_HAPPY30), + LABEL(BTN_TRIGGER_HAPPY31), + LABEL(BTN_TRIGGER_HAPPY32), + LABEL(BTN_TRIGGER_HAPPY33), + LABEL(BTN_TRIGGER_HAPPY34), + LABEL(BTN_TRIGGER_HAPPY35), + LABEL(BTN_TRIGGER_HAPPY36), + LABEL(BTN_TRIGGER_HAPPY37), + LABEL(BTN_TRIGGER_HAPPY38), + LABEL(BTN_TRIGGER_HAPPY39), + LABEL(BTN_TRIGGER_HAPPY40), + LABEL_END, +}; + +static struct label rel_labels[] = { + LABEL(REL_X), + LABEL(REL_Y), + LABEL(REL_Z), + LABEL(REL_RX), + LABEL(REL_RY), + LABEL(REL_RZ), + LABEL(REL_HWHEEL), + LABEL(REL_DIAL), + LABEL(REL_WHEEL), + LABEL(REL_MISC), + LABEL_END, +}; + +static struct label abs_labels[] = { + LABEL(ABS_X), + LABEL(ABS_Y), + LABEL(ABS_Z), + LABEL(ABS_RX), + LABEL(ABS_RY), + LABEL(ABS_RZ), + LABEL(ABS_THROTTLE), + LABEL(ABS_RUDDER), + LABEL(ABS_WHEEL), + LABEL(ABS_GAS), + LABEL(ABS_BRAKE), + LABEL(ABS_HAT0X), + LABEL(ABS_HAT0Y), + LABEL(ABS_HAT1X), + LABEL(ABS_HAT1Y), + LABEL(ABS_HAT2X), + LABEL(ABS_HAT2Y), + LABEL(ABS_HAT3X), + LABEL(ABS_HAT3Y), + LABEL(ABS_PRESSURE), + LABEL(ABS_DISTANCE), + LABEL(ABS_TILT_X), + LABEL(ABS_TILT_Y), + LABEL(ABS_TOOL_WIDTH), + LABEL(ABS_VOLUME), + LABEL(ABS_MISC), + LABEL(ABS_MT_SLOT), + LABEL(ABS_MT_TOUCH_MAJOR), + LABEL(ABS_MT_TOUCH_MINOR), + LABEL(ABS_MT_WIDTH_MAJOR), + LABEL(ABS_MT_WIDTH_MINOR), + LABEL(ABS_MT_ORIENTATION), + LABEL(ABS_MT_POSITION_X), + LABEL(ABS_MT_POSITION_Y), + LABEL(ABS_MT_TOOL_TYPE), + LABEL(ABS_MT_BLOB_ID), + LABEL(ABS_MT_TRACKING_ID), + LABEL(ABS_MT_PRESSURE), + LABEL(ABS_MT_DISTANCE), + LABEL_END, +}; + +static struct label sw_labels[] = { + LABEL(SW_LID), + LABEL(SW_TABLET_MODE), + LABEL(SW_HEADPHONE_INSERT), + LABEL(SW_RFKILL_ALL), + LABEL(SW_RADIO), + LABEL(SW_MICROPHONE_INSERT), + LABEL(SW_DOCK), + LABEL(SW_LINEOUT_INSERT), + LABEL(SW_JACK_PHYSICAL_INSERT), + LABEL(SW_VIDEOOUT_INSERT), + LABEL(SW_CAMERA_LENS_COVER), + LABEL(SW_KEYPAD_SLIDE), + LABEL(SW_FRONT_PROXIMITY), + LABEL(SW_ROTATE_LOCK), + LABEL_END, +}; + +static struct label msc_labels[] = { + LABEL(MSC_SERIAL), + LABEL(MSC_PULSELED), + LABEL(MSC_GESTURE), + LABEL(MSC_RAW), + LABEL(MSC_SCAN), + LABEL_END, +}; + +static struct label led_labels[] = { + LABEL(LED_NUML), + LABEL(LED_CAPSL), + LABEL(LED_SCROLLL), + LABEL(LED_COMPOSE), + LABEL(LED_KANA), + LABEL(LED_SLEEP), + LABEL(LED_SUSPEND), + LABEL(LED_MUTE), + LABEL(LED_MISC), + LABEL(LED_MAIL), + LABEL(LED_CHARGING), + LABEL_END, +}; + +static struct label rep_labels[] = { + LABEL(REP_DELAY), + LABEL(REP_PERIOD), + LABEL_END, +}; + +static struct label snd_labels[] = { + LABEL(SND_CLICK), + LABEL(SND_BELL), + LABEL(SND_TONE), + LABEL_END, +}; + +static struct label id_labels[] = { + LABEL(ID_BUS), + LABEL(ID_VENDOR), + LABEL(ID_PRODUCT), + LABEL(ID_VERSION), + LABEL_END, +}; + +static struct label bus_labels[] = { + LABEL(BUS_PCI), + LABEL(BUS_ISAPNP), + LABEL(BUS_USB), + LABEL(BUS_HIL), + LABEL(BUS_BLUETOOTH), + LABEL(BUS_VIRTUAL), + LABEL(BUS_ISA), + LABEL(BUS_I8042), + LABEL(BUS_XTKBD), + LABEL(BUS_RS232), + LABEL(BUS_GAMEPORT), + LABEL(BUS_PARPORT), + LABEL(BUS_AMIGA), + LABEL(BUS_ADB), + LABEL(BUS_I2C), + LABEL(BUS_HOST), + LABEL(BUS_GSC), + LABEL(BUS_ATARI), + LABEL(BUS_SPI), + LABEL_END, +}; + +static struct label mt_tool_labels[] = { + LABEL(MT_TOOL_FINGER), + LABEL(MT_TOOL_PEN), + LABEL(MT_TOOL_MAX), + LABEL_END, +}; + +static struct label ff_status_labels[] = { + LABEL(FF_STATUS_STOPPED), + LABEL(FF_STATUS_PLAYING), + LABEL(FF_STATUS_MAX), + LABEL_END, +}; + +static struct label ff_labels[] = { + LABEL(FF_RUMBLE), + LABEL(FF_PERIODIC), + LABEL(FF_CONSTANT), + LABEL(FF_SPRING), + LABEL(FF_FRICTION), + LABEL(FF_DAMPER), + LABEL(FF_INERTIA), + LABEL(FF_RAMP), + LABEL(FF_SQUARE), + LABEL(FF_TRIANGLE), + LABEL(FF_SINE), + LABEL(FF_SAW_UP), + LABEL(FF_SAW_DOWN), + LABEL(FF_CUSTOM), + LABEL(FF_GAIN), + LABEL(FF_AUTOCENTER), + LABEL_END, +}; + +static struct label key_value_labels[] = { + { "UP", 0 }, + { "DOWN", 1 }, + { "REPEAT", 2 }, + LABEL_END, +};
diff --git a/toolbox/getprop.c b/toolbox/getprop.c new file mode 100644 index 0000000..c001fda --- /dev/null +++ b/toolbox/getprop.c
@@ -0,0 +1,55 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <cutils/properties.h> + +#include <sys/system_properties.h> +#include "dynarray.h" + +static void record_prop(const char* key, const char* name, void* opaque) +{ + strlist_t* list = opaque; + char temp[PROP_VALUE_MAX + PROP_NAME_MAX + 16]; + snprintf(temp, sizeof temp, "[%s]: [%s]", key, name); + strlist_append_dup(list, temp); +} + +static void list_properties(void) +{ + strlist_t list[1] = { STRLIST_INITIALIZER }; + + /* Record properties in the string list */ + (void)property_list(record_prop, list); + + /* Sort everything */ + strlist_sort(list); + + /* print everything */ + STRLIST_FOREACH(list, str, printf("%s\n", str)); + + /* voila */ + strlist_done(list); +} + +int __system_property_wait(prop_info *pi); + +int getprop_main(int argc, char *argv[]) +{ + int n = 0; + + if (argc == 1) { + list_properties(); + } else { + char value[PROPERTY_VALUE_MAX]; + char *default_value; + if(argc > 2) { + default_value = argv[2]; + } else { + default_value = ""; + } + + property_get(argv[1], value, default_value); + printf("%s\n", value); + } + return 0; +}
diff --git a/toolbox/getsebool.c b/toolbox/getsebool.c new file mode 100644 index 0000000..aab5200 --- /dev/null +++ b/toolbox/getsebool.c
@@ -0,0 +1,104 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <getopt.h> +#include <errno.h> +#include <string.h> +#include <selinux/selinux.h> + +static void usage(const char *progname) +{ + fprintf(stderr, "usage: %s -a or %s boolean...\n", progname, progname); + exit(1); +} + +int getsebool_main(int argc, char **argv) +{ + int i, get_all = 0, rc = 0, active, pending, len = 0, opt; + char **names; + + while ((opt = getopt(argc, argv, "a")) > 0) { + switch (opt) { + case 'a': + if (argc > 2) + usage(argv[0]); + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", + argv[0]); + return 1; + } + errno = 0; + rc = security_get_boolean_names(&names, &len); + if (rc) { + fprintf(stderr, + "%s: Unable to get boolean names: %s\n", + argv[0], strerror(errno)); + return 1; + } + if (!len) { + printf("No booleans\n"); + return 0; + } + get_all = 1; + break; + default: + usage(argv[0]); + } + } + + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", argv[0]); + return 1; + } + if (!len) { + if (argc < 2) + usage(argv[0]); + len = argc - 1; + names = malloc(sizeof(char *) * len); + if (!names) { + fprintf(stderr, "%s: out of memory\n", argv[0]); + return 2; + } + for (i = 0; i < len; i++) { + names[i] = strdup(argv[i + 1]); + if (!names[i]) { + fprintf(stderr, "%s: out of memory\n", + argv[0]); + return 2; + } + } + } + + for (i = 0; i < len; i++) { + active = security_get_boolean_active(names[i]); + if (active < 0) { + if (get_all && errno == EACCES) + continue; + fprintf(stderr, "Error getting active value for %s\n", + names[i]); + rc = -1; + goto out; + } + pending = security_get_boolean_pending(names[i]); + if (pending < 0) { + fprintf(stderr, "Error getting pending value for %s\n", + names[i]); + rc = -1; + goto out; + } + if (pending != active) { + printf("%s --> %s pending: %s\n", names[i], + (active ? "on" : "off"), + (pending ? "on" : "off")); + } else { + printf("%s --> %s\n", names[i], + (active ? "on" : "off")); + } + } + +out: + for (i = 0; i < len; i++) + free(names[i]); + free(names); + return rc; +}
diff --git a/toolbox/hd.c b/toolbox/hd.c new file mode 100644 index 0000000..da31245 --- /dev/null +++ b/toolbox/hd.c
@@ -0,0 +1,96 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +int hd_main(int argc, char *argv[]) +{ + int c; + int fd; + unsigned char buf[4096]; + int res; + int read_len; + int rv = 0; + int i; + int filepos = 0; + int sum; + int lsum; + + int base = -1; + int count = 0; + int repeat = 0; + + do { + c = getopt(argc, argv, "b:c:r:"); + if (c == EOF) + break; + switch (c) { + case 'b': + base = strtol(optarg, NULL, 0); + break; + case 'c': + count = strtol(optarg, NULL, 0); + break; + case 'r': + repeat = strtol(optarg, NULL, 0); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (optind + 1 != argc) { + fprintf(stderr, "Usage: %s [-b base] [-c count] [-r delay] file\n", argv[0]); + exit(1); + } + + fd = open(argv[optind], O_RDONLY); + if(fd < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + + do { + if(base >= 0) { + lseek(fd, base, SEEK_SET); + filepos = base; + } + sum = 0; + lsum = 0; + while(1) { + read_len = sizeof(buf); + if(count > 0 && base + count - filepos < read_len) + read_len = base + count - filepos; + res = read(fd, &buf, read_len); + for(i = 0; i < res; i++) { + if((i & 15) == 0) { + printf("%08x: ", filepos + i); + } + lsum += buf[i]; + sum += buf[i]; + printf("%02x ", buf[i]); + if(((i & 15) == 15) || (i == res - 1)) { + printf("s %x\n", lsum); + lsum = 0; + } + } + if(res <= 0) { + printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno)); + return 1; + } + filepos += res; + if(filepos == base + count) + break; + } + printf("sum %x\n", sum); + if(repeat) + sleep(repeat); + } while(repeat); + return 0; +}
diff --git a/toolbox/id.c b/toolbox/id.c new file mode 100644 index 0000000..bc79288 --- /dev/null +++ b/toolbox/id.c
@@ -0,0 +1,64 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> + +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif + +static void print_uid(uid_t uid) +{ + struct passwd *pw = getpwuid(uid); + + if (pw) { + printf("%d(%s)", uid, pw->pw_name); + } else { + printf("%d",uid); + } +} + +static void print_gid(gid_t gid) +{ + struct group *gr = getgrgid(gid); + if (gr) { + printf("%d(%s)", gid, gr->gr_name); + } else { + printf("%d",gid); + } +} + +int id_main(int argc, char **argv) +{ + gid_t list[64]; + int n, max; +#ifdef HAVE_SELINUX + char *secctx; +#endif + + max = getgroups(64, list); + if (max < 0) max = 0; + + printf("uid="); + print_uid(getuid()); + printf(" gid="); + print_gid(getgid()); + if (max) { + printf(" groups="); + print_gid(list[0]); + for(n = 1; n < max; n++) { + printf(","); + print_gid(list[n]); + } + } +#ifdef HAVE_SELINUX + if (getcon(&secctx) == 0) { + printf(" context=%s", secctx); + free(secctx); + } +#endif + printf("\n"); + return 0; +}
diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c new file mode 100644 index 0000000..80c0e5c --- /dev/null +++ b/toolbox/ifconfig.c
@@ -0,0 +1,163 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include <arpa/inet.h> + +static void die(const char *s) +{ + fprintf(stderr,"error: %s (%s)\n", s, strerror(errno)); + exit(-1); +} + +static void setflags(int s, struct ifreq *ifr, int set, int clr) +{ + if(ioctl(s, SIOCGIFFLAGS, ifr) < 0) die("SIOCGIFFLAGS"); + ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set; + if(ioctl(s, SIOCSIFFLAGS, ifr) < 0) die("SIOCSIFFLAGS"); +} + +static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) +{ + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); +} + +static void setmtu(int s, struct ifreq *ifr, const char *mtu) +{ + int m = atoi(mtu); + ifr->ifr_mtu = m; + if(ioctl(s, SIOCSIFMTU, ifr) < 0) die("SIOCSIFMTU"); +} +static void setdstaddr(int s, struct ifreq *ifr, const char *addr) +{ + init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_dstaddr, addr); + if(ioctl(s, SIOCSIFDSTADDR, ifr) < 0) die("SIOCSIFDSTADDR"); +} + +static void setnetmask(int s, struct ifreq *ifr, const char *addr) +{ + init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr); + if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK"); +} + +static void setaddr(int s, struct ifreq *ifr, const char *addr) +{ + init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr); + if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR"); +} + +int ifconfig_main(int argc, char *argv[]) +{ + struct ifreq ifr; + int s; + unsigned int addr, mask, flags; + char astring[20]; + char mstring[20]; + char *updown, *brdcst, *loopbk, *ppp, *running, *multi; + + argc--; + argv++; + + if(argc == 0) return 0; + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, argv[0], IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + argc--, argv++; + + if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + die("cannot open control socket\n"); + } + + if (argc == 0) { + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { + perror(ifr.ifr_name); + return -1; + } else + flags = ifr.ifr_flags; + + sprintf(astring, "%d.%d.%d.%d", + addr & 0xff, + ((addr >> 8) & 0xff), + ((addr >> 16) & 0xff), + ((addr >> 24) & 0xff)); + sprintf(mstring, "%d.%d.%d.%d", + mask & 0xff, + ((mask >> 8) & 0xff), + ((mask >> 16) & 0xff), + ((mask >> 24) & 0xff)); + printf("%s: ip %s mask %s flags [", ifr.ifr_name, + astring, + mstring + ); + + updown = (flags & IFF_UP) ? "up" : "down"; + brdcst = (flags & IFF_BROADCAST) ? " broadcast" : ""; + loopbk = (flags & IFF_LOOPBACK) ? " loopback" : ""; + ppp = (flags & IFF_POINTOPOINT) ? " point-to-point" : ""; + running = (flags & IFF_RUNNING) ? " running" : ""; + multi = (flags & IFF_MULTICAST) ? " multicast" : ""; + printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi); + return 0; + } + + while(argc > 0) { + if (!strcmp(argv[0], "up")) { + setflags(s, &ifr, IFF_UP, 0); + } else if (!strcmp(argv[0], "mtu")) { + argc--, argv++; + if (!argc) { + errno = EINVAL; + die("expecting a value for parameter \"mtu\""); + } + setmtu(s, &ifr, argv[0]); + } else if (!strcmp(argv[0], "-pointopoint")) { + setflags(s, &ifr, IFF_POINTOPOINT, 1); + } else if (!strcmp(argv[0], "pointopoint")) { + argc--, argv++; + if (!argc) { + errno = EINVAL; + die("expecting an IP address for parameter \"pointtopoint\""); + } + setdstaddr(s, &ifr, argv[0]); + setflags(s, &ifr, IFF_POINTOPOINT, 0); + } else if (!strcmp(argv[0], "down")) { + setflags(s, &ifr, 0, IFF_UP); + } else if (!strcmp(argv[0], "netmask")) { + argc--, argv++; + if (!argc) { + errno = EINVAL; + die("expecting an IP address for parameter \"netmask\""); + } + setnetmask(s, &ifr, argv[0]); + } else if (isdigit(argv[0][0])) { + setaddr(s, &ifr, argv[0]); + setflags(s, &ifr, IFF_UP, 0); + } + argc--, argv++; + } + return 0; +}
diff --git a/toolbox/iftop.c b/toolbox/iftop.c new file mode 100644 index 0000000..800c0f0 --- /dev/null +++ b/toolbox/iftop.c
@@ -0,0 +1,278 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#define PROC_NET_DEV "/proc/net/dev" + +#define MAX_IF 8 /* max interfaces we can handle */ + +#ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +#endif + +#define _STR(s) #s +#define STR(s) _STR(s) + +struct if_stats { + char name[IFNAMSIZ]; + + unsigned int mtu; + + unsigned int rx_bytes; + unsigned int rx_packets; + unsigned int rx_errors; + unsigned int rx_dropped; + + unsigned int tx_bytes; + unsigned int tx_packets; + unsigned int tx_errors; + unsigned int tx_dropped; +}; + +static int get_mtu(const char *if_name) +{ + struct ifreq ifr; + int s, ret; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_addr.sa_family = AF_INET; + strcpy(ifr.ifr_name, if_name); + + ret = ioctl(s, SIOCGIFMTU, &ifr); + if (ret < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + ret = close(s); + if (ret < 0) { + perror("close"); + exit(EXIT_FAILURE); + } + + return ifr.ifr_mtu; +} + +static int get_interfaces(struct if_stats *ifs) +{ + char buf[PAGE_SIZE]; + char *p; + int ret, nr, fd; + + fd = open(PROC_NET_DEV, O_RDONLY); + if (fd < 0) { + perror("open"); + exit(EXIT_FAILURE); + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret < 0) { + perror("read"); + exit(EXIT_FAILURE); + } else if (!ret) { + fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n"); + exit(EXIT_FAILURE); + } + buf[ret] = '\0'; + + /* skip down to the third line */ + p = strchr(buf, '\n'); + if (!p) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + p = strchr(p + 1, '\n'); + if (!p) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + p += 1; + + /* + * Key: + * if: (Rx) bytes packets errs drop fifo frame compressed multicast \ + * (Tx) bytes packets errs drop fifo colls carrier compressed + */ + for (nr = 0; nr < MAX_IF; nr++) { + char *c; + + ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name); + if (ret != 1) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + + /* + * This works around a bug in the proc file where large interface names + * or Rx byte counts eat the delimiter, breaking sscanf. + */ + c = strchr(ifs->name, ':'); + if (c) + *c = '\0'; + + p = strchr(p, ':') + 1; + + ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u " + "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets, + &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes, + &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped); + if (ret != 8) { + fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n"); + exit(EXIT_FAILURE); + } + + ifs->mtu = get_mtu(ifs->name); + + p = strchr(p, '\n') + 1; + if (*p == '\0') { + nr++; + break; + } + + ifs++; + } + + ret = close(fd); + if (ret) { + perror("close"); + exit(EXIT_FAILURE); + } + + return nr; +} + +static void print_header(void) +{ + printf(" Rx Tx\n"); + printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n", + "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes", + "packets", "errs", "drpd"); +} + +static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr) +{ + int i = 0; + + while (nr--) { + if (old->rx_packets || old->tx_packets) { + printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n", + new->name, new->mtu, + new->rx_bytes - old->rx_bytes, + new->rx_packets - old->rx_packets, + new->rx_errors - old->rx_errors, + new->rx_dropped - old->rx_dropped, + new->tx_bytes - old->tx_bytes, + new->tx_packets - old->tx_packets, + new->tx_errors - old->tx_errors, + new->tx_dropped - old->tx_dropped); + i++; + } + old++; + new++; + } + + return i; +} + +static void usage(const char *cmd) +{ + fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd); +} + +int iftop_main(int argc, char *argv[]) +{ + struct if_stats ifs[2][MAX_IF]; + int count = 0, header_interval = 22, delay = 1, i; + unsigned int toggle = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -d requires an argument.\n"); + exit(EXIT_FAILURE); + } + delay = atoi(argv[i++]); + if (!delay) + delay = 1; + continue; + } + if (!strcmp(argv[i], "-r")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -r requires an argument.\n"); + exit(EXIT_FAILURE); + } + header_interval = atoi(argv[i++]); + if (header_interval < MAX_IF) + header_interval = MAX_IF; + continue; + } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + usage(argv[0]); + exit(EXIT_FAILURE); + } + + get_interfaces(ifs[!toggle]); + if (header_interval) + print_header(); + while (1) { + int nr; + + sleep(delay); + nr = get_interfaces(ifs[toggle]); + if (header_interval && count + nr > header_interval) { + print_header(); + count = 0; + } + count += print_interfaces(ifs[!toggle], ifs[toggle], nr); + toggle = !toggle; + } + + return 0; +}
diff --git a/toolbox/insmod.c b/toolbox/insmod.c new file mode 100644 index 0000000..756a64b --- /dev/null +++ b/toolbox/insmod.c
@@ -0,0 +1,97 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +extern int init_module(void *, unsigned long, const char *); + +static void *read_file(const char *filename, ssize_t *_size) +{ + int ret, fd; + struct stat sb; + ssize_t size; + void *buffer = NULL; + + /* open the file */ + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + /* find out how big it is */ + if (fstat(fd, &sb) < 0) + goto bail; + size = sb.st_size; + + /* allocate memory for it to be read into */ + buffer = malloc(size); + if (!buffer) + goto bail; + + /* slurp it into our buffer */ + ret = read(fd, buffer, size); + if (ret != size) + goto bail; + + /* let the caller know how big it is */ + *_size = size; + +bail: + close(fd); + return buffer; +} + +#define min(x,y) ((x) < (y) ? (x) : (y)) +int insmod_main(int argc, char **argv) +{ + void *file; + ssize_t size = 0; + char opts[1024]; + int ret; + + /* make sure we've got an argument */ + if (argc < 2) { + fprintf(stderr, "usage: insmod <module.o>\n"); + return -1; + } + + /* read the file into memory */ + file = read_file(argv[1], &size); + if (!file) { + fprintf(stderr, "insmod: can't open '%s'\n", argv[1]); + return -1; + } + + opts[0] = '\0'; + if (argc > 2) { + int i, len; + char *end = opts + sizeof(opts) - 1; + char *ptr = opts; + + for (i = 2; (i < argc) && (ptr < end); i++) { + len = min(strlen(argv[i]), end - ptr); + memcpy(ptr, argv[i], len); + ptr += len; + *ptr++ = ' '; + } + *(ptr - 1) = '\0'; + } + + /* pass it to the kernel */ + ret = init_module(file, size, opts); + if (ret != 0) { + fprintf(stderr, + "insmod: init_module '%s' failed (%s)\n", + argv[1], strerror(errno)); + } + + /* free the file buffer */ + free(file); + + return ret; +} +
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c new file mode 100644 index 0000000..fb555d2 --- /dev/null +++ b/toolbox/ioctl.c
@@ -0,0 +1,128 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <fcntl.h> +#include <getopt.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> +#include <pthread.h> +#include <sys/ioctl.h> + +int ioctl_main(int argc, char *argv[]) +{ + int c; + int fd; + int res; + + int read_only = 0; + int length = -1; + int arg_size = 4; + int direct_arg = 0; + uint32_t ioctl_nr; + void *ioctl_args; + uint8_t *ioctl_argp; + uint8_t *ioctl_argp_save; + int rem; + + do { + c = getopt(argc, argv, "rdl:a:h"); + if (c == EOF) + break; + switch (c) { + case 'r': + read_only = 1; + break; + case 'd': + direct_arg = 1; + break; + case 'l': + length = strtol(optarg, NULL, 0); + break; + case 'a': + arg_size = strtol(optarg, NULL, 0); + break; + case 'h': + fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n" + " -l <lenght> Length of io buffer\n" + " -a <argsize> Size of each argument (1-8)\n" + " -r Open device in read only mode\n" + " -d Direct argument (no iobuffer)\n" + " -h Print help\n", argv[0]); + return -1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(optind + 2 > argc) { + fprintf(stderr, "%s: too few arguments\n", argv[0]); + exit(1); + } + + fd = open(argv[optind], O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", argv[optind]); + return 1; + } + optind++; + + ioctl_nr = strtol(argv[optind], NULL, 0); + optind++; + + if(direct_arg) { + arg_size = 4; + length = 4; + } + + if(length < 0) { + length = (argc - optind) * arg_size; + } + if(length) { + ioctl_args = calloc(1, length); + + ioctl_argp_save = ioctl_argp = ioctl_args; + rem = length; + while(optind < argc) { + uint64_t tmp = strtoull(argv[optind], NULL, 0); + if(rem < arg_size) { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + exit(1); + } + memcpy(ioctl_argp, &tmp, arg_size); + ioctl_argp += arg_size; + rem -= arg_size; + optind++; + } + } + printf("sending ioctl 0x%x", ioctl_nr); + rem = length; + while(rem--) { + printf(" 0x%02x", *ioctl_argp_save++); + } + printf("\n"); + + if(direct_arg) + res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args); + else if(length) + res = ioctl(fd, ioctl_nr, ioctl_args); + else + res = ioctl(fd, ioctl_nr, 0); + if (res < 0) { + fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res); + return 1; + } + if(length) { + printf("return buf:"); + ioctl_argp = ioctl_args; + rem = length; + while(rem--) { + printf(" %02x", *ioctl_argp++); + } + printf("\n"); + } + return 0; +}
diff --git a/toolbox/ionice.c b/toolbox/ionice.c new file mode 100644 index 0000000..4a182f2 --- /dev/null +++ b/toolbox/ionice.c
@@ -0,0 +1,57 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <cutils/iosched_policy.h> + +static char *classes[] = {"none", "rt", "be", "idle", NULL}; + +int ionice_main(int argc, char *argv[]) +{ + IoSchedClass clazz = IoSchedClass_NONE; + int ioprio = 0; + int pid; + + if(argc != 2 && argc != 4) { + fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n"); + return 1; + } + + if (!(pid = atoi(argv[1]))) { + fprintf(stderr, "Invalid pid specified\n"); + return 1; + } + + if (argc == 2) { + if (android_get_ioprio(pid, &clazz, &ioprio)) { + fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno)); + return 1; + } + fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio); + return 0; + } + + if (!strcmp(argv[2], "none")) { + clazz = IoSchedClass_NONE; + } else if (!strcmp(argv[2], "rt")) { + clazz = IoSchedClass_RT; + } else if (!strcmp(argv[2], "be")) { + clazz = IoSchedClass_BE; + } else if (!strcmp(argv[2], "idle")) { + clazz = IoSchedClass_IDLE; + } else { + fprintf(stderr, "Unsupported class '%s'\n", argv[2]); + return 1; + } + + ioprio = atoi(argv[3]); + + printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio); + if (android_set_ioprio(pid, clazz, ioprio)) { + fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno)); + return 1; + } + + return 0; +}
diff --git a/toolbox/kill.c b/toolbox/kill.c new file mode 100644 index 0000000..4d0e479 --- /dev/null +++ b/toolbox/kill.c
@@ -0,0 +1,35 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/types.h> +#include <signal.h> + +int kill_main(int argc, char **argv) +{ + int sig = SIGTERM; + int result = 0; + + argc--; + argv++; + + if(argc >= 2 && argv[0][0] == '-'){ + sig = atoi(argv[0] + 1); + argc--; + argv++; + } + + while(argc > 0){ + int pid = atoi(argv[0]); + int err = kill(pid, sig); + if (err < 0) { + result = err; + fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno)); + } + + argc--; + argv++; + } + + return result; +}
diff --git a/toolbox/ln.c b/toolbox/ln.c new file mode 100644 index 0000000..dcd5e3a --- /dev/null +++ b/toolbox/ln.c
@@ -0,0 +1,34 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +static int usage() +{ + fprintf(stderr,"ln [-s] <target> <name>\n"); + return -1; +} + +int ln_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2) return usage(); + + if(!strcmp(argv[1],"-s")) { + symbolic = 1; + argc--; + argv++; + } + + if(argc < 3) return usage(); + + if(symbolic) { + ret = symlink(argv[1], argv[2]); + } else { + ret = link(argv[1], argv[2]); + } + if(ret < 0) + fprintf(stderr, "link failed %s\n", strerror(errno)); + return ret; +}
diff --git a/toolbox/load_policy.c b/toolbox/load_policy.c new file mode 100644 index 0000000..eb5aba6 --- /dev/null +++ b/toolbox/load_policy.c
@@ -0,0 +1,49 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> +#include <selinux/selinux.h> + +int load_policy_main(int argc, char **argv) +{ + int fd, rc, vers; + struct stat sb; + void *map; + const char *path; + + if (argc != 2) { + fprintf(stderr, "usage: %s policy-file\n", argv[0]); + exit(1); + } + + path = argv[1]; + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Could not open %s: %s\n", path, strerror(errno)); + exit(2); + } + + if (fstat(fd, &sb) < 0) { + fprintf(stderr, "Could not stat %s: %s\n", path, strerror(errno)); + exit(3); + } + + map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, "Could not mmap %s: %s\n", path, strerror(errno)); + exit(4); + } + + rc = security_load_policy(map, sb.st_size); + if (rc < 0) { + fprintf(stderr, "Could not load %s: %s\n", path, strerror(errno)); + exit(5); + } + munmap(map, sb.st_size); + close(fd); + exit(0); +}
diff --git a/toolbox/log.c b/toolbox/log.c new file mode 100644 index 0000000..a3470cc --- /dev/null +++ b/toolbox/log.c
@@ -0,0 +1,145 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <log/log.h> +#include <ctype.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <stdlib.h> +#include <cutils/sockets.h> +#include <unistd.h> + +/* + * Note: also accepts 0-9 priorities + * returns ANDROID_LOG_UNKNOWN if the character is unrecognized + */ +static android_LogPriority filterCharToPri (char c) +{ + android_LogPriority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+ANDROID_LOG_SILENT)) { + pri = ANDROID_LOG_VERBOSE; + } else { + pri = (android_LogPriority)(c - '0'); + } + } else if (c == 'v') { + pri = ANDROID_LOG_VERBOSE; + } else if (c == 'd') { + pri = ANDROID_LOG_DEBUG; + } else if (c == 'i') { + pri = ANDROID_LOG_INFO; + } else if (c == 'w') { + pri = ANDROID_LOG_WARN; + } else if (c == 'e') { + pri = ANDROID_LOG_ERROR; + } else if (c == 'f') { + pri = ANDROID_LOG_FATAL; + } else if (c == 's') { + pri = ANDROID_LOG_SILENT; + } else if (c == '*') { + pri = ANDROID_LOG_DEFAULT; + } else { + pri = ANDROID_LOG_UNKNOWN; + } + + return pri; +} + +static int usage(const char *s) +{ + fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s); + + fprintf(stderr, "\tpriorityChar should be one of:\n" + "\t\tv,d,i,w,e\n"); + exit(-1); +} + + +int log_main(int argc, char *argv[]) +{ + android_LogPriority priority; + const char *tag = "log"; + char buffer[4096]; + int i; + + priority = ANDROID_LOG_INFO; + + for (;;) { + int ret; + + ret = getopt(argc, argv, "t:p:h"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 't': + tag = optarg; + break; + + case 'p': + priority = filterCharToPri(optarg[0]); + if (priority == ANDROID_LOG_UNKNOWN) { + usage(argv[0]); + } + break; + + case 'h': + usage(argv[0]); + break; + } + } + + if (optind == argc) { + usage(argv[0]); + } + + buffer[0] = '\0'; + + for (i = optind ; i < argc ; i++) { + strncat(buffer, argv[i], sizeof(buffer)-1); + strncat(buffer, " ", sizeof(buffer)-1); + } + + if(buffer[0] == 0) { + usage(argv[0]); + } + + __android_log_print(priority, tag, "%s", buffer); + + return 0; +} +
diff --git a/toolbox/ls.c b/toolbox/ls.c new file mode 100644 index 0000000..e93ce92 --- /dev/null +++ b/toolbox/ls.c
@@ -0,0 +1,490 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif + +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> + +#include <pwd.h> +#include <grp.h> + +#include <linux/kdev_t.h> +#include <limits.h> + +#include "dynarray.h" + +// bits for flags argument +#define LIST_LONG (1 << 0) +#define LIST_ALL (1 << 1) +#define LIST_RECURSIVE (1 << 2) +#define LIST_DIRECTORIES (1 << 3) +#define LIST_SIZE (1 << 4) +#define LIST_LONG_NUMERIC (1 << 5) +#define LIST_CLASSIFY (1 << 6) +#define LIST_MACLABEL (1 << 7) + +// fwd +static int listpath(const char *name, int flags); + +static char mode2kind(unsigned mode) +{ + switch(mode & S_IFMT){ + case S_IFSOCK: return 's'; + case S_IFLNK: return 'l'; + case S_IFREG: return '-'; + case S_IFDIR: return 'd'; + case S_IFBLK: return 'b'; + case S_IFCHR: return 'c'; + case S_IFIFO: return 'p'; + default: return '?'; + } +} + +static void mode2str(unsigned mode, char *out) +{ + *out++ = mode2kind(mode); + + *out++ = (mode & 0400) ? 'r' : '-'; + *out++ = (mode & 0200) ? 'w' : '-'; + if(mode & 04000) { + *out++ = (mode & 0100) ? 's' : 'S'; + } else { + *out++ = (mode & 0100) ? 'x' : '-'; + } + *out++ = (mode & 040) ? 'r' : '-'; + *out++ = (mode & 020) ? 'w' : '-'; + if(mode & 02000) { + *out++ = (mode & 010) ? 's' : 'S'; + } else { + *out++ = (mode & 010) ? 'x' : '-'; + } + *out++ = (mode & 04) ? 'r' : '-'; + *out++ = (mode & 02) ? 'w' : '-'; + if(mode & 01000) { + *out++ = (mode & 01) ? 't' : 'T'; + } else { + *out++ = (mode & 01) ? 'x' : '-'; + } + *out = 0; +} + +static void user2str(unsigned uid, char *out) +{ + struct passwd *pw = getpwuid(uid); + if(pw) { + strcpy(out, pw->pw_name); + } else { + sprintf(out, "%d", uid); + } +} + +static void group2str(unsigned gid, char *out) +{ + struct group *gr = getgrgid(gid); + if(gr) { + strcpy(out, gr->gr_name); + } else { + sprintf(out, "%d", gid); + } +} + +static int show_total_size(const char *dirname, DIR *d, int flags) +{ + struct dirent *de; + char tmp[1024]; + struct stat s; + int sum = 0; + + /* run through the directory and sum up the file block sizes */ + while ((de = readdir(d)) != 0) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) + continue; + + if (strcmp(dirname, "/") == 0) + snprintf(tmp, sizeof(tmp), "/%s", de->d_name); + else + snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name); + + if (lstat(tmp, &s) < 0) { + fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno)); + rewinddir(d); + return -1; + } + + sum += s.st_blocks / 2; + } + + printf("total %d\n", sum); + rewinddir(d); + return 0; +} + +static int listfile_size(const char *path, const char *filename, int flags) +{ + struct stat s; + + if (lstat(path, &s) < 0) { + fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno)); + return -1; + } + + /* blocks are 512 bytes, we want output to be KB */ + if ((flags & LIST_SIZE) != 0) { + printf("%lld ", s.st_blocks / 2); + } + + if ((flags & LIST_CLASSIFY) != 0) { + char filetype = mode2kind(s.st_mode); + if (filetype != 'l') { + printf("%c ", filetype); + } else { + struct stat link_dest; + if (!stat(path, &link_dest)) { + printf("l%c ", mode2kind(link_dest.st_mode)); + } else { + fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno)); + printf("l? "); + } + } + } + + printf("%s\n", filename); + + return 0; +} + +static int listfile_long(const char *path, int flags) +{ + struct stat s; + char date[32]; + char mode[16]; + char user[16]; + char group[16]; + const char *name; + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + if(lstat(path, &s) < 0) { + return -1; + } + + mode2str(s.st_mode, mode); + if (flags & LIST_LONG_NUMERIC) { + sprintf(user, "%ld", s.st_uid); + sprintf(group, "%ld", s.st_gid); + } else { + user2str(s.st_uid, user); + group2str(s.st_gid, group); + } + + strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); + date[31] = 0; + +// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) + + switch(s.st_mode & S_IFMT) { + case S_IFBLK: + case S_IFCHR: + printf("%s %-8s %-8s %3d, %3d %s %s\n", + mode, user, group, + (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), + date, name); + break; + case S_IFREG: + printf("%s %-8s %-8s %8ld %s %s\n", + mode, user, group, s.st_size, date, name); + break; + case S_IFLNK: { + char linkto[256]; + int len; + + len = readlink(path, linkto, 256); + if(len < 0) return -1; + + if(len > 255) { + linkto[252] = '.'; + linkto[253] = '.'; + linkto[254] = '.'; + linkto[255] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, date, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, date, name); + + } + return 0; +} + +static int listfile_maclabel(const char *path, int flags) +{ + struct stat s; + char mode[16]; + char user[16]; + char group[16]; + char *maclabel = NULL; + const char *name; + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + if(lstat(path, &s) < 0) { + return -1; + } + +#ifdef HAVE_SELINUX + lgetfilecon(path, &maclabel); +#else + maclabel = strdup("-"); +#endif + if (!maclabel) { + return -1; + } + + mode2str(s.st_mode, mode); + user2str(s.st_uid, user); + group2str(s.st_gid, group); + + switch(s.st_mode & S_IFMT) { + case S_IFLNK: { + char linkto[256]; + int len; + + len = readlink(path, linkto, sizeof(linkto)); + if(len < 0) return -1; + + if(len > sizeof(linkto)-1) { + linkto[sizeof(linkto)-4] = '.'; + linkto[sizeof(linkto)-3] = '.'; + linkto[sizeof(linkto)-2] = '.'; + linkto[sizeof(linkto)-1] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, maclabel, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, maclabel, name); + + } + + free(maclabel); + + return 0; +} + +static int listfile(const char *dirname, const char *filename, int flags) +{ + if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) { + printf("%s\n", filename); + return 0; + } + + char tmp[4096]; + const char* pathname = filename; + + if (dirname != NULL) { + snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename); + pathname = tmp; + } else { + pathname = filename; + } + + if ((flags & LIST_MACLABEL) != 0) { + return listfile_maclabel(pathname, flags); + } else if ((flags & LIST_LONG) != 0) { + return listfile_long(pathname, flags); + } else /*((flags & LIST_SIZE) != 0)*/ { + return listfile_size(pathname, filename, flags); + } +} + +static int listdir(const char *name, int flags) +{ + char tmp[4096]; + DIR *d; + struct dirent *de; + strlist_t files = STRLIST_INITIALIZER; + + d = opendir(name); + if(d == 0) { + fprintf(stderr, "opendir failed, %s\n", strerror(errno)); + return -1; + } + + if ((flags & LIST_SIZE) != 0) { + show_total_size(name, d, flags); + } + + while((de = readdir(d)) != 0){ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; + if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; + + strlist_append_dup(&files, de->d_name); + } + + strlist_sort(&files); + STRLIST_FOREACH(&files, filename, listfile(name, filename, flags)); + strlist_done(&files); + + if (flags & LIST_RECURSIVE) { + strlist_t subdirs = STRLIST_INITIALIZER; + + rewinddir(d); + + while ((de = readdir(d)) != 0) { + struct stat s; + int err; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) + continue; + + if (!strcmp(name, "/")) + snprintf(tmp, sizeof(tmp), "/%s", de->d_name); + else + snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name); + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (tmp[strlen(tmp)-1] == '/') + err = stat(tmp, &s); + else + err = lstat(tmp, &s); + + if (err < 0) { + perror(tmp); + closedir(d); + return -1; + } + + if (S_ISDIR(s.st_mode)) { + strlist_append_dup(&subdirs, tmp); + } + } + strlist_sort(&subdirs); + STRLIST_FOREACH(&subdirs, path, { + printf("\n%s:\n", path); + listdir(path, flags); + }); + strlist_done(&subdirs); + } + + closedir(d); + return 0; +} + +static int listpath(const char *name, int flags) +{ + struct stat s; + int err; + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (name[strlen(name)-1] == '/') + err = stat(name, &s); + else + err = lstat(name, &s); + + if (err < 0) { + perror(name); + return -1; + } + + if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) { + if (flags & LIST_RECURSIVE) + printf("\n%s:\n", name); + return listdir(name, flags); + } else { + /* yeah this calls stat() again*/ + return listfile(NULL, name, flags); + } +} + +int ls_main(int argc, char **argv) +{ + int flags = 0; + int listed = 0; + + if(argc > 1) { + int i; + int err = 0; + strlist_t files = STRLIST_INITIALIZER; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* an option ? */ + const char *arg = argv[i]+1; + while (arg[0]) { + switch (arg[0]) { + case 'l': flags |= LIST_LONG; break; + case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break; + case 's': flags |= LIST_SIZE; break; + case 'R': flags |= LIST_RECURSIVE; break; + case 'd': flags |= LIST_DIRECTORIES; break; + case 'Z': flags |= LIST_MACLABEL; break; + case 'a': flags |= LIST_ALL; break; + case 'F': flags |= LIST_CLASSIFY; break; + default: + fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]); + exit(1); + } + arg++; + } + } else { + /* not an option ? */ + strlist_append_dup(&files, argv[i]); + } + } + + if (files.count > 0) { + STRLIST_FOREACH(&files, path, { + if (listpath(path, flags) != 0) { + err = EXIT_FAILURE; + } + }); + strlist_done(&files); + return err; + } + } + + // list working directory if no files or directories were specified + return listpath(".", flags); +}
diff --git a/toolbox/lsmod.c b/toolbox/lsmod.c new file mode 100644 index 0000000..8b55ee6 --- /dev/null +++ b/toolbox/lsmod.c
@@ -0,0 +1,10 @@ +#include <stdio.h> + +extern int cat_main(int argc, char **argv); + +int lsmod_main(int argc, char **argv) +{ + char *cat_argv[] = { "cat", "/proc/modules", NULL }; + return cat_main(2, cat_argv); +} +
diff --git a/toolbox/lsof.c b/toolbox/lsof.c new file mode 100644 index 0000000..4e2f77a --- /dev/null +++ b/toolbox/lsof.c
@@ -0,0 +1,254 @@ +/* + * Copyright (c) 2010, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <pwd.h> +#include <sys/stat.h> + +#define BUF_MAX 1024 +#define CMD_DISPLAY_MAX (9 + 1) +#define USER_DISPLAY_MAX (10 + 1) + +struct pid_info_t { + pid_t pid; + char user[USER_DISPLAY_MAX]; + + char cmdline[CMD_DISPLAY_MAX]; + + char path[PATH_MAX]; + ssize_t parent_length; +}; + +void print_header() +{ + printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n", + "COMMAND", + "PID", + "USER", + "FD", + "TYPE", + "DEVICE", + "SIZE/OFF", + "NODE", + "NAME"); +} + +void print_type(char *type, struct pid_info_t* info) +{ + static ssize_t link_dest_size; + static char link_dest[PATH_MAX]; + + strncat(info->path, type, sizeof(info->path)); + if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) { + if (errno == ENOENT) + goto out; + + snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno)); + } else { + link_dest[link_dest_size] = '\0'; + } + + // Things that are just the root filesystem are uninteresting (we already know) + if (!strcmp(link_dest, "/")) + goto out; + + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", + info->cmdline, info->pid, info->user, type, + "???", "???", "???", "???", link_dest); + +out: + info->path[info->parent_length] = '\0'; +} + +// Prints out all file that have been memory mapped +void print_maps(struct pid_info_t* info) +{ + FILE *maps; + char buffer[PATH_MAX + 100]; + + size_t offset; + int major, minor; + char device[10]; + long int inode; + char file[PATH_MAX]; + + strncat(info->path, "maps", sizeof(info->path)); + + maps = fopen(info->path, "r"); + if (!maps) + goto out; + + while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode, + file) == 4) { + // We don't care about non-file maps + if (inode == 0 || !strcmp(device, "00:00")) + continue; + + printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", + info->cmdline, info->pid, info->user, "mem", + "???", device, offset, inode, file); + } + + fclose(maps); + +out: + info->path[info->parent_length] = '\0'; +} + +// Prints out all open file descriptors +void print_fds(struct pid_info_t* info) +{ + static char* fd_path = "fd/"; + strncat(info->path, fd_path, sizeof(info->path)); + + int previous_length = info->parent_length; + info->parent_length += strlen(fd_path); + + DIR *dir = opendir(info->path); + if (dir == NULL) { + char msg[BUF_MAX]; + snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno)); + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", + info->cmdline, info->pid, info->user, "FDS", + "", "", "", "", msg); + goto out; + } + + struct dirent* de; + while ((de = readdir(dir))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + print_type(de->d_name, info); + } + closedir(dir); + +out: + info->parent_length = previous_length; + info->path[info->parent_length] = '\0'; +} + +void lsof_dumpinfo(pid_t pid) +{ + int fd; + struct pid_info_t info; + struct stat pidstat; + struct passwd *pw; + + info.pid = pid; + snprintf(info.path, sizeof(info.path), "/proc/%d/", pid); + info.parent_length = strlen(info.path); + + // Get the UID by calling stat on the proc/pid directory. + if (!stat(info.path, &pidstat)) { + pw = getpwuid(pidstat.st_uid); + if (pw) { + strncpy(info.user, pw->pw_name, USER_DISPLAY_MAX - 1); + info.user[USER_DISPLAY_MAX - 1] = '\0'; + } else { + snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid); + } + } else { + strcpy(info.user, "???"); + } + + // Read the command line information; each argument is terminated with NULL. + strncat(info.path, "cmdline", sizeof(info.path)); + fd = open(info.path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Couldn't read %s\n", info.path); + return; + } + char cmdline[PATH_MAX]; + if (read(fd, cmdline, sizeof(cmdline)) < 0) { + fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno)); + close(fd); + return; + } + close(fd); + info.path[info.parent_length] = '\0'; + + // We only want the basename of the cmdline + strncpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); + info.cmdline[sizeof(info.cmdline)-1] = '\0'; + + // Read each of these symlinks + print_type("cwd", &info); + print_type("exe", &info); + print_type("root", &info); + + print_fds(&info); + print_maps(&info); +} + +int lsof_main(int argc, char *argv[]) +{ + long int pid = 0; + char* endptr; + if (argc == 2) { + pid = strtol(argv[1], &endptr, 10); + } + + print_header(); + + if (pid) { + lsof_dumpinfo(pid); + } else { + DIR *dir = opendir("/proc"); + if (dir == NULL) { + fprintf(stderr, "Couldn't open /proc\n"); + return -1; + } + + struct dirent* de; + while ((de = readdir(dir))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + // Only inspect directories that are PID numbers + pid = strtol(de->d_name, &endptr, 10); + if (*endptr != '\0') + continue; + + lsof_dumpinfo(pid); + } + closedir(dir); + } + + return 0; +}
diff --git a/toolbox/lsusb.c b/toolbox/lsusb.c new file mode 100644 index 0000000..236e74b --- /dev/null +++ b/toolbox/lsusb.c
@@ -0,0 +1,227 @@ +/* + * 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 <endian.h> +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include <usbhost/usbhost.h> + +static int verbose = 0; +static char str_buff[4096]; + +static const char *get_str(struct usb_device *dev, int id) +{ + char *str = usb_device_get_string(dev, id); + + if (id && str) { + strlcpy(str_buff, str, sizeof(str_buff)); + free(str); + } else { + snprintf(str_buff, sizeof(str_buff), "%02x", id); + } + + return str_buff; +} + + +static void lsusb_parse_device_descriptor(struct usb_device *dev, + struct usb_device_descriptor *desc) +{ + printf(" Device Descriptor\n"); + printf("\tbcdUSB: %04x\n", letoh16(desc->bcdUSB)); + printf("\tbDeviceClass: %02x\n", desc->bDeviceClass); + printf("\tbDeviceSubClass: %02x\n", desc->bDeviceSubClass); + printf("\tbDeviceProtocol: %02x\n", desc->bDeviceProtocol); + printf("\tbMaxPacketSize0: %02x\n", desc->bMaxPacketSize0); + printf("\tidVendor: %04x\n", letoh16(desc->idVendor)); + printf("\tidProduct: %04x\n", letoh16(desc->idProduct)); + printf("\tbcdDevice: %04x\n", letoh16(desc->bcdDevice)); + printf("\tiManufacturer: %s\n", get_str(dev, desc->iManufacturer)); + printf("\tiProduct: %s\n", get_str(dev, desc->iProduct)); + printf("\tiSerialNumber: %s\n", get_str(dev,desc->iSerialNumber)); + printf("\tbNumConfiguration: %02x\n", desc->bNumConfigurations); + printf("\n"); +} + +static void lsusb_parse_config_descriptor(struct usb_device *dev, + struct usb_config_descriptor *desc) +{ + printf(" Config Descriptor\n"); + printf("\twTotalLength: %04x\n", letoh16(desc->wTotalLength)); + printf("\tbNumInterfaces: %02x\n", desc->bNumInterfaces); + printf("\tbConfigurationValue: %02x\n", desc->bConfigurationValue); + printf("\tiConfiguration: %s\n", get_str(dev, desc->iConfiguration)); + printf("\tbmAttributes: %02x\n", desc->bmAttributes); + printf("\tbMaxPower: %d mA\n", desc->bMaxPower * 2); + printf("\n"); +} + +static void lsusb_parse_interface_descriptor(struct usb_device *dev, + struct usb_interface_descriptor *desc) +{ + printf(" Interface Descriptor\n"); + printf("\tbInterfaceNumber: %02x\n", desc->bInterfaceNumber); + printf("\tbAlternateSetting: %02x\n", desc->bAlternateSetting); + printf("\tbNumEndpoints: %02x\n", desc->bNumEndpoints); + printf("\tbInterfaceClass: %02x\n", desc->bInterfaceClass); + printf("\tbInterfaceSubClass: %02x\n", desc->bInterfaceSubClass); + printf("\tbInterfaceProtocol: %02x\n", desc->bInterfaceProtocol); + printf("\tiInterface: %s\n", get_str(dev, desc->iInterface)); + printf("\n"); +} + +static void lsusb_parse_endpoint_descriptor(struct usb_device *dev, + struct usb_endpoint_descriptor *desc) +{ + printf(" Endpoint Descriptor\n"); + printf("\tbEndpointAddress: %02x\n", desc->bEndpointAddress); + printf("\tbmAttributes: %02x\n", desc->bmAttributes); + printf("\twMaxPacketSize: %02x\n", letoh16(desc->wMaxPacketSize)); + printf("\tbInterval: %02x\n", desc->bInterval); + printf("\tbRefresh: %02x\n", desc->bRefresh); + printf("\tbSynchAddress: %02x\n", desc->bSynchAddress); + printf("\n"); +} + +static void lsusb_dump_descriptor(struct usb_device *dev, + struct usb_descriptor_header *desc) +{ + int i; + printf(" Descriptor type %02x\n", desc->bDescriptorType); + + for (i = 0; i < desc->bLength; i++ ) { + if ((i % 16) == 0) + printf("\t%02x:", i); + printf(" %02x", ((uint8_t *)desc)[i]); + if ((i % 16) == 15) + printf("\n"); + } + + if ((i % 16) != 0) + printf("\n"); + printf("\n"); +} + +static void lsusb_parse_descriptor(struct usb_device *dev, + struct usb_descriptor_header *desc) +{ + switch (desc->bDescriptorType) { + case USB_DT_DEVICE: + lsusb_parse_device_descriptor(dev, (struct usb_device_descriptor *) desc); + break; + + case USB_DT_CONFIG: + lsusb_parse_config_descriptor(dev, (struct usb_config_descriptor *) desc); + break; + + case USB_DT_INTERFACE: + lsusb_parse_interface_descriptor(dev, (struct usb_interface_descriptor *) desc); + break; + + case USB_DT_ENDPOINT: + lsusb_parse_endpoint_descriptor(dev, (struct usb_endpoint_descriptor *) desc); + break; + + default: + lsusb_dump_descriptor(dev, desc); + + break; + } +} + +static int lsusb_device_added(const char *dev_name, void *client_data) +{ + struct usb_device *dev = usb_device_open(dev_name); + + if (!dev) { + fprintf(stderr, "can't open device %s: %s\n", dev_name, strerror(errno)); + return 0; + } + + if (verbose) { + struct usb_descriptor_iter iter; + struct usb_descriptor_header *desc; + + printf("%s:\n", dev_name); + + usb_descriptor_iter_init(dev, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) + lsusb_parse_descriptor(dev, desc); + + } else { + uint16_t vid, pid; + char *mfg_name, *product_name, *serial; + + vid = usb_device_get_vendor_id(dev); + pid = usb_device_get_product_id(dev); + mfg_name = usb_device_get_manufacturer_name(dev); + product_name = usb_device_get_product_name(dev); + serial = usb_device_get_serial(dev); + + printf("%s: %04x:%04x %s %s %s\n", dev_name, vid, pid, + mfg_name, product_name, serial); + + free(mfg_name); + free(product_name); + free(serial); + } + + usb_device_close(dev); + + return 0; +} + +static int lsusb_device_removed(const char *dev_name, void *client_data) +{ + return 0; +} + + +static int lsusb_discovery_done(void *client_data) +{ + return 1; +} + + + +int lsusb_main(int argc, char **argv) +{ + struct usb_host_context *ctx; + + if (argc == 2 && !strcmp(argv[1], "-v")) + verbose = 1; + + ctx = usb_host_init(); + if (!ctx) { + perror("usb_host_init:"); + return 1; + } + + usb_host_run(ctx, + lsusb_device_added, + lsusb_device_removed, + lsusb_discovery_done, + NULL); + + usb_host_cleanup(ctx); + + return 0; +} +
diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c new file mode 100644 index 0000000..c81d1db --- /dev/null +++ b/toolbox/mkdir.c
@@ -0,0 +1,80 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <cutils/memory.h> + +static int usage() +{ + fprintf(stderr,"mkdir [OPTION] <target>\n"); + fprintf(stderr," --help display usage and exit\n"); + fprintf(stderr," -p, --parents create parent directories as needed\n"); + return -1; +} + +int mkdir_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2 || strcmp(argv[1], "--help") == 0) { + return usage(); + } + + int recursive = (strcmp(argv[1], "-p") == 0 || + strcmp(argv[1], "--parents") == 0) ? 1 : 0; + + if(recursive && argc < 3) { + // -p specified without a path + return usage(); + } + + if(recursive) { + argc--; + argv++; + } + + char currpath[PATH_MAX], *pathpiece; + struct stat st; + + while(argc > 1) { + argc--; + argv++; + if(recursive) { + // reset path + strcpy(currpath, ""); + // create the pieces of the path along the way + pathpiece = strtok(argv[0], "/"); + if(argv[0][0] == '/') { + // prepend / if needed + strcat(currpath, "/"); + } + while(pathpiece != NULL) { + if(strlen(currpath) + strlen(pathpiece) + 2/*NUL and slash*/ > PATH_MAX) { + fprintf(stderr, "Invalid path specified: too long\n"); + return 1; + } + strcat(currpath, pathpiece); + strcat(currpath, "/"); + if(stat(currpath, &st) != 0) { + ret = mkdir(currpath, 0777); + if(ret < 0) { + fprintf(stderr, "mkdir failed for %s, %s\n", currpath, strerror(errno)); + return ret; + } + } + pathpiece = strtok(NULL, "/"); + } + } else { + ret = mkdir(argv[0], 0777); + if(ret < 0) { + fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno)); + return ret; + } + } + } + + return 0; +}
diff --git a/toolbox/mknod.c b/toolbox/mknod.c new file mode 100644 index 0000000..0d2131d --- /dev/null +++ b/toolbox/mknod.c
@@ -0,0 +1,100 @@ +/* + * Copyright (c) 20012, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +// if glibc version >= 2.25 +#include <sys/sysmacros.h> + +static void usage(char *cmd); + +const struct { + char name; + mode_t mode; +} modes_table [] = { + {'b', S_IFBLK}, + {'c', S_IFCHR}, + {'u', S_IFCHR}, + {'p', S_IFIFO} +}; + +int mknod_main(int argc, char *argv[]) { + mode_t mode = 0666; + char *name; + dev_t dev = 0; + int i, total; + + if ((argc != 5) && (argc != 3)) { + usage(argv[0]); + return -1; + } + + name = argv[1]; + total = sizeof(modes_table) / sizeof(modes_table[0]); + for (i = 0; i < total; i ++) { + if (*argv[2] == modes_table[i].name) { + mode |= modes_table[i].mode; + break; + } + } + + if (i == total) { + usage(argv[0]); + return -1; + } + if (argc == 5) { + dev = makedev(atoi(argv[3]), atoi(argv[4])); + } + if (mknod(name, mode, dev)) { + fprintf(stderr, "mknod error: %s\n", strerror(errno)); + } + return 0; +} + +static void usage(char *cmd) { + fprintf(stderr, "Usage: %s NAME TYPE [MAJOR MINOR]\n" + "b create a block (buffered) special file\n" + "c, u create a character (unbuffered) special file" + "p create a FIFO" + , cmd); +} + +#ifdef HOST +int main(int argc, char *argv[]) +{ + return mknod_main(argc, argv); +} +#endif
diff --git a/toolbox/mount.c b/toolbox/mount.c new file mode 100644 index 0000000..b570bdc --- /dev/null +++ b/toolbox/mount.c
@@ -0,0 +1,347 @@ +/* + * mount.c, by rmk + */ + +#include <sys/mount.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/loop.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define DEFAULT_LOOP_DEVICE "/dev/block/loop0" +#define LOOPDEV_MAXLEN 64 + +struct mount_opts { + const char str[8]; + unsigned long rwmask; + unsigned long rwset; + unsigned long rwnoset; +}; + +struct extra_opts { + char *str; + char *end; + int used_size; + int alloc_size; +}; + +/* Newer mount options which may not be defined in a non-bionic libc; + * copied from bionic's sys/mount.h */ +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif +#ifndef MS_REC +#define MS_REC 16384 +#endif +#ifndef MS_VERBOSE +#define MS_VERBOSE 32768 +#endif + +/* + * These options define the function of "mount(2)". + */ +#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) + + +static const struct mount_opts options[] = { + /* name mask set noset */ + { "async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS }, + { "atime", MS_NOATIME, 0, MS_NOATIME }, + { "bind", MS_TYPE, MS_BIND, 0, }, + { "dev", MS_NODEV, 0, MS_NODEV }, + { "diratime", MS_NODIRATIME, 0, MS_NODIRATIME }, + { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0 }, + { "exec", MS_NOEXEC, 0, MS_NOEXEC }, + { "move", MS_TYPE, MS_MOVE, 0 }, + { "recurse", MS_REC, MS_REC, 0 }, + { "remount", MS_TYPE, MS_REMOUNT, 0 }, + { "ro", MS_RDONLY, MS_RDONLY, 0 }, + { "rw", MS_RDONLY, 0, MS_RDONLY }, + { "suid", MS_NOSUID, 0, MS_NOSUID }, + { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 }, + { "verbose", MS_VERBOSE, MS_VERBOSE, 0 }, +}; + +static void add_extra_option(struct extra_opts *extra, char *s) +{ + int len = strlen(s); + int newlen = extra->used_size + len; + + if (extra->str) + len++; /* +1 for ',' */ + + if (newlen >= extra->alloc_size) { + char *new; + + new = realloc(extra->str, newlen + 1); /* +1 for NUL */ + if (!new) + return; + + extra->str = new; + extra->end = extra->str + extra->used_size; + extra->alloc_size = newlen; + } + + if (extra->used_size) { + *extra->end = ','; + extra->end++; + } + strcpy(extra->end, s); + extra->used_size += len; + +} + +static unsigned long +parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev) +{ + char *s; + + *loop = 0; + while ((s = strsep(&arg, ",")) != NULL) { + char *opt = s; + unsigned int i; + int res, no = s[0] == 'n' && s[1] == 'o'; + + if (no) + s += 2; + + if (strncmp(s, "loop=", 5) == 0) { + *loop = 1; + strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN); + continue; + } + + if (strcmp(s, "loop") == 0) { + *loop = 1; + strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN); + continue; + } + for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { + res = strcmp(s, options[i].str); + + if (res == 0) { + rwflag &= ~options[i].rwmask; + if (no) + rwflag |= options[i].rwnoset; + else + rwflag |= options[i].rwset; + } + if (res <= 0) + break; + } + + if (res != 0 && s[0]) + add_extra_option(extra, opt); + } + + return rwflag; +} + +static char *progname; + +static struct extra_opts extra; +static unsigned long rwflag; + +static int +do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop, + char *loopdev) +{ + char *s; + int error = 0; + + if (loop) { + int file_fd, device_fd; + int flags; + + flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR; + + file_fd = open(dev, flags); + if (file_fd < 0) { + perror("open backing file failed"); + return 1; + } + device_fd = open(loopdev, flags); + if (device_fd < 0) { + perror("open loop device failed"); + close(file_fd); + return 1; + } + if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) { + perror("ioctl LOOP_SET_FD failed"); + close(file_fd); + close(device_fd); + return 1; + } + + close(file_fd); + close(device_fd); + dev = loopdev; + } + + while ((s = strsep(&type, ",")) != NULL) { +retry: + if (mount(dev, dir, s, rwflag, data) == -1) { + error = errno; + /* + * If the filesystem is not found, or the + * superblock is invalid, try the next. + */ + if (error == ENODEV || error == EINVAL) + continue; + + /* + * If we get EACCESS, and we're trying to + * mount readwrite and this isn't a remount, + * try read only. + */ + if (error == EACCES && + (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) { + rwflag |= MS_RDONLY; + goto retry; + } + break; + } + } + + if (error) { + errno = error; + perror("mount"); + return 255; + } + + return 0; +} + +static int print_mounts() +{ + FILE* f; + int length; + char buffer[100]; + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stdout, "could not open /proc/mounts\n"); + return -1; + } + + do { + length = fread(buffer, 1, 100, f); + if (length > 0) + fwrite(buffer, 1, length, stdout); + } while (length > 0); + + fclose(f); + return 0; +} + +static int get_mounts_dev_dir(const char *arg, char **dev, char **dir) +{ + FILE *f; + char mount_dev[256]; + char mount_dir[256]; + char mount_type[256]; + char mount_opts[256]; + int mount_freq; + int mount_passno; + int match; + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stdout, "could not open /proc/mounts\n"); + return -1; + } + + do { + match = fscanf(f, "%255s %255s %255s %255s %d %d\n", + mount_dev, mount_dir, mount_type, + mount_opts, &mount_freq, &mount_passno); + mount_dev[255] = 0; + mount_dir[255] = 0; + mount_type[255] = 0; + mount_opts[255] = 0; + if (match == 6 && + (strcmp(arg, mount_dev) == 0 || + strcmp(arg, mount_dir) == 0)) { + *dev = strdup(mount_dev); + *dir = strdup(mount_dir); + fclose(f); + return 0; + } + } while (match != EOF); + + fclose(f); + return -1; +} + +int mount_main(int argc, char *argv[]) +{ + char *type = NULL; + char *dev = NULL; + char *dir = NULL; + int c; + int loop = 0; + char loopdev[LOOPDEV_MAXLEN]; + + progname = argv[0]; + rwflag = MS_VERBOSE; + + // mount with no arguments is equivalent to "cat /proc/mounts" + if (argc == 1) return print_mounts(); + + do { + c = getopt(argc, argv, "o:rt:w"); + if (c == EOF) + break; + switch (c) { + case 'o': + rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev); + break; + case 'r': + rwflag |= MS_RDONLY; + break; + case 't': + type = optarg; + break; + case 'w': + rwflag &= ~MS_RDONLY; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + exit(1); + } + } while (1); + + /* + * If remount, bind or move was specified, then we don't + * have a "type" as such. Use the dummy "none" type. + */ + if (rwflag & MS_TYPE) + type = "none"; + + if (optind + 2 == argc) { + dev = argv[optind]; + dir = argv[optind + 1]; + } else if (optind + 1 == argc && rwflag & MS_REMOUNT) { + get_mounts_dev_dir(argv[optind], &dev, &dir); + } + + if (dev == NULL || dir == NULL || type == NULL) { + fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] " + "device directory\n", progname); + exit(1); + } + + return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev); + /* We leak dev and dir in some cases, but we're about to exit */ +}
diff --git a/toolbox/mv.c b/toolbox/mv.c new file mode 100644 index 0000000..a5bc225 --- /dev/null +++ b/toolbox/mv.c
@@ -0,0 +1,59 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + + +int mv_main(int argc, char *argv[]) +{ + const char* dest; + struct stat st; + int i; + + if (argc < 3) { + fprintf(stderr,"USAGE: %s <source...> <destination>\n", argv[0]); + return -1; + } + + /* check if destination exists */ + dest = argv[argc - 1]; + if (stat(dest, &st)) { + /* an error, unless the destination was missing */ + if (errno != ENOENT) { + fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno)); + return -1; + } + st.st_mode = 0; + } + + for (i = 1; i < argc - 1; i++) { + const char *source = argv[i]; + char fullDest[PATH_MAX + 1 + PATH_MAX + 1]; + /* assume we build "dest/source", and let rename() fail on pathsize */ + if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) { + fprintf(stderr, "path too long\n"); + return -1; + } + strcpy(fullDest, dest); + + /* if destination is a directory, concat the source file name */ + if (S_ISDIR(st.st_mode)) { + const char *fileName = strrchr(source, '/'); + if (fullDest[strlen(fullDest)-1] != '/') { + strcat(fullDest, "/"); + } + strcat(fullDest, fileName ? fileName + 1 : source); + } + + /* attempt to move it */ + if (rename(source, fullDest)) { + fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno)); + return -1; + } + } + + return 0; +} +
diff --git a/toolbox/nandread.c b/toolbox/nandread.c new file mode 100644 index 0000000..cd37630 --- /dev/null +++ b/toolbox/nandread.c
@@ -0,0 +1,292 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <linux/version.h> +#include <mtd/mtd-user.h> +#include <sys/ioctl.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +# define MTD_MODE_RAW MTD_FILE_MODE_RAW +#endif + +static int test_empty(const char *buf, size_t size) +{ + while(size--) { + if (*buf++ != 0xff) + return 0; + } + return 1; +} + +int nandread_main(int argc, char **argv) +{ + char *devname = NULL; + char *filename = NULL; + char *statusfilename = NULL; + char *statusext = ".stat"; + int fd; + int outfd = -1; + FILE *statusfile = NULL; + int ret; + int verbose = 0; + void *buffer; + loff_t pos, opos, end, bpos; + loff_t start = 0, len = 0; + int c; + int i; + int empty_pages = 0; + int page_count = 0; + int bad_block; + int rawmode = 0; + uint32_t *oob_data; + uint8_t *oob_fixed; + size_t spare_size = 64; + struct mtd_info_user mtdinfo; + struct mtd_ecc_stats initial_ecc, last_ecc, ecc; + struct mtd_oob_buf oobbuf; + nand_ecclayout_t ecclayout; + + do { + c = getopt(argc, argv, "d:f:s:S:L:Rhv"); + if (c == EOF) + break; + switch (c) { + case 'd': + devname = optarg; + break; + case 'f': + filename = optarg; + break; + case 's': + spare_size = atoi(optarg); + break; + case 'S': + start = strtoll(optarg, NULL, 0); + break; + case 'L': + len = strtoll(optarg, NULL, 0); + break; + case 'R': + rawmode = 1; + break; + case 'v': + verbose++; + break; + case 'h': + fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n" + " -d <dev> Read from <dev>\n" + " -f <file> Write to <file>\n" + " -s <size> Number of spare bytes in file (default 64)\n" + " -R Raw mode\n" + " -S <start> Start offset (default 0)\n" + " -L <len> Length (default 0)\n" + " -v Print info\n" + " -h Print help\n", argv[0]); + return -1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (optind < argc) { + fprintf(stderr, "%s: extra arguments\n", argv[0]); + return 1; + } + if (!devname) { + fprintf(stderr, "%s: specify device name\n", argv[0]); + return 1; + } + + fd = open(devname, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno)); + return 1; + } + + if (filename) { + outfd = creat(filename, 0666); + if (outfd < 0) { + fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno)); + return 1; + } + statusfilename = malloc(strlen(filename) + strlen(statusext) + 1); + strcpy(statusfilename, filename); + strcat(statusfilename, statusext); + statusfile = fopen(statusfilename, "w+"); + if (!statusfile) { + fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno)); + return 1; + } + } + + ret = ioctl(fd, MEMGETINFO, &mtdinfo); + if (ret) { + fprintf(stderr, "failed get mtd info for %s, %s\n", + devname, strerror(errno)); + return 1; + } + + if (verbose) { + printf("size: %u\n", mtdinfo.size); + printf("erase size: %u\n", mtdinfo.erasesize); + printf("write size: %u\n", mtdinfo.writesize); + printf("oob size: %u\n", mtdinfo.oobsize); + } + + buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size); + if (!buffer) { + fprintf(stderr, "failed allocate readbuffer size %u\n", + mtdinfo.writesize + mtdinfo.oobsize); + return 1; + } + + oobbuf.length = mtdinfo.oobsize; + oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize); + memset(oob_data, 0xff, mtdinfo.oobsize + spare_size); + oobbuf.ptr = (uint8_t *)oob_data + spare_size; + + ret = ioctl(fd, ECCGETLAYOUT, &ecclayout); + if (ret) { + fprintf(stderr, "failed get ecc layout for %s, %s\n", + devname, strerror(errno)); + return 1; + } + if (verbose) { + printf("ecc bytes: %u\n", ecclayout.eccbytes); + printf("oobavail: %u\n", ecclayout.oobavail); + } + if (ecclayout.oobavail > spare_size) + printf("oobavail, %d > image spare size, %d\n", ecclayout.oobavail, spare_size); + + ret = ioctl(fd, ECCGETSTATS, &initial_ecc); + if (ret) { + fprintf(stderr, "failed get ecc stats for %s, %s\n", + devname, strerror(errno)); + return 1; + } + last_ecc = initial_ecc; + + if (verbose) { + printf("initial ecc corrected: %u\n", initial_ecc.corrected); + printf("initial ecc failed: %u\n", initial_ecc.failed); + printf("initial ecc badblocks: %u\n", initial_ecc.badblocks); + printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks); + } + + if (rawmode) { + rawmode = mtdinfo.oobsize; + ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); + if (ret) { + fprintf(stderr, "failed set raw mode for %s, %s\n", + devname, strerror(errno)); + return 1; + } + } + + end = len ? (start + len) : mtdinfo.size; + for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) { + bad_block = 0; + if (verbose > 3) + printf("reading at %llx\n", pos); + lseek64(fd, pos, SEEK_SET); + ret = read(fd, buffer, mtdinfo.writesize + rawmode); + if (ret < (int)mtdinfo.writesize) { + fprintf(stderr, "short read at %llx, %d\n", pos, ret); + bad_block = 2; + } + if (!rawmode) { + oobbuf.start = pos; + ret = ioctl(fd, MEMREADOOB, &oobbuf); + if (ret) { + fprintf(stderr, "failed to read oob data at %llx, %d\n", pos, ret); + bad_block = 2; + } + } + ret = ioctl(fd, ECCGETSTATS, &ecc); + if (ret) { + fprintf(stderr, "failed get ecc stats for %s, %s\n", + devname, strerror(errno)); + return 1; + } + bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize; + ret = ioctl(fd, MEMGETBADBLOCK, &bpos); + if (ret && errno != EOPNOTSUPP) { + printf("badblock at %llx\n", pos); + bad_block = 1; + } + if (ecc.corrected != last_ecc.corrected) + printf("ecc corrected, %u, at %llx\n", ecc.corrected - last_ecc.corrected, pos); + if (ecc.failed != last_ecc.failed) + printf("ecc failed, %u, at %llx\n", ecc.failed - last_ecc.failed, pos); + if (ecc.badblocks != last_ecc.badblocks) + printf("ecc badblocks, %u, at %llx\n", ecc.badblocks - last_ecc.badblocks, pos); + if (ecc.bbtblocks != last_ecc.bbtblocks) + printf("ecc bbtblocks, %u, at %llx\n", ecc.bbtblocks - last_ecc.bbtblocks, pos); + + if (!rawmode) { + oob_fixed = (uint8_t *)oob_data; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { + int len = ecclayout.oobfree[i].length; + if (oob_fixed + len > oobbuf.ptr) + len = oobbuf.ptr - oob_fixed; + if (len) { + memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len); + oob_fixed += len; + } + } + } + + if (outfd >= 0) { + ret = write(outfd, buffer, mtdinfo.writesize + spare_size); + if (ret < (int)(mtdinfo.writesize + spare_size)) { + fprintf(stderr, "short write at %llx, %d\n", pos, ret); + close(outfd); + outfd = -1; + } + if (ecc.corrected != last_ecc.corrected) + fprintf(statusfile, "%08llx: ecc corrected\n", opos); + if (ecc.failed != last_ecc.failed) + fprintf(statusfile, "%08llx: ecc failed\n", opos); + if (bad_block == 1) + fprintf(statusfile, "%08llx: badblock\n", opos); + if (bad_block == 2) + fprintf(statusfile, "%08llx: read error\n", opos); + opos += mtdinfo.writesize + spare_size; + } + + last_ecc = ecc; + page_count++; + if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size)) + empty_pages++; + else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1)))) + printf("page at %llx (%d oobbytes): %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", pos, oobbuf.start, + oob_data[0], oob_data[1], oob_data[2], oob_data[3], + oob_data[4], oob_data[5], oob_data[6], oob_data[7]); + } + + if (outfd >= 0) { + fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages); + fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected); + fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed); + fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks); + fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks); + } + if (verbose) { + printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected); + printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed); + printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks); + printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks); + } + printf("read %d pages, %d empty\n", page_count, empty_pages); + + return 0; +}
diff --git a/toolbox/netstat.c b/toolbox/netstat.c new file mode 100644 index 0000000..5768599 --- /dev/null +++ b/toolbox/netstat.c
@@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> + +typedef union iaddr iaddr; +typedef union iaddr6 iaddr6; + +union iaddr { + unsigned u; + unsigned char b[4]; +}; + +union iaddr6 { + struct { + unsigned a; + unsigned b; + unsigned c; + unsigned d; + } u; + unsigned char b[16]; +}; + +static const char *state2str(unsigned state) +{ + switch(state){ + case 0x1: return "ESTABLISHED"; + case 0x2: return "SYN_SENT"; + case 0x3: return "SYN_RECV"; + case 0x4: return "FIN_WAIT1"; + case 0x5: return "FIN_WAIT2"; + case 0x6: return "TIME_WAIT"; + case 0x7: return "CLOSE"; + case 0x8: return "CLOSE_WAIT"; + case 0x9: return "LAST_ACK"; + case 0xA: return "LISTEN"; + case 0xB: return "CLOSING"; + default: return "UNKNOWN"; + } +} + +/* addr + : + port + \0 */ +#define ADDR_LEN INET6_ADDRSTRLEN + 1 + 5 + 1 + +static void addr2str(int af, const void *addr, unsigned port, char *buf) +{ + if (inet_ntop(af, addr, buf, ADDR_LEN) == NULL) { + *buf = '\0'; + return; + } + size_t len = strlen(buf); + if (port) { + snprintf(buf+len, ADDR_LEN-len, ":%d", port); + } else { + strncat(buf+len, ":*", ADDR_LEN-len-1); + } +} + +static void ipv4(const char *filename, const char *label) { + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + return; + } + char buf[BUFSIZ]; + fgets(buf, BUFSIZ, fp); + while (fgets(buf, BUFSIZ, fp)){ + char lip[ADDR_LEN]; + char rip[ADDR_LEN]; + iaddr laddr, raddr; + unsigned lport, rport, state, txq, rxq, num; + int n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x", + &num, &laddr.u, &lport, &raddr.u, &rport, + &state, &txq, &rxq); + if (n == 8) { + addr2str(AF_INET, &laddr, lport, lip); + addr2str(AF_INET, &raddr, rport, rip); + + printf("%4s %6d %6d %-22s %-22s %s\n", + label, txq, rxq, lip, rip, + state2str(state)); + } + } + fclose(fp); +} + +static void ipv6(const char *filename, const char *label) { + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + return; + } + char buf[BUFSIZ]; + fgets(buf, BUFSIZ, fp); + while (fgets(buf, BUFSIZ, fp)){ + char lip[ADDR_LEN]; + char rip[ADDR_LEN]; + iaddr6 laddr6, raddr6; + unsigned lport, rport, state, txq, rxq, num; + int n = sscanf(buf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x", + &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c, &laddr6.u.d, &lport, + &raddr6.u.a, &raddr6.u.b, &raddr6.u.c, &raddr6.u.d, &rport, + &state, &txq, &rxq); + if (n == 14) { + addr2str(AF_INET6, &laddr6, lport, lip); + addr2str(AF_INET6, &raddr6, rport, rip); + + printf("%4s %6d %6d %-22s %-22s %s\n", + label, txq, rxq, lip, rip, + state2str(state)); + } + } + fclose(fp); +} + +int netstat_main(int argc, char *argv[]) +{ + printf("Proto Recv-Q Send-Q Local Address Foreign Address State\n"); + ipv4("/proc/net/tcp", "tcp"); + ipv4("/proc/net/udp", "udp"); + ipv6("/proc/net/tcp6", "tcp6"); + ipv6("/proc/net/udp6", "udp6"); + return 0; +}
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c new file mode 100644 index 0000000..5f94083 --- /dev/null +++ b/toolbox/newfs_msdos.c
@@ -0,0 +1,1090 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $"; +#endif /* not lint */ + +#include <sys/param.h> + +#ifndef ANDROID + #include <sys/fdcio.h> + #include <sys/disk.h> + #include <sys/disklabel.h> + #include <sys/mount.h> +#else + #include <stdarg.h> + #include <linux/fs.h> + #include <linux/hdreg.h> +#endif + +#if HAVE_GLIBC + #define __unused __attribute__((__unused__)) +#endif + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ +#define BPN 4 /* bits per nibble */ +#define NPB 2 /* nibbles per byte */ + +#define DOSMAGIC 0xaa55 /* DOS magic number */ +#define MINBPS 512 /* minimum bytes per sector */ +#define MAXSPC 128 /* maximum sectors per cluster */ +#define MAXNFT 16 /* maximum number of FATs */ +#define DEFBLK 4096 /* default block size */ +#define DEFBLK16 2048 /* default block size FAT16 */ +#define DEFRDE 512 /* default root directory entries */ +#define RESFTE 2 /* reserved FAT entries */ +#define MINCLS12 1 /* minimum FAT12 clusters */ +#define MINCLS16 0x1000 /* minimum FAT16 clusters */ +#define MINCLS32 2 /* minimum FAT32 clusters */ +#define MAXCLS12 0xfed /* maximum FAT12 clusters */ +#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */ +#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */ + +#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ + (fat) == 16 ? MINCLS16 : \ + MINCLS32) + +#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ + (fat) == 16 ? MAXCLS16 : \ + MAXCLS32) + +#define mk1(p, x) \ + (p) = (u_int8_t)(x) + +#define mk2(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010) + +#define mk4(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010), \ + (p)[2] = (u_int8_t)((x) >> 020), \ + (p)[3] = (u_int8_t)((x) >> 030) + +#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) +#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) +#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) +#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) + +struct bs { + u_int8_t jmp[3]; /* bootstrap entry point */ + u_int8_t oem[8]; /* OEM name and version */ +}; + +struct bsbpb { + u_int8_t bps[2]; /* bytes per sector */ + u_int8_t spc; /* sectors per cluster */ + u_int8_t res[2]; /* reserved sectors */ + u_int8_t nft; /* number of FATs */ + u_int8_t rde[2]; /* root directory entries */ + u_int8_t sec[2]; /* total sectors */ + u_int8_t mid; /* media descriptor */ + u_int8_t spf[2]; /* sectors per FAT */ + u_int8_t spt[2]; /* sectors per track */ + u_int8_t hds[2]; /* drive heads */ + u_int8_t hid[4]; /* hidden sectors */ + u_int8_t bsec[4]; /* big total sectors */ +}; + +struct bsxbpb { + u_int8_t bspf[4]; /* big sectors per FAT */ + u_int8_t xflg[2]; /* FAT control flags */ + u_int8_t vers[2]; /* file system version */ + u_int8_t rdcl[4]; /* root directory start cluster */ + u_int8_t infs[2]; /* file system info sector */ + u_int8_t bkbs[2]; /* backup boot sector */ + u_int8_t rsvd[12]; /* reserved */ +}; + +struct bsx { + u_int8_t drv; /* drive number */ + u_int8_t rsvd; /* reserved */ + u_int8_t sig; /* extended boot signature */ + u_int8_t volid[4]; /* volume ID number */ + u_int8_t label[11]; /* volume label */ + u_int8_t type[8]; /* file system type */ +}; + +struct de { + u_int8_t namext[11]; /* name and extension */ + u_int8_t attr; /* attributes */ + u_int8_t rsvd[10]; /* reserved */ + u_int8_t time[2]; /* creation time */ + u_int8_t date[2]; /* creation date */ + u_int8_t clus[2]; /* starting cluster */ + u_int8_t size[4]; /* size */ +}; + +struct bpb { + u_int bps; /* bytes per sector */ + u_int spc; /* sectors per cluster */ + u_int res; /* reserved sectors */ + u_int nft; /* number of FATs */ + u_int rde; /* root directory entries */ + u_int sec; /* total sectors */ + u_int mid; /* media descriptor */ + u_int spf; /* sectors per FAT */ + u_int spt; /* sectors per track */ + u_int hds; /* drive heads */ + u_int hid; /* hidden sectors */ + u_int bsec; /* big total sectors */ + u_int bspf; /* big sectors per FAT */ + u_int rdcl; /* root directory start cluster */ + u_int infs; /* file system info sector */ + u_int bkbs; /* backup boot sector */ +}; + +#define BPBGAP 0, 0, 0, 0, 0, 0 + +static struct { + const char *name; + struct bpb bpb; +} const stdfmt[] = { + {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}}, + {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, + {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, + {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, + {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, + {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, + {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, + {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, + {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, + {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} +}; + +static const u_int8_t bootcode[] = { + 0xfa, /* cli */ + 0x31, 0xc0, /* xor ax,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + 0xfb, /* sti */ + 0x8e, 0xd8, /* mov ds,ax */ + 0xe8, 0x00, 0x00, /* call $ + 3 */ + 0x5e, /* pop si */ + 0x83, 0xc6, 0x19, /* add si,+19h */ + 0xbb, 0x07, 0x00, /* mov bx,0007h */ + 0xfc, /* cld */ + 0xac, /* lodsb */ + 0x84, 0xc0, /* test al,al */ + 0x74, 0x06, /* jz $ + 8 */ + 0xb4, 0x0e, /* mov ah,0eh */ + 0xcd, 0x10, /* int 10h */ + 0xeb, 0xf5, /* jmp $ - 9 */ + 0x30, 0xe4, /* xor ah,ah */ + 0xcd, 0x16, /* int 16h */ + 0xcd, 0x19, /* int 19h */ + 0x0d, 0x0a, + 'N', 'o', 'n', '-', 's', 'y', 's', 't', + 'e', 'm', ' ', 'd', 'i', 's', 'k', + 0x0d, 0x0a, + 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', + 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', + ' ', 'r', 'e', 'b', 'o', 'o', 't', + 0x0d, 0x0a, + 0 +}; + +static void check_mounted(const char *, mode_t); +static void getstdfmt(const char *, struct bpb *); +static void getdiskinfo(int, const char *, const char *, int, + struct bpb *); +static void print_bpb(struct bpb *); +static u_int ckgeom(const char *, u_int, const char *); +static u_int argtou(const char *, u_int, u_int, const char *); +static off_t argtooff(const char *, const char *); +static int oklabel(const char *); +static void mklabel(u_int8_t *, const char *); +static void setstr(u_int8_t *, const char *, size_t); +static void usage(void); + +#ifdef ANDROID +#define powerof2(x) ((((x) - 1) & (x)) == 0) +#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#endif +/* + * Construct a FAT12, FAT16, or FAT32 file system. + */ +int +newfs_msdos_main(int argc, char *argv[]) +{ + static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; + const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL; + u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0; + u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0; + u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0; + int opt_N = 0; + int Iflag = 0, mflag = 0, oflag = 0; + char buf[MAXPATHLEN]; + struct stat sb; + struct timeval tv; + struct bpb bpb; + struct tm *tm; + struct bs *bs; + struct bsbpb *bsbpb; + struct bsxbpb *bsxbpb; + struct bsx *bsx; + struct de *de; + u_int8_t *img; + const char *fname, *dtype, *bname; + ssize_t n; + time_t now; + u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; + int ch, fd, fd1; + off_t opt_create = 0, opt_ofs = 0; + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case '@': + opt_ofs = argtooff(optarg, "offset"); + break; + case 'N': + opt_N = 1; + break; + case 'B': + opt_B = optarg; + break; + case 'C': + opt_create = argtooff(optarg, "create size"); + break; + case 'F': + if (strcmp(optarg, "12") && + strcmp(optarg, "16") && + strcmp(optarg, "32")) + errx(1, "%s: bad FAT type", optarg); + opt_F = atoi(optarg); + break; + case 'I': + opt_I = argto4(optarg, 0, "volume ID"); + Iflag = 1; + break; + case 'L': + if (!oklabel(optarg)) + errx(1, "%s: bad volume label", optarg); + opt_L = optarg; + break; + case 'O': + if (strlen(optarg) > 8) + errx(1, "%s: bad OEM string", optarg); + opt_O = optarg; + break; + case 'S': + opt_S = argto2(optarg, 1, "bytes/sector"); + break; + case 'a': + opt_a = argto4(optarg, 1, "sectors/FAT"); + break; + case 'b': + opt_b = argtox(optarg, 1, "block size"); + opt_c = 0; + break; + case 'c': + opt_c = argto1(optarg, 1, "sectors/cluster"); + opt_b = 0; + break; + case 'e': + opt_e = argto2(optarg, 1, "directory entries"); + break; + case 'f': + opt_f = optarg; + break; + case 'h': + opt_h = argto2(optarg, 1, "drive heads"); + break; + case 'i': + opt_i = argto2(optarg, 1, "info sector"); + break; + case 'k': + opt_k = argto2(optarg, 1, "backup sector"); + break; + case 'm': + opt_m = argto1(optarg, 0, "media descriptor"); + mflag = 1; + break; + case 'n': + opt_n = argto1(optarg, 1, "number of FATs"); + break; + case 'o': + opt_o = argto4(optarg, 0, "hidden sectors"); + oflag = 1; + break; + case 'r': + opt_r = argto2(optarg, 1, "reserved sectors"); + break; + case 's': + opt_s = argto4(optarg, 1, "file system size"); + break; + case 'u': + opt_u = argto2(optarg, 1, "sectors/track"); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) + usage(); + fname = *argv++; + if (!opt_create && !strchr(fname, '/')) { + snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + err(1, NULL); + } + dtype = *argv; + if (opt_create) { + if (opt_N) + errx(1, "create (-C) is incompatible with -N"); + fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd == -1) + errx(1, "failed to create %s", fname); + if (ftruncate(fd, opt_create)) + errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create); + } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1) + err(1, "%s", fname); + if (fstat(fd, &sb)) + err(1, "%s", fname); + if (opt_create) { + if (!S_ISREG(sb.st_mode)) + warnx("warning, %s is not a regular file", fname); + } else { + if (!S_ISCHR(sb.st_mode)) + warnx("warning, %s is not a character device", fname); + } + if (!opt_N) + check_mounted(fname, sb.st_mode); + if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET)) + errx(1, "cannot seek to %jd", (intmax_t)opt_ofs); + memset(&bpb, 0, sizeof(bpb)); + if (opt_f) { + getstdfmt(opt_f, &bpb); + bpb.bsec = bpb.sec; + bpb.sec = 0; + bpb.bspf = bpb.spf; + bpb.spf = 0; + } + if (opt_h) + bpb.hds = opt_h; + if (opt_u) + bpb.spt = opt_u; + if (opt_S) + bpb.bps = opt_S; + if (opt_s) + bpb.bsec = opt_s; + if (oflag) + bpb.hid = opt_o; + if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) { + off_t delta; + getdiskinfo(fd, fname, dtype, oflag, &bpb); + if (opt_s) { + bpb.bsec = opt_s; + } + bpb.bsec -= (opt_ofs / bpb.bps); + delta = bpb.bsec % bpb.spt; + if (delta != 0) { + warnx("trim %d sectors from %d to adjust to a multiple of %d", + (int)delta, bpb.bsec, bpb.spt); + bpb.bsec -= delta; + } + if (bpb.spc == 0) { /* set defaults */ + if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */ + bpb.spc = 1; + else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */ + bpb.spc = 8; + else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ + bpb.spc = 16; + else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */ + bpb.spc = 32; + else + bpb.spc = 64; /* otherwise 32k */ + } + } + if (!powerof2(bpb.bps)) + errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps); + if (bpb.bps < MINBPS) + errx(1, "bytes/sector (%u) is too small; minimum is %u", + bpb.bps, MINBPS); + if (!(fat = opt_F)) { + if (opt_f) + fat = 12; + else if (!opt_e && (opt_i || opt_k)) + fat = 32; + } + if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k))) + errx(1, "-%c is not a legal FAT%s option", + fat == 32 ? 'e' : opt_i ? 'i' : 'k', + fat == 32 ? "32" : "12/16"); + if (opt_f && fat == 32) + bpb.rde = 0; + if (opt_b) { + if (!powerof2(opt_b)) + errx(1, "block size (%u) is not a power of 2", opt_b); + if (opt_b < bpb.bps) + errx(1, "block size (%u) is too small; minimum is %u", + opt_b, bpb.bps); + if (opt_b > bpb.bps * MAXSPC) + errx(1, "block size (%u) is too large; maximum is %u", + opt_b, bpb.bps * MAXSPC); + bpb.spc = opt_b / bpb.bps; + } + if (opt_c) { + if (!powerof2(opt_c)) + errx(1, "sectors/cluster (%u) is not a power of 2", opt_c); + bpb.spc = opt_c; + } + if (opt_r) + bpb.res = opt_r; + if (opt_n) { + if (opt_n > MAXNFT) + errx(1, "number of FATs (%u) is too large; maximum is %u", + opt_n, MAXNFT); + bpb.nft = opt_n; + } + if (opt_e) + bpb.rde = opt_e; + if (mflag) { + if (opt_m < 0xf0) + errx(1, "illegal media descriptor (%#x)", opt_m); + bpb.mid = opt_m; + } + if (opt_a) + bpb.bspf = opt_a; + if (opt_i) + bpb.infs = opt_i; + if (opt_k) + bpb.bkbs = opt_k; + bss = 1; + bname = NULL; + fd1 = -1; + if (opt_B) { + bname = opt_B; + if (!strchr(bname, '/')) { + snprintf(buf, sizeof(buf), "/boot/%s", bname); + if (!(bname = strdup(buf))) + err(1, NULL); + } + if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) + err(1, "%s", bname); + if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps || + sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) + errx(1, "%s: inappropriate file type or format", bname); + bss = sb.st_size / bpb.bps; + } + if (!bpb.nft) + bpb.nft = 2; + if (!fat) { + if (bpb.bsec < (bpb.res ? bpb.res : bss) + + howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) * + ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) * + bpb.nft + + howmany(bpb.rde ? bpb.rde : DEFRDE, + bpb.bps / sizeof(struct de)) + + (bpb.spc ? MINCLS16 : MAXCLS12 + 1) * + (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps))) + fat = 12; + else if (bpb.rde || bpb.bsec < + (bpb.res ? bpb.res : bss) + + howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft + + howmany(DEFRDE, bpb.bps / sizeof(struct de)) + + (MAXCLS16 + 1) * + (bpb.spc ? bpb.spc : howmany(8192, bpb.bps))) + fat = 16; + else + fat = 32; + } + x = bss; + if (fat == 32) { + if (!bpb.infs) { + if (x == MAXU16 || x == bpb.bkbs) + errx(1, "no room for info sector"); + bpb.infs = x; + } + if (bpb.infs != MAXU16 && x <= bpb.infs) + x = bpb.infs + 1; + if (!bpb.bkbs) { + if (x == MAXU16) + errx(1, "no room for backup sector"); + bpb.bkbs = x; + } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) + errx(1, "backup sector would overwrite info sector"); + if (bpb.bkbs != MAXU16 && x <= bpb.bkbs) + x = bpb.bkbs + 1; + } + if (!bpb.res) + bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x; + else if (bpb.res < x) + errx(1, "too few reserved sectors"); + if (fat != 32 && !bpb.rde) + bpb.rde = DEFRDE; + rds = howmany(bpb.rde, bpb.bps / sizeof(struct de)); + if (!bpb.spc) + for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps); + bpb.spc < MAXSPC && + bpb.res + + howmany((RESFTE + maxcls(fat)) * (fat / BPN), + bpb.bps * NPB) * bpb.nft + + rds + + (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec; + bpb.spc <<= 1); + if (fat != 32 && bpb.bspf > MAXU16) + errx(1, "too many sectors/FAT for FAT12/16"); + x1 = bpb.res + rds; + x = bpb.bspf ? bpb.bspf : 1; + if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) + errx(1, "meta data exceeds file system size"); + x1 += x * bpb.nft; + x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB / + (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft); + x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), + bpb.bps * NPB); + if (!bpb.bspf) { + bpb.bspf = x2; + x1 += (bpb.bspf - 1) * bpb.nft; + } + cls = (bpb.bsec - x1) / bpb.spc; + x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE; + if (cls > x) + cls = x; + if (bpb.bspf < x2) + warnx("warning: sectors/FAT limits file system to %u clusters", + cls); + if (cls < mincls(fat)) + errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, + mincls(fat)); + if (cls > maxcls(fat)) { + cls = maxcls(fat); + bpb.bsec = x1 + (cls + 1) * bpb.spc - 1; + warnx("warning: FAT type limits file system to %u sectors", + bpb.bsec); + } + printf("%s: %u sector%s in %u FAT%u cluster%s " + "(%u bytes/cluster)\n", fname, cls * bpb.spc, + cls * bpb.spc == 1 ? "" : "s", cls, fat, + cls == 1 ? "" : "s", bpb.bps * bpb.spc); + if (!bpb.mid) + bpb.mid = !bpb.hid ? 0xf0 : 0xf8; + if (fat == 32) + bpb.rdcl = RESFTE; + if (bpb.hid + bpb.bsec <= MAXU16) { + bpb.sec = bpb.bsec; + bpb.bsec = 0; + } + if (fat != 32) { + bpb.spf = bpb.bspf; + bpb.bspf = 0; + } + print_bpb(&bpb); + if (!opt_N) { + gettimeofday(&tv, NULL); + now = tv.tv_sec; + tm = localtime(&now); + if (!(img = malloc(bpb.bps))) + err(1, NULL); + dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; + for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) { + x = lsn; + if (opt_B && + fat == 32 && bpb.bkbs != MAXU16 && + bss <= bpb.bkbs && x >= bpb.bkbs) { + x -= bpb.bkbs; + if (!x && lseek(fd1, opt_ofs, SEEK_SET)) + err(1, "%s", bname); + } + if (opt_B && x < bss) { + if ((n = read(fd1, img, bpb.bps)) == -1) + err(1, "%s", bname); + if ((unsigned)n != bpb.bps) + errx(1, "%s: can't read sector %u", bname, x); + } else + memset(img, 0, bpb.bps); + if (!lsn || + (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) { + x1 = sizeof(struct bs); + bsbpb = (struct bsbpb *)(img + x1); + mk2(bsbpb->bps, bpb.bps); + mk1(bsbpb->spc, bpb.spc); + mk2(bsbpb->res, bpb.res); + mk1(bsbpb->nft, bpb.nft); + mk2(bsbpb->rde, bpb.rde); + mk2(bsbpb->sec, bpb.sec); + mk1(bsbpb->mid, bpb.mid); + mk2(bsbpb->spf, bpb.spf); + mk2(bsbpb->spt, bpb.spt); + mk2(bsbpb->hds, bpb.hds); + mk4(bsbpb->hid, bpb.hid); + mk4(bsbpb->bsec, bpb.bsec); + x1 += sizeof(struct bsbpb); + if (fat == 32) { + bsxbpb = (struct bsxbpb *)(img + x1); + mk4(bsxbpb->bspf, bpb.bspf); + mk2(bsxbpb->xflg, 0); + mk2(bsxbpb->vers, 0); + mk4(bsxbpb->rdcl, bpb.rdcl); + mk2(bsxbpb->infs, bpb.infs); + mk2(bsxbpb->bkbs, bpb.bkbs); + x1 += sizeof(struct bsxbpb); + } + bsx = (struct bsx *)(img + x1); + mk1(bsx->sig, 0x29); + if (Iflag) + x = opt_I; + else + x = (((u_int)(1 + tm->tm_mon) << 8 | + (u_int)tm->tm_mday) + + ((u_int)tm->tm_sec << 8 | + (u_int)(tv.tv_usec / 10))) << 16 | + ((u_int)(1900 + tm->tm_year) + + ((u_int)tm->tm_hour << 8 | + (u_int)tm->tm_min)); + mk4(bsx->volid, x); + mklabel(bsx->label, opt_L ? opt_L : "NO NAME"); + sprintf(buf, "FAT%u", fat); + setstr(bsx->type, buf, sizeof(bsx->type)); + if (!opt_B) { + x1 += sizeof(struct bsx); + bs = (struct bs *)img; + mk1(bs->jmp[0], 0xeb); + mk1(bs->jmp[1], x1 - 2); + mk1(bs->jmp[2], 0x90); + setstr(bs->oem, opt_O ? opt_O : "BSD 4.4", + sizeof(bs->oem)); + memcpy(img + x1, bootcode, sizeof(bootcode)); + mk2(img + MINBPS - 2, DOSMAGIC); + } + } else if (fat == 32 && bpb.infs != MAXU16 && + (lsn == bpb.infs || + (bpb.bkbs != MAXU16 && + lsn == bpb.bkbs + bpb.infs))) { + mk4(img, 0x41615252); + mk4(img + MINBPS - 28, 0x61417272); + mk4(img + MINBPS - 24, 0xffffffff); + mk4(img + MINBPS - 20, bpb.rdcl); + mk2(img + MINBPS - 2, DOSMAGIC); + } else if (lsn >= bpb.res && lsn < dir && + !((lsn - bpb.res) % + (bpb.spf ? bpb.spf : bpb.bspf))) { + mk1(img[0], bpb.mid); + for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) + mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); + } else if (lsn == dir && opt_L) { + de = (struct de *)img; + mklabel(de->namext, opt_L); + mk1(de->attr, 050); + x = (u_int)tm->tm_hour << 11 | + (u_int)tm->tm_min << 5 | + (u_int)tm->tm_sec >> 1; + mk2(de->time, x); + x = (u_int)(tm->tm_year - 80) << 9 | + (u_int)(tm->tm_mon + 1) << 5 | + (u_int)tm->tm_mday; + mk2(de->date, x); + } + if ((n = write(fd, img, bpb.bps)) == -1) + err(1, "%s", fname); + if ((unsigned)n != bpb.bps) { + errx(1, "%s: can't write sector %u", fname, lsn); + exit(1); + } + } + } + return 0; +} + +/* + * Exit with error if file system is mounted. + */ +static void +check_mounted(const char *fname, mode_t mode) +{ + struct statfs *mp; + const char *s1, *s2; + size_t len; + int n, r; + +#ifdef ANDROID + warnx("Skipping mount checks"); +#else + if (!(n = getmntinfo(&mp, MNT_NOWAIT))) + err(1, "getmntinfo"); + len = strlen(_PATH_DEV); + s1 = fname; + if (!strncmp(s1, _PATH_DEV, len)) + s1 += len; + r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; + for (; n--; mp++) { + s2 = mp->f_mntfromname; + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || + !strcmp(s1, s2)) + errx(1, "%s is mounted on %s", fname, mp->f_mntonname); + } +#endif +} + +/* + * Get a standard format. + */ +static void +getstdfmt(const char *fmt, struct bpb *bpb) +{ + u_int x, i; + + x = sizeof(stdfmt) / sizeof(stdfmt[0]); + for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); + if (i == x) + errx(1, "%s: unknown standard format", fmt); + *bpb = stdfmt[i].bpb; +} + +/* + * Get disk slice, partition, and geometry information. + */ + +#ifdef ANDROID +static void +getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, + struct bpb *bpb) +{ + struct hd_geometry geom; + + if (ioctl(fd, BLKSSZGET, &bpb->bps)) { + fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno)); + exit(1); + } + + ckgeom(fname, bpb->bps, "bytes/sector"); + + if (ioctl(fd, BLKGETSIZE, &bpb->bsec)) { + fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno)); + exit(1); + } + + if (ioctl(fd, HDIO_GETGEO, &geom)) { + fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno)); + geom.heads = 64; + geom.sectors = 63; + } + + if (!geom.heads) { + printf("Bogus heads from kernel - setting sane value\n"); + geom.heads = 64; + } + + if (!geom.sectors) { + printf("Bogus sectors from kernel - setting sane value\n"); + geom.sectors = 63; + } + + bpb->spt = geom.sectors; + ckgeom(fname, bpb->spt, "sectors/track"); + + bpb->hds = geom.heads; + ckgeom(fname, bpb->hds, "drive heads"); +} + +#else + +static void +getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, + struct bpb *bpb) +{ + struct disklabel *lp, dlp; + struct fd_type type; + off_t ms, hs = 0; + + lp = NULL; + + /* If the user specified a disk type, try to use that */ + if (dtype != NULL) { + lp = getdiskbyname(dtype); + } + + /* Maybe it's a floppy drive */ + if (lp == NULL) { + if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { + struct stat st; + + if (fstat(fd, &st)) + err(1, "Cannot get disk size"); + /* create a fake geometry for a file image */ + ms = st.st_size; + dlp.d_secsize = 512; + dlp.d_nsectors = 63; + dlp.d_ntracks = 255; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } else if (ioctl(fd, FD_GTYPE, &type) != -1) { + dlp.d_secsize = 128 << type.secsize; + dlp.d_nsectors = type.sectrac; + dlp.d_ntracks = type.heads; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } + } + + /* Maybe it's a fixed drive */ + if (lp == NULL) { + if (ioctl(fd, DIOCGDINFO, &dlp) == -1) { + if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1) + errx(1, "Cannot get sector size, %s", strerror(errno)); + + /* XXX Should we use bpb->bps if it's set? */ + dlp.d_secperunit = ms / dlp.d_secsize; + + if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) { + warnx("Cannot get number of sectors per track, %s", strerror(errno)); + dlp.d_nsectors = 63; + } + if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { + warnx("Cannot get number of heads, %s", strerror(errno)); + if (dlp.d_secperunit <= 63*1*1024) + dlp.d_ntracks = 1; + else if (dlp.d_secperunit <= 63*16*1024) + dlp.d_ntracks = 16; + else + dlp.d_ntracks = 255; + } + } + + hs = (ms / dlp.d_secsize) - dlp.d_secperunit; + lp = &dlp; + } + + if (bpb->bps == 0) + bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector"); + if (bpb->spt == 0) + bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track"); + if (bpb->hds == 0) + bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads"); + if (bpb->bsec == 0) + bpb->bsec = lp->d_secperunit; + if (bpb->hid == 0) + bpb->hid = hs; +} +#endif + +/* + * Print out BPB values. + */ +static void +print_bpb(struct bpb *bpb) +{ + printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res, + bpb->nft); + if (bpb->rde) + printf(" rde=%u", bpb->rde); + if (bpb->sec) + printf(" sec=%u", bpb->sec); + printf(" mid=%#x", bpb->mid); + if (bpb->spf) + printf(" spf=%u", bpb->spf); + printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid); + if (bpb->bsec) + printf(" bsec=%u", bpb->bsec); + if (!bpb->spf) { + printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl); + printf(" infs="); + printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs); + printf(" bkbs="); + printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs); + } + printf("\n"); +} + +/* + * Check a disk geometry value. + */ +static u_int +ckgeom(const char *fname, u_int val, const char *msg) +{ + if (!val) + errx(1, "%s: no default %s", fname, msg); + if (val > MAXU16) + errx(1, "%s: illegal %s %d", fname, msg, val); + return val; +} + +/* + * Convert and check a numeric option argument. + */ +static u_int +argtou(const char *arg, u_int lo, u_int hi, const char *msg) +{ + char *s; + u_long x; + + errno = 0; + x = strtoul(arg, &s, 0); + if (errno || !*arg || *s || x < lo || x > hi) + errx(1, "%s: bad %s", arg, msg); + return x; +} + +/* + * Same for off_t, with optional skmgpP suffix + */ +static off_t +argtooff(const char *arg, const char *msg) +{ + char *s; + off_t x; + + x = strtoll(arg, &s, 0); + /* allow at most one extra char */ + if (errno || x < 0 || (s[0] && s[1]) ) + errx(1, "%s: bad %s", arg, msg); + if (*s) { /* the extra char is the multiplier */ + switch (*s) { + default: + errx(1, "%s: bad %s", arg, msg); + /* notreached */ + + case 's': /* sector */ + case 'S': + x <<= 9; /* times 512 */ + break; + + case 'k': /* kilobyte */ + case 'K': + x <<= 10; /* times 1024 */ + break; + + case 'm': /* megabyte */ + case 'M': + x <<= 20; /* times 1024*1024 */ + break; + + case 'g': /* gigabyte */ + case 'G': + x <<= 30; /* times 1024*1024*1024 */ + break; + + case 'p': /* partition start */ + case 'P': /* partition start */ + case 'l': /* partition length */ + case 'L': /* partition length */ + errx(1, "%s: not supported yet %s", arg, msg); + /* notreached */ + } + } + return x; +} + +/* + * Check a volume label. + */ +static int +oklabel(const char *src) +{ + int c, i; + + for (i = 0; i <= 11; i++) { + c = (u_char)*src++; + if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) + break; + } + return i && !c; +} + +/* + * Make a volume label. + */ +static void +mklabel(u_int8_t *dest, const char *src) +{ + int c, i; + + for (i = 0; i < 11; i++) { + c = *src ? toupper(*src++) : ' '; + *dest++ = !i && c == '\xe5' ? 5 : c; + } +} + +/* + * Copy string, padding with spaces. + */ +static void +setstr(u_int8_t *dest, const char *src, size_t len) +{ + while (len--) + *dest++ = *src ? *src++ : ' '; +} + +/* + * Print usage message. + */ +static void +usage(void) +{ + fprintf(stderr, + "usage: newfs_msdos [ -options ] special [disktype]\n" + "where the options are:\n" + "\t-@ create file system at specified offset\n" + "\t-B get bootstrap from file\n" + "\t-C create image file with specified size\n" + "\t-F FAT type (12, 16, or 32)\n" + "\t-I volume ID\n" + "\t-L volume label\n" + "\t-N don't create file system: just print out parameters\n" + "\t-O OEM string\n" + "\t-S bytes/sector\n" + "\t-a sectors/FAT\n" + "\t-b block size\n" + "\t-c sectors/cluster\n" + "\t-e root directory entries\n" + "\t-f standard format\n" + "\t-h drive heads\n" + "\t-i file system info sector\n" + "\t-k backup boot sector\n" + "\t-m media descriptor\n" + "\t-n number of FATs\n" + "\t-o hidden sectors\n" + "\t-r reserved sectors\n" + "\t-s file system size (sectors)\n" + "\t-u sectors/track\n"); + exit(1); +}
diff --git a/toolbox/notify.c b/toolbox/notify.c new file mode 100644 index 0000000..c983ed5 --- /dev/null +++ b/toolbox/notify.c
@@ -0,0 +1,145 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <errno.h> + +int notify_main(int argc, char *argv[]) +{ + int c; + int nfd, ffd; + int res; + char event_buf[512]; + struct inotify_event *event; + int event_mask = IN_ALL_EVENTS; + int event_count = 1; + int print_files = 0; + int verbose = 2; + int width = 80; + char **file_names; + int file_count; + int id_offset = 0; + int i; + char *buf; + + do { + c = getopt(argc, argv, "m:c:pv:w:"); + if (c == EOF) + break; + switch (c) { + case 'm': + event_mask = strtol(optarg, NULL, 0); + break; + case 'c': + event_count = atoi(optarg); + break; + case 'p': + print_files = 1; + break; + case 'v': + verbose = atoi(optarg); + break; + case 'w': + width = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if (argc <= optind) { + fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]); + return 1; + } + + nfd = inotify_init(); + if(nfd < 0) { + fprintf(stderr, "inotify_init failed, %s\n", strerror(errno)); + return 1; + } + file_names = argv + optind; + file_count = argc - optind; + for(i = 0; i < file_count; i++) { + res = inotify_add_watch(nfd, file_names[i], event_mask); + if(res < 0) { + fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno)); + return 1; + } + if(i == 0) + id_offset = -res; + if(res + id_offset != i) { + fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i); + return 1; + } + } + + buf = malloc(width + 2); + + while(1) { + int event_pos = 0; + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + continue; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + while(res >= (int)sizeof(*event)) { + int event_size; + event = (struct inotify_event *)(event_buf + event_pos); + if(verbose >= 2) + printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : ""); + else if(verbose >= 2) + printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : ""); + else if(verbose >= 1) + printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(print_files && (event->mask & IN_MODIFY)) { + char filename[512]; + ssize_t read_len; + char *display_name; + int buflen; + strcpy(filename, file_names[event->wd + id_offset]); + if(event->len) { + strcat(filename, "/"); + strcat(filename, event->name); + } + ffd = open(filename, O_RDONLY); + display_name = (verbose >= 2 || event->len == 0) ? filename : event->name; + buflen = width - strlen(display_name); + read_len = read(ffd, buf, buflen); + if(read_len > 0) { + if(read_len < buflen && buf[read_len-1] != '\n') { + buf[read_len] = '\n'; + read_len++; + } + if(read_len == buflen) { + buf[--read_len] = '\0'; + buf[--read_len] = '\n'; + buf[--read_len] = '.'; + buf[--read_len] = '.'; + buf[--read_len] = '.'; + } + else { + buf[read_len] = '\0'; + } + printf("%s: %s", display_name, buf); + } + close(ffd); + } + if(event_count && --event_count == 0) + return 0; + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + } + + return 0; +}
diff --git a/toolbox/powerd.c b/toolbox/powerd.c new file mode 100644 index 0000000..1f29a8b --- /dev/null +++ b/toolbox/powerd.c
@@ -0,0 +1,441 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/select.h> +#include <sys/inotify.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +//#include <linux/input.h> // this does not compile + +// from <linux/input.h> + +struct input_event { + struct timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ +#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */ +#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f + +#define KEY_POWER 116 +#define KEY_SLEEP 142 +#define SW_0 0x00 + +// end <linux/input.h> + +struct notify_entry { + int id; + int (*handler)(struct notify_entry *entry, struct inotify_event *event); + const char *filename; +}; + +int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event) +{ + static int state = -1; + int last_state; + char buf[40]; + int read_len; + int fd; + + last_state = state; + fd = open(entry->filename, O_RDONLY); + read_len = read(fd, buf, sizeof(buf)); + if(read_len > 0) { + //printf("charging_state_notify_handler: \"%s\"\n", buf); + state = !(strncmp(buf, "Unknown", 7) == 0 + || strncmp(buf, "Discharging", 11) == 0); + } + close(fd); + //printf("charging_state_notify_handler: %d -> %d\n", last_state, state); + return state > last_state; +} + +struct notify_entry watched_files[] = { + { + .filename = "/sys/android_power/charging_state", + .handler = charging_state_notify_handler + } +}; + +int call_notify_handler(struct inotify_event *event) +{ + unsigned int start, i; + start = event->wd - watched_files[0].id; + if(start >= ARRAY_SIZE(watched_files)) + start = 0; + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + for(i = start; i < ARRAY_SIZE(watched_files); i++) { + if(event->wd == watched_files[i].id) { + if(watched_files[i].handler) { + return watched_files[i].handler(&watched_files[i], event); + } + return 1; + } + } + for(i = 0; i < start; i++) { + if(event->wd == watched_files[i].id) { + if(watched_files[i].handler) { + return watched_files[i].handler(&watched_files[i], event); + } + return 1; + } + } + return 0; +} + +int handle_inotify_event(int nfd) +{ + int res; + int wake_up = 0; + struct inotify_event *event; + char event_buf[512]; + int event_pos = 0; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 0; + } + printf("got %d bytes of event information\n", res); + while(res >= (int)sizeof(*event)) { + int event_size; + event = (struct inotify_event *)(event_buf + event_pos); + wake_up |= call_notify_handler(event); + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return wake_up; +} + +int powerd_main(int argc, char *argv[]) +{ + int c; + unsigned int i; + int res; + struct timeval tv; + int eventfd; + int notifyfd; + int powerfd; + int powerfd_is_sleep; + int user_activity_fd; + int acquire_partial_wake_lock_fd; + int acquire_full_wake_lock_fd; + int release_wake_lock_fd; + char *eventdev = "/dev/input/event0"; + const char *android_sleepdev = "/sys/android_power/request_sleep"; + const char *android_autooff_dev = "/sys/android_power/auto_off_timeout"; + const char *android_user_activity_dev = "/sys/android_power/last_user_activity"; + const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock"; + const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock"; + const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock"; + const char *powerdev = "/sys/power/state"; + const char suspendstring[] = "standby"; + const char wakelockstring[] = "powerd"; + fd_set rfds; + struct input_event event; + struct input_event light_event; + struct input_event light_event2; + int gotkey = 1; + time_t idle_time = 5; + const char *idle_time_string = "5"; + time_t lcd_light_time = 0; + time_t key_light_time = 0; + int verbose = 1; + int event_sleep = 0; + int got_power_key_down = 0; + struct timeval power_key_down_time = { 0, 0 }; + + light_event.type = EV_LED; + light_event.code = 4; // bright lcd backlight + light_event.value = 0; // light off -- sleep after timeout + + light_event2.type = EV_LED; + light_event2.code = 8; // keyboard backlight + light_event2.value = 0; // light off -- sleep after timeout + + do { + c = getopt(argc, argv, "e:ni:vql:k:"); + if (c == EOF) + break; + switch (c) { + case 'e': + eventdev = optarg; + break; + case 'n': + gotkey = 0; + break; + case 'i': + idle_time = atoi(optarg); + idle_time_string = optarg; + break; + case 'v': + verbose = 2; + break; + case 'q': + verbose = 0; + break; + case 'l': + lcd_light_time = atoi(optarg); + break; + case 'k': + key_light_time = atoi(optarg); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(optind != argc) { + fprintf(stderr,"%s [-e eventdev]\n", argv[0]); + return 1; + } + + eventfd = open(eventdev, O_RDWR | O_NONBLOCK); + if(eventfd < 0) { + fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno)); + return 1; + } + if(key_light_time >= lcd_light_time) { + lcd_light_time = key_light_time + 1; + fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n" + "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time); + } + + user_activity_fd = open(android_user_activity_dev, O_RDWR); + if(user_activity_fd >= 0) { + int auto_off_fd = open(android_autooff_dev, O_RDWR); + write(auto_off_fd, idle_time_string, strlen(idle_time_string)); + close(auto_off_fd); + } + + powerfd = open(android_sleepdev, O_RDWR); + if(powerfd >= 0) { + powerfd_is_sleep = 1; + if(verbose > 0) + printf("Using android sleep dev: %s\n", android_sleepdev); + } + else { + powerfd_is_sleep = 0; + powerfd = open(powerdev, O_RDWR); + if(powerfd >= 0) { + if(verbose > 0) + printf("Using linux power dev: %s\n", powerdev); + } + } + if(powerfd < 0) { + fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno)); + return 1; + } + + notifyfd = inotify_init(); + if(notifyfd < 0) { + fprintf(stderr, "inotify_init failed, %s\n", strerror(errno)); + return 1; + } + fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL)); + for(i = 0; i < ARRAY_SIZE(watched_files); i++) { + watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY); + printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id); + } + + acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR); + acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR); + release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR); + + if(user_activity_fd >= 0) { + idle_time = 60*60*24; // driver handles real timeout + } + if(gotkey) { + tv.tv_sec = idle_time; + tv.tv_usec = 0; + } + else { + tv.tv_sec = 0; + tv.tv_usec = 500000; + } + + while(1) { + FD_ZERO(&rfds); + //FD_SET(0, &rfds); + FD_SET(eventfd, &rfds); + FD_SET(notifyfd, &rfds); + res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv); + if(res < 0) { + fprintf(stderr, "select failed, %s\n", strerror(errno)); + return 1; + } + if(res == 0) { + if(light_event2.value == 1) + goto light2_off; + if(light_event.value == 1) + goto light_off; + if(user_activity_fd < 0) { + if(gotkey && verbose > 0) + printf("Idle - sleep\n"); + if(!gotkey && verbose > 1) + printf("Reenter sleep\n"); + goto sleep; + } + else { + tv.tv_sec = 60*60*24; + tv.tv_usec = 0; + } + } + if(res > 0) { + //if(FD_ISSET(0, &rfds)) { + // printf("goto data on stdin quit\n"); + // return 0; + //} + if(FD_ISSET(notifyfd, &rfds)) { + write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + if(handle_inotify_event(notifyfd) > 0) { + write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + if(FD_ISSET(eventfd, &rfds)) { + write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + res = read(eventfd, &event, sizeof(event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + return 1; + } + if(event.type == EV_PWR && event.code == KEY_SLEEP) { + event_sleep = event.value; + } + if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) { + gotkey = 1; + if(user_activity_fd >= 0) { + char buf[32]; + int len; + len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec); + write(user_activity_fd, buf, len); + } + if(lcd_light_time | key_light_time) { + tv.tv_sec = key_light_time; + light_event.value = 1; + write(eventfd, &light_event, sizeof(light_event)); + light_event2.value = 1; + write(eventfd, &light_event2, sizeof(light_event2)); + } + else { + tv.tv_sec = idle_time; + } + tv.tv_usec = 0; + if(verbose > 1) + printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : ""); + if(event.code == KEY_POWER) { + if(event.value == 0) { + int tmp_got_power_key_down = got_power_key_down; + got_power_key_down = 0; + if(tmp_got_power_key_down) { + // power key released + if(verbose > 0) + printf("Power key released - sleep\n"); + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + goto sleep; + } + } + else if(event_sleep == 0) { + got_power_key_down = 1; + power_key_down_time = event.time; + } + } + } + if(event.type == EV_SW && event.code == SW_0 && event.value == 0) { + if(verbose > 0) + printf("Flip closed - sleep\n"); + power_key_down_time = event.time; + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + goto sleep; + } + write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); + } + } + if(0) { +light_off: + light_event.value = 0; + write(eventfd, &light_event, sizeof(light_event)); + tv.tv_sec = idle_time - lcd_light_time; + } + if(0) { +light2_off: + light_event2.value = 0; + write(eventfd, &light_event2, sizeof(light_event2)); + tv.tv_sec = lcd_light_time - key_light_time; + } + if(0) { +sleep: + if(light_event.value == 1) { + light_event.value = 0; + write(eventfd, &light_event, sizeof(light_event)); + light_event2.value = 0; + write(eventfd, &light_event2, sizeof(light_event2)); + tv.tv_sec = idle_time - lcd_light_time; + } + if(powerfd_is_sleep) { + char buf[32]; + int len; + len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec); + write(powerfd, buf, len); + } + else + write(powerfd, suspendstring, sizeof(suspendstring) - 1); + gotkey = 0; + tv.tv_sec = 0; + tv.tv_usec = 500000; + } + } + + return 0; +}
diff --git a/toolbox/printenv.c b/toolbox/printenv.c new file mode 100644 index 0000000..d5ea531 --- /dev/null +++ b/toolbox/printenv.c
@@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> + +extern char** environ; + +int printenv_main (int argc, char **argv) +{ + char** e; + char* v; + int i; + + if (argc == 1) { + e = environ; + while (*e) { + printf("%s\n", *e); + e++; + } + } else { + for (i=1; i<argc; i++) { + v = getenv(argv[i]); + if (v) { + printf("%s\n", v); + } + } + } + + return 0; +} +
diff --git a/toolbox/ps.c b/toolbox/ps.c new file mode 100644 index 0000000..7c3de4a --- /dev/null +++ b/toolbox/ps.c
@@ -0,0 +1,262 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> + +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> + +#include <pwd.h> + +#include <cutils/sched_policy.h> + +static char *nexttoksep(char **strp, char *sep) +{ + char *p = strsep(strp,sep); + return (p == 0) ? "" : p; +} +static char *nexttok(char **strp) +{ + return nexttoksep(strp, " "); +} + +#define SHOW_PRIO 1 +#define SHOW_TIME 2 +#define SHOW_POLICY 4 +#define SHOW_CPU 8 +#define SHOW_MACLABEL 16 + +static int display_flags = 0; + +static int ps_line(int pid, int tid, char *namefilter) +{ + char statline[1024]; + char cmdline[1024]; + char macline[1024]; + char user[32]; + struct stat stats; + int fd, r; + char *ptr, *name, *state; + int ppid, tty; + unsigned wchan, rss, vss, eip; + unsigned utime, stime; + int prio, nice, rtprio, sched, psr; + struct passwd *pw; + + sprintf(statline, "/proc/%d", pid); + stat(statline, &stats); + + if(tid) { + sprintf(statline, "/proc/%d/task/%d/stat", pid, tid); + cmdline[0] = 0; + snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid); + } else { + sprintf(statline, "/proc/%d/stat", pid); + sprintf(cmdline, "/proc/%d/cmdline", pid); + snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid); + fd = open(cmdline, O_RDONLY); + if(fd == 0) { + r = 0; + } else { + r = read(fd, cmdline, 1023); + close(fd); + if(r < 0) r = 0; + } + cmdline[r] = 0; + } + + fd = open(statline, O_RDONLY); + if(fd == 0) return -1; + r = read(fd, statline, 1023); + close(fd); + if(r < 0) return -1; + statline[r] = 0; + + ptr = statline; + nexttok(&ptr); // skip pid + ptr++; // skip "(" + + name = ptr; + ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')', + *ptr++ = '\0'; // and null-terminate name. + + ptr++; // skip " " + state = nexttok(&ptr); + ppid = atoi(nexttok(&ptr)); + nexttok(&ptr); // pgrp + nexttok(&ptr); // sid + tty = atoi(nexttok(&ptr)); + + nexttok(&ptr); // tpgid + nexttok(&ptr); // flags + nexttok(&ptr); // minflt + nexttok(&ptr); // cminflt + nexttok(&ptr); // majflt + nexttok(&ptr); // cmajflt +#if 1 + utime = atoi(nexttok(&ptr)); + stime = atoi(nexttok(&ptr)); +#else + nexttok(&ptr); // utime + nexttok(&ptr); // stime +#endif + nexttok(&ptr); // cutime + nexttok(&ptr); // cstime + prio = atoi(nexttok(&ptr)); + nice = atoi(nexttok(&ptr)); + nexttok(&ptr); // threads + nexttok(&ptr); // itrealvalue + nexttok(&ptr); // starttime + vss = strtoul(nexttok(&ptr), 0, 10); // vsize + rss = strtoul(nexttok(&ptr), 0, 10); // rss + nexttok(&ptr); // rlim + nexttok(&ptr); // startcode + nexttok(&ptr); // endcode + nexttok(&ptr); // startstack + nexttok(&ptr); // kstkesp + eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip + nexttok(&ptr); // signal + nexttok(&ptr); // blocked + nexttok(&ptr); // sigignore + nexttok(&ptr); // sigcatch + wchan = strtoul(nexttok(&ptr), 0, 10); // wchan + nexttok(&ptr); // nswap + nexttok(&ptr); // cnswap + nexttok(&ptr); // exit signal + psr = atoi(nexttok(&ptr)); // processor + rtprio = atoi(nexttok(&ptr)); // rt_priority + sched = atoi(nexttok(&ptr)); // scheduling policy + + tty = atoi(nexttok(&ptr)); + + if(tid != 0) { + ppid = pid; + pid = tid; + } + + pw = getpwuid(stats.st_uid); + if(pw == 0) { + sprintf(user,"%d",(int)stats.st_uid); + } else { + strcpy(user,pw->pw_name); + } + + if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) { + if (display_flags & SHOW_MACLABEL) { + fd = open(macline, O_RDONLY); + strcpy(macline, "-"); + if (fd >= 0) { + r = read(fd, macline, sizeof(macline)-1); + close(fd); + if (r > 0) + macline[r] = 0; + } + printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name); + return 0; + } + + printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4); + if (display_flags & SHOW_CPU) + printf(" %-2d", psr); + if (display_flags & SHOW_PRIO) + printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched); + if (display_flags & SHOW_POLICY) { + SchedPolicy p; + if (get_sched_policy(pid, &p) < 0) + printf(" un "); + else { + if (p == SP_BACKGROUND) + printf(" bg "); + else if (p == SP_FOREGROUND) + printf(" fg "); + else + printf(" er "); + } + } + printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name); + if(display_flags&SHOW_TIME) + printf(" (u:%d, s:%d)", utime, stime); + + printf("\n"); + } + return 0; +} + + +void ps_threads(int pid, char *namefilter) +{ + char tmp[128]; + DIR *d; + struct dirent *de; + + sprintf(tmp,"/proc/%d/task",pid); + d = opendir(tmp); + if(d == 0) return; + + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int tid = atoi(de->d_name); + if(tid == pid) continue; + ps_line(pid, tid, namefilter); + } + } + closedir(d); +} + +int ps_main(int argc, char **argv) +{ + DIR *d; + struct dirent *de; + char *namefilter = 0; + int pidfilter = 0; + int threads = 0; + + d = opendir("/proc"); + if(d == 0) return -1; + + while(argc > 1){ + if(!strcmp(argv[1],"-t")) { + threads = 1; + } else if(!strcmp(argv[1],"-x")) { + display_flags |= SHOW_TIME; + } else if(!strcmp(argv[1], "-Z")) { + display_flags |= SHOW_MACLABEL; + } else if(!strcmp(argv[1],"-P")) { + display_flags |= SHOW_POLICY; + } else if(!strcmp(argv[1],"-p")) { + display_flags |= SHOW_PRIO; + } else if(!strcmp(argv[1],"-c")) { + display_flags |= SHOW_CPU; + } else if(isdigit(argv[1][0])){ + pidfilter = atoi(argv[1]); + } else { + namefilter = argv[1]; + } + argc--; + argv++; + } + + if (display_flags & SHOW_MACLABEL) { + printf("LABEL USER PID PPID NAME\n"); + } else { + printf("USER PID PPID VSIZE RSS %s%s %s WCHAN PC NAME\n", + (display_flags&SHOW_CPU)?"CPU ":"", + (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"", + (display_flags&SHOW_POLICY)?"PCY " : ""); + } + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int pid = atoi(de->d_name); + if(!pidfilter || (pidfilter == pid)) { + ps_line(pid, 0, namefilter); + if(threads) ps_threads(pid, namefilter); + } + } + } + closedir(d); + return 0; +} +
diff --git a/toolbox/r.c b/toolbox/r.c new file mode 100644 index 0000000..7de0396 --- /dev/null +++ b/toolbox/r.c
@@ -0,0 +1,97 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> + +#define PAGE_SIZE 4096 + +static int usage() +{ + fprintf(stderr,"r [-b|-s] <address> [<value>]\n"); + return -1; +} + +int r_main(int argc, char *argv[]) +{ + int width = 4, set = 0, fd; + unsigned addr, value, endaddr = 0; + unsigned long mmap_start, mmap_size; + void *page; + char *end; + + if(argc < 2) return usage(); + + if(!strcmp(argv[1], "-b")) { + width = 1; + argc--; + argv++; + } else if(!strcmp(argv[1], "-s")) { + width = 2; + argc--; + argv++; + } + + if(argc < 2) return usage(); + addr = strtoul(argv[1], 0, 16); + + end = strchr(argv[1], '-'); + if (end) + endaddr = strtoul(end + 1, 0, 16); + + if (!endaddr) + endaddr = addr + width - 1; + + if (endaddr <= addr) { + fprintf(stderr, "invalid end address\n"); + return -1; + } + + if(argc > 2) { + set = 1; + value = strtoul(argv[2], 0, 16); + } + + fd = open("/dev/mem", O_RDWR | O_SYNC); + if(fd < 0) { + fprintf(stderr,"cannot open /dev/mem\n"); + return -1; + } + + mmap_start = addr & ~(PAGE_SIZE - 1); + mmap_size = endaddr - mmap_start + 1; + mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + + page = mmap(0, mmap_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, mmap_start); + + if(page == MAP_FAILED){ + fprintf(stderr,"cannot mmap region\n"); + return -1; + } + + while (addr <= endaddr) { + switch(width){ + case 4: { + unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %08x\n", addr, *x); + break; + } + case 2: { + unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %04x\n", addr, *x); + break; + } + case 1: { + unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095)); + if(set) *x = value; + fprintf(stderr,"%08x: %02x\n", addr, *x); + break; + } + } + addr += width; + } + return 0; +}
diff --git a/toolbox/readtty.c b/toolbox/readtty.c new file mode 100644 index 0000000..2b27548 --- /dev/null +++ b/toolbox/readtty.c
@@ -0,0 +1,183 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +struct { + char key; + char *chars; +} map[] = { + { '1', "_ -1?!,.:;\"'<=>()_" }, + { '2', "Cabc2ABC" }, + { '3', "Fdef3DEF" }, + { '4', "Ighi4GHI" }, + { '5', "Ljkl5JKL" }, + { '6', "Omno6MNO" }, + { '7', "Spqrs7PQRS" }, + { '8', "Vtuv8TUV" }, + { '9', "Zwxyz9WXYZ" }, + { '0', "*+&0@/#*" }, +}; + +char next_char(char key, char current) +{ + int i; + char *next; + for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) { + if(key == map[i].key) { + next = strchr(map[i].chars, current); + if(next && next[1]) + return next[1]; + return map[i].chars[1]; + } + } + return key; +} + +char prev_char(char key, char current) +{ + int i; + char *next; + for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) { + if(key == map[i].key) { + next = strchr(map[i].chars+1, current); + if(next && next[-1]) + return next[-1]; + return map[i].chars[1]; + } + } + return key; +} + +int readtty_main(int argc, char *argv[]) +{ + int c; + //int flags; + char buf[1]; + int res; + struct termios ttyarg; + struct termios savedttyarg; + int nonblock = 0; + int timeout = 0; + int flush = 0; + int phone = 0; + char *accept = NULL; + char *rejectstring = NULL; + char last_char_in = 0; + char current_char = 0; + char *exit_string = NULL; + int exit_match = 0; + + do { + c = getopt(argc, argv, "nt:fa:r:pe:"); + if (c == EOF) + break; + switch (c) { + case 't': + timeout = atoi(optarg); + break; + case 'n': + nonblock = 1; + break; + case 'f': + flush = 1; + break; + case 'a': + accept = optarg; + break; + case 'r': + rejectstring = optarg; + break; + case 'p': + phone = 1; + break; + case 'e': + exit_string = optarg; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(flush) + tcflush(STDIN_FILENO, TCIFLUSH); + ioctl(STDIN_FILENO, TCGETS , &savedttyarg) ; /* set changed tty arguments */ + ttyarg = savedttyarg; + ttyarg.c_cc[VMIN] = (timeout > 0 || nonblock) ? 0 : 1; /* minimum of 0 chars */ + ttyarg.c_cc[VTIME] = timeout; /* wait max 15/10 sec */ + ttyarg.c_iflag = BRKINT | ICRNL; + ttyarg.c_lflag &= ~(ECHO | ICANON); + ioctl(STDIN_FILENO, TCSETS , &ttyarg); + + while (1) { + res = read(STDIN_FILENO, buf, 1); + if(res <= 0) { + if(phone) { + if(current_char) { + write(STDERR_FILENO, ¤t_char, 1); + write(STDOUT_FILENO, ¤t_char, 1); + if(exit_string && current_char == exit_string[exit_match]) { + exit_match++; + if(exit_string[exit_match] == '\0') + break; + } + else + exit_match = 0; + current_char = 0; + } + continue; + } + break; + } + if(accept && strchr(accept, buf[0]) == NULL) { + if(rejectstring) { + write(STDOUT_FILENO, rejectstring, strlen(rejectstring)); + break; + } + if(flush) + tcflush(STDIN_FILENO, TCIFLUSH); + continue; + } + if(phone) { + //if(!isprint(buf[0])) { + // fprintf(stderr, "got unprintable character 0x%x\n", buf[0]); + //} + if(buf[0] == '\0') { + if(current_char) { + current_char = prev_char(last_char_in, current_char); + write(STDERR_FILENO, ¤t_char, 1); + write(STDERR_FILENO, "\b", 1); + } + continue; + } + if(current_char && buf[0] != last_char_in) { + write(STDERR_FILENO, ¤t_char, 1); + write(STDOUT_FILENO, ¤t_char, 1); + if(exit_string && current_char == exit_string[exit_match]) { + exit_match++; + if(exit_string[exit_match] == '\0') + break; + } + else + exit_match = 0; + current_char = 0; + } + last_char_in = buf[0]; + current_char = next_char(last_char_in, current_char); + write(STDERR_FILENO, ¤t_char, 1); + write(STDERR_FILENO, "\b", 1); + continue; + } + write(STDOUT_FILENO, buf, 1); + break; + } + ioctl(STDIN_FILENO, TCSETS , &savedttyarg) ; /* set changed tty arguments */ + + return 0; +}
diff --git a/toolbox/reboot.c b/toolbox/reboot.c new file mode 100644 index 0000000..84fc723 --- /dev/null +++ b/toolbox/reboot.c
@@ -0,0 +1,64 @@ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <cutils/android_reboot.h> +#include <unistd.h> + +#if HAVE_GLIBC +#include <linux/reboot.h> +#include <sys/syscall.h> +#define __reboot(...) syscall(__NR_reboot,__VA_ARGS__) +#endif + +int reboot_main(int argc, char *argv[]) +{ + int ret; + int nosync = 0; + int poweroff = 0; + int flags = 0; + + opterr = 0; + do { + int c; + + c = getopt(argc, argv, "np"); + + if (c == EOF) { + break; + } + + switch (c) { + case 'n': + nosync = 1; + break; + case 'p': + poweroff = 1; + break; + case '?': + fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]); + exit(EXIT_FAILURE); + } + } while (1); + + if(argc > optind + 1) { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(nosync) + /* also set NO_REMOUNT_RO as remount ro includes an implicit sync */ + flags = ANDROID_RB_FLAG_NO_SYNC | ANDROID_RB_FLAG_NO_REMOUNT_RO; + + if(poweroff) + ret = android_reboot(ANDROID_RB_POWEROFF, flags, 0); + else if(argc > optind) + ret = android_reboot(ANDROID_RB_RESTART2, flags, argv[optind]); + else + ret = android_reboot(ANDROID_RB_RESTART, flags, 0); + if(ret < 0) { + perror("reboot"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "reboot returned\n"); + return 0; +}
diff --git a/toolbox/renice.c b/toolbox/renice.c new file mode 100644 index 0000000..978b329 --- /dev/null +++ b/toolbox/renice.c
@@ -0,0 +1,144 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sched.h> + +static void +usage(const char *s) +{ + fprintf(stderr, "USAGE: %s [[-r] priority pids ...] [-g pid]\n", s); + exit(EXIT_FAILURE); +} + +void print_prio(pid_t pid) +{ + int sched; + struct sched_param sp; + + printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid)); + + printf("scheduling class: "); + sched = sched_getscheduler(pid); + switch (sched) { + case SCHED_FIFO: + printf("FIFO\n"); + break; + case SCHED_RR: + printf("RR\n"); + break; + case SCHED_OTHER: + printf("Normal\n"); + break; + case -1: + perror("sched_getscheduler"); + break; + default: + printf("Unknown\n"); + } + + sched_getparam(pid, &sp); + printf("RT prio: %d (of %d to %d)\n", sp.sched_priority, + sched_get_priority_min(sched), sched_get_priority_max(sched)); +} + +int renice_main(int argc, char *argv[]) +{ + int prio; + int realtime = 0; + char *cmd = argv[0]; + + // consume command name + argc--; + argv++; + + if (argc < 1) + usage(cmd); + + if(strcmp("-r", argv[0]) == 0) { + // do realtime priority adjustment + realtime = 1; + argc--; + argv++; + } + + if(strcmp("-g", argv[0]) == 0) { + if (argc < 2) + usage(cmd); + print_prio(atoi(argv[1])); + return 0; + } + + if (argc < 1) + usage(cmd); + + prio = atoi(argv[0]); + argc--; + argv++; + + if (argc < 1) + usage(cmd); + + while(argc) { + pid_t pid; + + pid = atoi(argv[0]); + argc--; + argv++; + + if (realtime) { + struct sched_param sp = { .sched_priority = prio }; + int ret; + + ret = sched_setscheduler(pid, SCHED_RR, &sp); + if (ret) { + perror("sched_set_scheduler"); + exit(EXIT_FAILURE); + } + } else { + int ret; + + ret = setpriority(PRIO_PROCESS, pid, prio); + if (ret) { + perror("setpriority"); + exit(EXIT_FAILURE); + } + } + } + + return 0; +} + +
diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c new file mode 100644 index 0000000..5ef0ef1 --- /dev/null +++ b/toolbox/restorecon.c
@@ -0,0 +1,141 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fts.h> +#include <selinux/selinux.h> +#include <selinux/label.h> + +#define FCPATH "/file_contexts" + +static struct selabel_handle *sehandle; +static const char *progname; +static int nochange; +static int verbose; + +static void usage(void) +{ + fprintf(stderr, "usage: %s [-f file_contexts] [-nrRv] pathname...\n", progname); + exit(1); +} + +static int restore(const char *pathname, const struct stat *sb) +{ + char *oldcontext, *newcontext; + + if (lgetfilecon(pathname, &oldcontext) < 0) { + fprintf(stderr, "Could not get context of %s: %s\n", + pathname, strerror(errno)); + return -1; + } + if (selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) { + fprintf(stderr, "Could not lookup context for %s: %s\n", pathname, + strerror(errno)); + return -1; + } + if (strcmp(newcontext, "<<none>>") && + strcmp(oldcontext, newcontext)) { + if (verbose) + printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext); + if (!nochange) { + if (lsetfilecon(pathname, newcontext) < 0) { + fprintf(stderr, "Could not label %s with %s: %s\n", + pathname, newcontext, strerror(errno)); + return -1; + } + } + } + freecon(oldcontext); + freecon(newcontext); + return 0; +} + +int restorecon_main(int argc, char **argv) +{ + struct selinux_opt seopts[] = { + { SELABEL_OPT_PATH, FCPATH } + }; + int ch, recurse = 0, ftsflags = FTS_PHYSICAL; + + progname = argv[0]; + + do { + ch = getopt(argc, argv, "f:nrRv"); + if (ch == EOF) + break; + switch (ch) { + case 'f': + seopts[0].value = optarg; + break; + case 'n': + nochange = 1; + break; + case 'r': + case 'R': + recurse = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + } + } while (1); + + argc -= optind; + argv += optind; + if (!argc) + usage(); + + sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + if (!sehandle) { + fprintf(stderr, "Could not load file contexts from %s: %s\n", seopts[0].value, + strerror(errno)); + return -1; + } + + if (recurse) { + FTS *fts; + FTSENT *ftsent; + fts = fts_open(argv, ftsflags, NULL); + if (!fts) { + fprintf(stderr, "Could not traverse filesystems (first was %s): %s\n", + argv[0], strerror(errno)); + return -1; + } + while ((ftsent = fts_read(fts))) { + switch (ftsent->fts_info) { + case FTS_DP: + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + fprintf(stderr, "Could not access %s: %s\n", ftsent->fts_path, + strerror(errno)); + fts_set(fts, ftsent, FTS_SKIP); + break; + default: + if (restore(ftsent->fts_path, ftsent->fts_statp) < 0) + fts_set(fts, ftsent, FTS_SKIP); + break; + } + } + } else { + int i, rc; + struct stat sb; + + for (i = 0; i < argc; i++) { + rc = lstat(argv[i], &sb); + if (rc < 0) { + fprintf(stderr, "Could not stat %s: %s\n", argv[i], + strerror(errno)); + continue; + } + restore(argv[i], &sb); + } + } + + return 0; +}
diff --git a/toolbox/rm.c b/toolbox/rm.c new file mode 100644 index 0000000..c29384e --- /dev/null +++ b/toolbox/rm.c
@@ -0,0 +1,93 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + +static int usage() +{ + fprintf(stderr,"rm [-rR] <target>\n"); + return -1; +} + +/* return -1 on failure, with errno set to the first error */ +static int unlink_recursive(const char* name) +{ + struct stat st; + DIR *dir; + struct dirent *de; + int fail = 0; + + /* is it a file or directory? */ + if (lstat(name, &st) < 0) + return -1; + + /* a file, so unlink it */ + if (!S_ISDIR(st.st_mode)) + return unlink(name); + + /* a directory, so open handle */ + dir = opendir(name); + if (dir == NULL) + return -1; + + /* recurse over components */ + errno = 0; + while ((de = readdir(dir)) != NULL) { + char dn[PATH_MAX]; + if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) + continue; + sprintf(dn, "%s/%s", name, de->d_name); + if (unlink_recursive(dn) < 0) { + fail = 1; + break; + } + errno = 0; + } + /* in case readdir or unlink_recursive failed */ + if (fail || errno < 0) { + int save = errno; + closedir(dir); + errno = save; + return -1; + } + + /* close directory handle */ + if (closedir(dir) < 0) + return -1; + + /* delete target directory */ + return rmdir(name); +} + +int rm_main(int argc, char *argv[]) +{ + int ret, err; + int i = 1; + int recursive = 0; + + if (argc < 2) + return usage(); + + /* check if recursive */ + if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) { + recursive = 1; + i = 2; + } + + /* loop over the file/directory args */ + err = 0; + for (; i < argc; i++) { + int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]); + if (ret < 0) { + fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno)); + err = -1; + } + } + + return err; +} +
diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c new file mode 100644 index 0000000..06f3df2 --- /dev/null +++ b/toolbox/rmdir.c
@@ -0,0 +1,29 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +static int usage() +{ + fprintf(stderr,"rmdir <directory>\n"); + return -1; +} + +int rmdir_main(int argc, char *argv[]) +{ + int symbolic = 0; + int ret; + if(argc < 2) return usage(); + + while(argc > 1) { + argc--; + argv++; + ret = rmdir(argv[0]); + if(ret < 0) { + fprintf(stderr, "rmdir failed for %s, %s\n", argv[0], strerror(errno)); + return ret; + } + } + + return 0; +}
diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c new file mode 100644 index 0000000..25257cc --- /dev/null +++ b/toolbox/rmmod.c
@@ -0,0 +1,44 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <errno.h> +#include <asm/unistd.h> + +extern int delete_module(const char *, unsigned int); + +int rmmod_main(int argc, char **argv) +{ + int ret; + char *modname, *dot; + + /* make sure we've got an argument */ + if (argc < 2) { + fprintf(stderr, "usage: rmmod <module>\n"); + return -1; + } + + /* if given /foo/bar/blah.ko, make a weak attempt + * to convert to "blah", just for convenience + */ + modname = strrchr(argv[1], '/'); + if (!modname) + modname = argv[1]; + else modname++; + + dot = strchr(argv[1], '.'); + if (dot) + *dot = '\0'; + + /* pass it to the kernel */ + ret = delete_module(modname, O_NONBLOCK | O_EXCL); + if (ret != 0) { + fprintf(stderr, "rmmod: delete_module '%s' failed (errno %d)\n", + modname, errno); + return -1; + } + + return 0; +} +
diff --git a/toolbox/rotatefb.c b/toolbox/rotatefb.c new file mode 100644 index 0000000..2ff4127 --- /dev/null +++ b/toolbox/rotatefb.c
@@ -0,0 +1,71 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> +#include <linux/fb.h> + + +int rotatefb_main(int argc, char *argv[]) +{ + int c; + char *fbdev = "/dev/graphics/fb0"; + int rotation = 0; + int fd; + int res; + struct fb_var_screeninfo fbinfo; + + do { + c = getopt(argc, argv, "d:"); + if (c == EOF) + break; + switch (c) { + case 'd': + fbdev = optarg; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + + if(optind + 1 != argc) { + fprintf(stderr, "%s: specify rotation\n", argv[0]); + exit(1); + } + rotation = atoi(argv[optind]); + + fd = open(fbdev, O_RDWR); + if(fd < 0) { + fprintf(stderr, "cannot open %s\n", fbdev); + return 1; + } + + res = ioctl(fd, FBIOGET_VSCREENINFO, &fbinfo); + if(res < 0) { + fprintf(stderr, "failed to get fbinfo: %s\n", strerror(errno)); + return 1; + } + if((fbinfo.rotate ^ rotation) & 1) { + unsigned int xres = fbinfo.yres; + fbinfo.yres = fbinfo.xres; + fbinfo.xres = xres; + fbinfo.xres_virtual = fbinfo.xres; + fbinfo.yres_virtual = fbinfo.yres * 2; + if(fbinfo.yoffset == xres) + fbinfo.yoffset = fbinfo.yres; + } + fbinfo.rotate = rotation; + res = ioctl(fd, FBIOPUT_VSCREENINFO, &fbinfo); + if(res < 0) { + fprintf(stderr, "failed to set fbinfo: %s\n", strerror(errno)); + return 1; + } + + return 0; +}
diff --git a/toolbox/route.c b/toolbox/route.c new file mode 100644 index 0000000..3e10014 --- /dev/null +++ b/toolbox/route.c
@@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/route.h> + +static inline int set_address(const char *address, struct sockaddr *sa) { + return inet_aton(address, &((struct sockaddr_in *)sa)->sin_addr); +} + +/* current support the following routing entries */ +/* route add default dev wlan0 */ +/* route add default gw 192.168.1.1 dev wlan0 */ +/* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */ + +int route_main(int argc, char *argv[]) +{ + struct rtentry rt = { + .rt_dst = {.sa_family = AF_INET}, + .rt_genmask = {.sa_family = AF_INET}, + .rt_gateway = {.sa_family = AF_INET}, + }; + + errno = EINVAL; + if (argc > 2 && !strcmp(argv[1], "add")) { + if (!strcmp(argv[2], "default")) { + /* route add default dev wlan0 */ + if (argc > 4 && !strcmp(argv[3], "dev")) { + rt.rt_flags = RTF_UP; + rt.rt_dev = argv[4]; + errno = 0; + goto apply; + } + + /* route add default gw 192.168.1.1 dev wlan0 */ + if (argc > 6 && !strcmp(argv[3], "gw") && !strcmp(argv[5], "dev")) { + rt.rt_flags = RTF_UP | RTF_GATEWAY; + rt.rt_dev = argv[6]; + if (set_address(argv[4], &rt.rt_gateway)) { + errno = 0; + } + goto apply; + } + } + + /* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */ + if (argc > 7 && !strcmp(argv[2], "-net") && + !strcmp(argv[4], "netmask")) { + if (!strcmp(argv[6], "gw")) { + rt.rt_flags = RTF_UP | RTF_GATEWAY; + if (set_address(argv[3], &rt.rt_dst) && + set_address(argv[5], &rt.rt_genmask) && + set_address(argv[7], &rt.rt_gateway)) { + errno = 0; + } + goto apply; + } else if (!strcmp(argv[6], "dev")) { + rt.rt_flags = RTF_UP; + rt.rt_dev = argv[7]; + if (set_address(argv[3], &rt.rt_dst) && + set_address(argv[5], &rt.rt_genmask)) { + errno = 0; + } + goto apply; + } + } + } + +apply: + if (!errno) { + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s != -1 && (ioctl(s, SIOCADDRT, &rt) != -1 || errno == EEXIST)) { + return 0; + } + } + puts(strerror(errno)); + return errno; +}
diff --git a/toolbox/runcon.c b/toolbox/runcon.c new file mode 100644 index 0000000..4a57bf3 --- /dev/null +++ b/toolbox/runcon.c
@@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <selinux/selinux.h> + +int runcon_main(int argc, char **argv) +{ + int rc; + + if (argc < 3) { + fprintf(stderr, "usage: %s context program args...\n", argv[0]); + exit(1); + } + + rc = setexeccon(argv[1]); + if (rc < 0) { + fprintf(stderr, "Could not set context to %s: %s\n", argv[1], strerror(errno)); + exit(2); + } + + argv += 2; + argc -= 2; + execvp(argv[0], argv); + fprintf(stderr, "Could not exec %s: %s\n", argv[0], strerror(errno)); + exit(3); +}
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c new file mode 100644 index 0000000..6859b50 --- /dev/null +++ b/toolbox/schedtop.c
@@ -0,0 +1,336 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> + +#include <stdint.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <signal.h> + +#include <pwd.h> + +struct thread_info { + int pid; + int tid; + char name[64]; + uint64_t exec_time; + uint64_t delay_time; + uint32_t run_count; +}; + +struct thread_table { + size_t allocated; + size_t active; + struct thread_info *data; +}; + +enum { + FLAG_BATCH = 1U << 0, + FLAG_HIDE_IDLE = 1U << 1, + FLAG_SHOW_THREADS = 1U << 2, + FLAG_USE_ALTERNATE_SCREEN = 1U << 3, +}; + +static int time_dp = 9; +static int time_div = 1; +#define NS_TO_S_D(ns) \ + (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div) + +struct thread_table processes; +struct thread_table last_processes; +struct thread_table threads; +struct thread_table last_threads; + +static void grow_table(struct thread_table *table) +{ + size_t size = table->allocated; + struct thread_info *new_table; + if (size < 128) + size = 128; + else + size *= 2; + + new_table = realloc(table->data, size * sizeof(*table->data)); + if (new_table == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + table->data = new_table; + table->allocated = size; +} + +static struct thread_info *get_item(struct thread_table *table) +{ + if (table->active >= table->allocated) + grow_table(table); + return table->data + table->active; +} + +static void commit_item(struct thread_table *table) +{ + table->active++; +} + +static int read_line(char *line, size_t line_size) +{ + int fd; + int len; + fd = open(line, O_RDONLY); + if(fd == 0) + return -1; + len = read(fd, line, line_size - 1); + close(fd); + if (len <= 0) + return -1; + line[len] = '\0'; + return 0; +} + +static void add_thread(int pid, int tid, struct thread_info *proc_info) +{ + char line[1024]; + char *name, *name_end; + size_t name_len; + struct thread_info *info; + if(tid == 0) + info = get_item(&processes); + else + info = get_item(&threads); + info->pid = pid; + info->tid = tid; + + if(tid) + sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid); + else + sprintf(line, "/proc/%d/schedstat", pid); + if (read_line(line, sizeof(line))) + return; + if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3) + return; + if (proc_info) { + proc_info->exec_time += info->exec_time; + proc_info->delay_time += info->delay_time; + proc_info->run_count += info->run_count; + } + + name = NULL; + if (!tid) { + sprintf(line, "/proc/%d/cmdline", pid); + if (read_line(line, sizeof(line)) == 0 && line[0]) { + name = line; + name_len = strlen(name); + } + } + if (!name) { + if (tid) + sprintf(line, "/proc/%d/task/%d/stat", pid, tid); + else + sprintf(line, "/proc/%d/stat", pid); + if (read_line(line, sizeof(line))) + return; + name = strchr(line, '('); + if (name == NULL) + return; + name_end = strchr(name, ')'); + if (name_end == NULL) + return; + name++; + name_len = name_end - name; + } + if (name_len >= sizeof(info->name)) + name_len = sizeof(info->name) - 1; + memcpy(info->name, name, name_len); + info->name[name_len] = '\0'; + if(tid == 0) + commit_item(&processes); + else + commit_item(&threads); +} + +static void add_threads(int pid, struct thread_info *proc_info) +{ + char path[1024]; + DIR *d; + struct dirent *de; + sprintf(path, "/proc/%d/task", pid); + d = opendir(path); + if(d == 0) return; + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int tid = atoi(de->d_name); + add_thread(pid, tid, proc_info); + } + } + closedir(d); +} + +static void print_threads(int pid, uint32_t flags) +{ + size_t i, j; + for (i = 0; i < last_threads.active; i++) { + int epid = last_threads.data[i].pid; + int tid = last_threads.data[i].tid; + if (epid != pid) + continue; + for (j = 0; j < threads.active; j++) + if (tid == threads.data[j].tid) + break; + if (j == threads.active) + printf(" %5u died\n", tid); + else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count) + printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid, + NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time), + NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time), + threads.data[j].run_count - last_threads.data[i].run_count, + NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time), + threads.data[j].run_count, threads.data[j].name); + } +} + +static void update_table(DIR *d, uint32_t flags) +{ + size_t i, j; + struct dirent *de; + + rewinddir(d); + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int pid = atoi(de->d_name); + struct thread_info *proc_info; + add_thread(pid, 0, NULL); + proc_info = &processes.data[processes.active - 1]; + proc_info->exec_time = 0; + proc_info->delay_time = 0; + proc_info->run_count = 0; + add_threads(pid, proc_info); + } + } + if (!(flags & FLAG_BATCH)) + printf("\e[H\e[0J"); + printf("Processes: %d, Threads %d\n", processes.active, threads.active); + switch (time_dp) { + case 3: + printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n"); + printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n"); + break; + case 6: + printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n"); + printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + default: + printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n"); + printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + } + for (i = 0; i < last_processes.active; i++) { + int pid = last_processes.data[i].pid; + int tid = last_processes.data[i].tid; + for (j = 0; j < processes.active; j++) + if (pid == processes.data[j].pid) + break; + if (j == processes.active) + printf("%5u died\n", pid); + else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { + printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid, + NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), + NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), + processes.data[j].run_count - last_processes.data[i].run_count, + NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time), + processes.data[j].run_count, processes.data[j].name); + if (flags & FLAG_SHOW_THREADS) + print_threads(pid, flags); + } + } + + { + struct thread_table tmp; + tmp = last_processes; + last_processes = processes; + processes = tmp; + processes.active = 0; + tmp = last_threads; + last_threads = threads; + threads = tmp; + threads.active = 0; + } +} + +void +sig_abort(int signum) +{ + printf("\e[?47l"); + exit(0); +} + + +int schedtop_main(int argc, char **argv) +{ + int c; + DIR *d; + struct dirent *de; + char *namefilter = 0; + int pidfilter = 0; + uint32_t flags = 0; + int delay = 3000000; + float delay_f; + + while(1) { + c = getopt(argc, argv, "d:ibtamun"); + if (c == EOF) + break; + switch (c) { + case 'd': + delay_f = atof(optarg); + delay = delay_f * 1000000; + break; + case 'b': + flags |= FLAG_BATCH; + break; + case 'i': + flags |= FLAG_HIDE_IDLE; + break; + case 't': + flags |= FLAG_SHOW_THREADS; + break; + case 'a': + flags |= FLAG_USE_ALTERNATE_SCREEN; + break; + case 'm': + time_dp = 3; + time_div = 1000000; + break; + case 'u': + time_dp = 6; + time_div = 1000; + break; + case 'n': + time_dp = 9; + time_div = 1; + break; + } + } + + d = opendir("/proc"); + if(d == 0) return -1; + + if (!(flags & FLAG_BATCH)) { + if(flags & FLAG_USE_ALTERNATE_SCREEN) { + signal(SIGINT, sig_abort); + signal(SIGPIPE, sig_abort); + signal(SIGTERM, sig_abort); + printf("\e7\e[?47h"); + } + printf("\e[2J"); + } + while (1) { + update_table(d, flags); + usleep(delay); + } + closedir(d); + return 0; +}
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c new file mode 100644 index 0000000..a09b2b7 --- /dev/null +++ b/toolbox/sendevent.c
@@ -0,0 +1,82 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#if HAVE_GLIBC +#include <linux/input.h> // this does not compile +#else +// from <linux/input.h> + +struct input_event { + struct timeval time; + __u16 type; + __u16 code; + __s32 value; +}; +#endif + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ +#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */ +#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ + +// end <linux/input.h> + + + +int sendevent_main(int argc, char *argv[]) +{ + int i; + int fd; + int ret; + int version; + struct input_event event; + + if(argc != 5) { + fprintf(stderr, "use: %s device type code value\n", argv[0]); + return 1; + } + + fd = open(argv[1], O_RDWR); + if(fd < 0) { + fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + if (ioctl(fd, EVIOCGVERSION, &version)) { + fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno)); + return 1; + } + memset(&event, 0, sizeof(event)); + event.type = atoi(argv[2]); + event.code = atoi(argv[3]); + event.value = atoi(argv[4]); + ret = write(fd, &event, sizeof(event)); + if(ret < sizeof(event)) { + fprintf(stderr, "write event failed, %s\n", strerror(errno)); + return -1; + } + return 0; +}
diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c new file mode 100644 index 0000000..0159c07 --- /dev/null +++ b/toolbox/setconsole.c
@@ -0,0 +1,166 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/ioctl.h> + +static int activate_thread_switch_vc; +static void *activate_thread(void *arg) +{ + int res; + int fd = (int)arg; + while(activate_thread_switch_vc >= 0) { + do { + res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc); + } while(res < 0 && errno == EINTR); + if (res < 0) { + fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc); + } + if(activate_thread_switch_vc >= 0) + sleep(1); + } + return NULL; +} + + +int setconsole_main(int argc, char *argv[]) +{ + int c; + int fd; + int res; + + int mode = -1; + int new_vc = 0; + int close_vc = 0; + int switch_vc = -1; + int printvc = 0; + char *ttydev = "/dev/tty0"; + + do { + c = getopt(argc, argv, "d:gtncv:poh"); + if (c == EOF) + break; + switch (c) { + case 'd': + ttydev = optarg; + break; + case 'g': + if(mode == KD_TEXT) { + fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); + exit(1); + } + mode = KD_GRAPHICS; + break; + case 't': + if(mode == KD_GRAPHICS) { + fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); + exit(1); + } + mode = KD_TEXT; + break; + case 'n': + new_vc = 1; + break; + case 'c': + close_vc = 1; + break; + case 'v': + switch_vc = atoi(optarg); + break; + case 'p': + printvc |= 1; + break; + case 'o': + printvc |= 2; + break; + case 'h': + fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n" + " -d <dev> Use <dev> instead of /dev/tty0\n" + " -v <vc> Switch to virtual console <vc>\n" + " -g Switch to graphics mode\n" + " -t Switch to text mode\n" + " -n Create and switch to new virtual console\n" + " -c Close unused virtual consoles\n" + " -p Print new virtual console\n" + " -o Print old virtual console\n" + " -h Print help\n", argv[0]); + return -1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) { + fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]); + return -1; + } + + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", ttydev); + return -1; + } + + if ((printvc && !new_vc) || (printvc & 2)) { + struct vt_stat vs; + + res = ioctl(fd, VT_GETSTATE, &vs); + if (res < 0) { + fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res); + } + printf("%d\n", vs.v_active); + } + + if (new_vc) { + int vtnum; + res = ioctl(fd, VT_OPENQRY, &vtnum); + if (res < 0 || vtnum == -1) { + fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum); + } + switch_vc = vtnum; + } + if (switch_vc != -1) { + pthread_t thread; + pthread_attr_t attr; + activate_thread_switch_vc = switch_vc; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread, &attr, activate_thread, (void*)fd); + + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc); + } while(res < 0 && errno == EINTR); + activate_thread_switch_vc = -1; + if (res < 0) { + fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc); + } + if(printvc & 1) + printf("%d\n", switch_vc); + + close(fd); + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", ttydev); + return -1; + } + } + if (close_vc) { + res = ioctl(fd, VT_DISALLOCATE, 0); + if (res < 0) { + fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res); + } + } + if (mode != -1) { + if (ioctl(fd, KDSETMODE, (void*)mode) < 0) { + fprintf(stderr, "KDSETMODE %d failed\n", mode); + return -1; + } + } + return 0; +}
diff --git a/toolbox/setenforce.c b/toolbox/setenforce.c new file mode 100644 index 0000000..1b0ea5c --- /dev/null +++ b/toolbox/setenforce.c
@@ -0,0 +1,44 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <selinux/selinux.h> + +void usage(const char *progname) +{ + fprintf(stderr, "usage: %s [ Enforcing | Permissive | 1 | 0 ]\n", + progname); + exit(1); +} + +int setenforce_main(int argc, char **argv) +{ + int rc = 0; + if (argc != 2) { + usage(argv[0]); + } + + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", argv[0]); + return 1; + } + if (strlen(argv[1]) == 1 && (argv[1][0] == '0' || argv[1][0] == '1')) { + rc = security_setenforce(atoi(argv[1])); + } else { + if (strcasecmp(argv[1], "enforcing") == 0) { + rc = security_setenforce(1); + } else if (strcasecmp(argv[1], "permissive") == 0) { + rc = security_setenforce(0); + } else + usage(argv[0]); + } + if (rc < 0) { + fprintf(stderr, "%s: Could not set enforcing status: %s\n", + argv[0], strerror(errno)); + return 2; + } + return 0; +}
diff --git a/toolbox/setkey.c b/toolbox/setkey.c new file mode 100644 index 0000000..1ff2774 --- /dev/null +++ b/toolbox/setkey.c
@@ -0,0 +1,89 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include <errno.h> + +static void setkey_usage(char *argv[]) +{ + fprintf(stderr, "%s [-t <table>] [-k <index>] [-v value] [-r] [-h]\n" + " -t <table> Select table\n" + " -k <index> Select key\n" + " -v <value> Set entry\n" + " -r Read current entry\n" + " -h Print help\n", argv[0]); +} + +#define TTYDEV "/dev/tty0" + +int setkey_main(int argc, char *argv[]) +{ + int fd; + struct kbentry kbe; + int did_something = 0; + + kbe.kb_table = 0; + kbe.kb_index = -1; + kbe.kb_value = 0; + + fd = open(TTYDEV, O_RDWR | O_SYNC); + if (fd < 0) { + fprintf(stderr, "open %s: %s\n", TTYDEV, strerror(errno)); + return 1; + } + + do { + int c, ret; + + c = getopt(argc, argv, "t:k:v:hr"); + if (c == EOF) + break; + + switch (c) { + case 't': + kbe.kb_table = strtol(optarg, NULL, 0); + break; + case 'k': + kbe.kb_index = strtol(optarg, NULL, 0); + break; + case 'v': + kbe.kb_value = strtol(optarg, NULL, 0); + ret = ioctl(fd, KDSKBENT, &kbe); + if (ret < 0) { + fprintf(stderr, "KDSKBENT %d %d %d failed: %s\n", + kbe.kb_table, kbe.kb_index, kbe.kb_value, + strerror(errno)); + return 1; + } + did_something = 1; + break; + case 'r': + ret = ioctl(fd, KDGKBENT, &kbe); + if (ret < 0) { + fprintf(stderr, "KDGKBENT %d %d failed: %s\n", + kbe.kb_table, kbe.kb_index, strerror(errno)); + return 1; + } + printf("0x%x 0x%x 0x%x\n", + kbe.kb_table, kbe.kb_index, kbe.kb_value); + did_something = 1; + break; + case 'h': + setkey_usage(argv); + return 1; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + return 1; + } + } while (1); + + if(optind != argc || !did_something) { + setkey_usage(argv); + return 1; + } + + return 0; +}
diff --git a/toolbox/setprop.c b/toolbox/setprop.c new file mode 100644 index 0000000..63ad2b4 --- /dev/null +++ b/toolbox/setprop.c
@@ -0,0 +1,18 @@ +#include <stdio.h> + +#include <cutils/properties.h> + +int setprop_main(int argc, char *argv[]) +{ + if(argc != 3) { + fprintf(stderr,"usage: setprop <key> <value>\n"); + return 1; + } + + if(property_set(argv[1], argv[2])){ + fprintf(stderr,"could not set property\n"); + return 1; + } + + return 0; +}
diff --git a/toolbox/setsebool.c b/toolbox/setsebool.c new file mode 100644 index 0000000..4a3d87d --- /dev/null +++ b/toolbox/setsebool.c
@@ -0,0 +1,55 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <selinux/selinux.h> +#include <errno.h> + +static int do_setsebool(int nargs, char **args) { + SELboolean *b = alloca(nargs * sizeof(SELboolean)); + char *v; + int i; + + if (is_selinux_enabled() <= 0) + return 0; + + for (i = 1; i < nargs; i++) { + char *name = args[i]; + v = strchr(name, '='); + if (!v) { + fprintf(stderr, "setsebool: argument %s had no =\n", name); + return -1; + } + *v++ = 0; + b[i-1].name = name; + if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) + b[i-1].value = 1; + else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) + b[i-1].value = 0; + else { + fprintf(stderr, "setsebool: invalid value %s\n", v); + return -1; + } + } + + if (security_set_boolean_list(nargs - 1, b, 0) < 0) + { + fprintf(stderr, "setsebool: unable to set booleans: %s", strerror(errno)); + return -1; + } + + return 0; +} + +int setsebool_main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s name=value...\n", argv[0]); + exit(1); + } + + return do_setsebool(argc, argv); +}
diff --git a/toolbox/sleep.c b/toolbox/sleep.c new file mode 100644 index 0000000..c09ae03 --- /dev/null +++ b/toolbox/sleep.c
@@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +static void +usage(const char *s) +{ + fprintf(stderr, "USAGE: %s SECONDS\n", s); + exit(-1); +} + +int sleep_main(int argc, char *argv[]) +{ + unsigned long seconds; + char *endptr; + + if (argc != 2) { + usage(argv[0]); + } + + seconds = strtoul(argv[1], &endptr, 10); + + if (endptr == argv[1]) { + usage(argv[0]); + } + + + sleep((unsigned int)seconds); + + return 0; +} + +
diff --git a/toolbox/smd.c b/toolbox/smd.c new file mode 100644 index 0000000..91e495c --- /dev/null +++ b/toolbox/smd.c
@@ -0,0 +1,41 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +int smd_main(int argc, char **argv) +{ + int fd, len, r, port = 0; + char devname[32]; + argc--; + argv++; + + if((argc > 0) && (argv[0][0] == '-')) { + port = atoi(argv[0] + 1); + argc--; + argv++; + } + + sprintf(devname,"/dev/smd%d",port); + fd = open(devname, O_WRONLY); + if(fd < 0) { + fprintf(stderr,"failed to open smd0 - %s\n", + strerror(errno)); + return -1; + } + while(argc > 0) { + len = strlen(argv[0]); + r = write(fd, argv[0], len); + if(r != len) { + fprintf(stderr,"failed to write smd0 (%d) %s\n", + r, strerror(errno)); + return -1; + } + argc--; + argv++; + write(fd, argc ? " " : "\r", 1); + } + close(fd); + return 0; +}
diff --git a/toolbox/sntpd.c b/toolbox/sntpd.c new file mode 100644 index 0000000..d100f13 --- /dev/null +++ b/toolbox/sntpd.c
@@ -0,0 +1,679 @@ +/* + * Copyright (C) 2012 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 <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <unistd.h> +#include <time.h> + +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/prctl.h> + +#include <linux/capability.h> +#include <linux/prctl.h> + +#include <netinet/in.h> +#include <netdb.h> + +#include <cutils/properties.h> +#include <private/android_filesystem_config.h> + +#define SNTP_DEBUG 0 + +/* NTP packet format */ +#define REFERENCE_TIME_OFFSET 16 +#define ORIGINATE_TIME_OFFSET 24 +#define RECEIVE_TIME_OFFSET 32 +#define TRANSMIT_TIME_OFFSET 40 +#define NTP_PACKET_SIZE 48 +#define NTP_MODE_CLIENT 3 +#define NTP_MODE_UNICAST 4 +#define NTP_MODE_BROADCAST 5 +#define NTP_VERSION 3 + +/* NTP port */ +#define NTP_PORT 123 + +/* NTP service */ +#define NTP_SERVICE "123" + +/* timeout value for udp response - 5 sec */ +#define NTP_TIMEOUT (5*1000) + +/* wait for 1 sec before retry in case of failure */ +#define RETRY_DELAY (1*1000) + +/* for the 1st time, wait for 1 sec before retry in case of failure */ +#define INITIAL_RETRY_DELAY (1*1000) + +/* Exponential backoff for retries */ +#define RETRY_BACKOFF (2) + +#define MAX_RETRY_DELAY (1048 * 1000) + +/* update internal - 20 minutes (with jitter will be 10-20 minutes) */ +#define UPDATE_INTERVAL (20*60*1000) + +/* threshold to update system time - 100 ms */ +#define UPDATE_THRESHOLD (100ll) + +/* maximum time drift allowed to filter out response from malfunctioning server + * - 20 sec */ +#define MAX_TIME_DRIFT (20ll*1000ll) + +/* Reject times which are this much older than the build time */ +#define MAX_BEFORE_BUILD_TIME (60 * 60 * 1000) + +#define BUF_SIZE 128 + +extern int capset(cap_user_header_t header, cap_user_data_t data); + +static const unsigned long long OFFSET_1900_TO_1970 = + ((365LL * 70LL) + 17LL) * 24LL * 60LL * 60LL; +static const char *default_ntp_server = "time.google.com"; + +static const char kPathSntpdLastSync[] = "/data/share/sntpd/last_sync"; + +static int count_getaddrinfo_failures = 0; + +typedef enum { + TIME_SYNC_SUCCESS = 0, + TIME_SYNC_ADDRESS_RESOLUTION_FAILURE = -1, + TIME_SYNC_OTHER_FAILURE = -2 +} TimeSyncStatus; + +// Gets the current build time. Return 0 on success. +static inline int get_build_time(struct timeval *tv) { + const char kPropBuildDate[] = "ro.build.date.utc"; + char build_time_str[PROPERTY_VALUE_MAX]; + if (property_get(kPropBuildDate, build_time_str, "") <= 0) { + fprintf(stderr, "Failed to get %s\n", kPropBuildDate); + return -1; + } + + errno = 0; + unsigned long long parsed = strtoull(build_time_str, NULL, 0); + if (errno != 0) { + fprintf(stderr, "Failed to parse %s: %s\n", kPropBuildDate, + strerror(errno)); + return -1; + } + + tv->tv_sec = parsed; + tv->tv_usec = 0; + return 0; +} + +static inline void record_localtime_str(struct timeval *tv, char *buf, + size_t buf_size) { + time_t nowtime = tv->tv_sec; + struct tm *nowtm = localtime(&nowtime); + size_t len = strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", nowtm); + + int fd = open(kPathSntpdLastSync, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", kPathSntpdLastSync, + strerror(errno)); + return; + } + if (write(fd, buf, len) != (ssize_t)len) { + fprintf(stderr, "Write file error: %s\n", strerror(errno)); + close(fd); + return; + } + // NOTE(gfhuang): For unknown reason, without this fchmod workaround, the + // file permission stucks at 0600. + if (fchmod(fd, 0644) != 0) { + fprintf(stderr, "fchmod error: %s\n", strerror(errno)); + } + close(fd); +} + +/** +* read an unsigned 32 bit big endian number from the given offset in the buffer. +*/ +static inline unsigned int read32(unsigned char *buffer, int offset) +{ + int i; + unsigned int ret=0; + + for (i = 0; i < 4; i ++) { + ret <<= 8; + ret |= (unsigned int) buffer[offset+i]; + } + return ret; +} + +/** +* read the NTP time stamp at the given offset in the buffer and returns +* it as a system time (milliseconds since January 1, 1970). +*/ +static inline unsigned long long read_timestamp(unsigned char *buffer, + unsigned int offset) +{ + unsigned int seconds = read32(buffer, offset); + unsigned int fraction = read32(buffer, offset + 4); + + return ((unsigned long long)seconds - OFFSET_1900_TO_1970) * 1000L + + ((unsigned long long)fraction * 1000L) / (1LL<<32); +} + + +/** +* write system time (milliseconds since January 1, 1970) as an NTP time stamp +* at the given offset in the buffer. +*/ +static inline void write_timeStamp(unsigned char *buffer, int offset, unsigned long long time) +{ + unsigned int seconds = time / 1000L; + unsigned int milliseconds = time % 1000L; + + seconds += OFFSET_1900_TO_1970; + // write seconds in big endian format + buffer[offset++] = (unsigned char)(seconds >> 24); + buffer[offset++] = (unsigned char)(seconds >> 16); + buffer[offset++] = (unsigned char)(seconds >> 8); + buffer[offset++] = (unsigned char)(seconds >> 0); + + long fraction = milliseconds * (1LL<<32) / 1000L; + // write fraction in big endian format + buffer[offset++] = (unsigned char)(fraction >> 24); + buffer[offset++] = (unsigned char)(fraction >> 16); + buffer[offset++] = (unsigned char)(fraction >> 8); + buffer[offset++] = (unsigned char)(fraction >> 0); +} + +#if SNTP_DEBUG +static void dump_timestamp(unsigned long long stamp, const char *name) +{ + fprintf(stdout, "%20s:\t%08x%08x\n", + name, + (int)(stamp>>32), + (int)stamp); +} +#endif // SNTP_DEBUG + +/* convert timeval to millisecond */ +static inline unsigned long long tv_to_ms(struct timeval *tv) +{ + unsigned long long ret = (unsigned long long) + tv->tv_sec * 1000L + + (unsigned long long)tv->tv_usec / 1000LL; + + return ret; +} + +/* convert millisecond to timeval */ +static inline void ms_to_tv(struct timeval *tv, unsigned long long ms) +{ + tv->tv_sec = ms / 1000; + tv->tv_usec = 1000 * (ms % 1000); +} + +/* convert timeval to millisecond */ +static inline unsigned long long ts_to_ms(const struct timespec *ts) +{ + return (unsigned long long)ts->tv_sec * 1000LL + + (unsigned long long)ts->tv_nsec / 1000000LL; +} + +static uint64_t random_int64() { + uint64_t ret = random() | ((uint64_t)random() << 32); + FILE* f = fopen("/dev/urandom", "r"); + if (!f) { + fprintf(stderr, "Warning: Failed to read from /dev/urandom %s\n", + strerror(errno)); + return ret; + } + fread(&ret, 1, sizeof(ret), f); + fclose(f); + return ret; +} + +typedef int(*sleep_fn)(const struct timespec *req, struct timespec *rem); +static void sleep_ms_with_jitter(uint64_t time_ms, sleep_fn sleep_fn_ptr) { + uint64_t jitter = random_int64() % (time_ms / 2); + time_ms = (time_ms / 2) + jitter; + + fprintf(stderr, "sleeping for %" PRId64 " milliseconds\n", time_ms); + struct timespec req = { + .tv_sec = time_ms / 1000, + .tv_nsec = (time_ms % 1000) * 1000000, + }; + sleep_fn_ptr(&req, NULL /* rem */); +} + +static TimeSyncStatus sntp_gettime(struct timeval *tv_upd) +{ + struct addrinfo hints; + struct addrinfo *res = 0, *rp = 0; + int s = -1; + TimeSyncStatus ret = TIME_SYNC_OTHER_FAILURE; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + + /* get addrinfo */ + s = getaddrinfo(default_ntp_server, NTP_SERVICE, &hints, &res); + if (s || !res) { + if (count_getaddrinfo_failures++ == 10) { + count_getaddrinfo_failures = 0; + fprintf(stderr, "error getaddrinfo: %s\n", gai_strerror(s)); + } + return TIME_SYNC_ADDRESS_RESOLUTION_FAILURE; + } + + /* + * pool.ntp.org is multihomed. Cycle through the server list until synced + * with one of them. + */ + for (rp = res; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET) { +#if SNTP_DEBUG + struct sockaddr_in *s = (struct sockaddr_in*)rp->ai_addr; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &s->sin_addr, buf, sizeof(buf)); + fprintf(stdout, "ipv4, %s\n", buf); +#endif // SNTP_DEBUG + } else if (rp->ai_family == AF_INET6) { +#if SNTP_DEBUG + struct sockaddr_in6 *s = (struct sockaddr_in6*)rp->ai_addr; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &s->sin6_addr, buf, sizeof(buf)); + fprintf(stdout, "ipv6, %s\n", buf); +#endif // SNTP_DEBUG + } else { + continue; + } + + /* create socket */ + int sock = socket(rp->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + fprintf(stderr, "error open socket: %s\n", strerror(errno)); + continue; + } + + /* set socket timeout */ + struct timeval tv; + ms_to_tv(&tv, NTP_TIMEOUT); + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ) { + fprintf(stderr, "error set socket option: %s\n", + strerror(errno)); + close(sock); + continue; + } + + if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ) { + fprintf(stderr, "error set socket option: %s\n", + strerror(errno)); + close(sock); + continue; + } + + /* write NTP packet */ + unsigned char send_buffer[NTP_PACKET_SIZE] = {0}; + send_buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); + if (gettimeofday(&tv, NULL)) { + fprintf(stderr, "error gettimeofday: %s\n", strerror(errno)); + close(sock); + continue; + } + + /* get a monotonic request time */ + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + fprintf(stderr, "error clock_gettime: %s\n", strerror(errno)); + close(sock); + continue; + } + + unsigned long long request_time = tv_to_ms(&tv); + unsigned long long real_request_time = ts_to_ms(&now); + + write_timeStamp(send_buffer, TRANSMIT_TIME_OFFSET, request_time); + + /* send socket */ + if (sendto(sock, send_buffer, NTP_PACKET_SIZE, 0, + (const struct sockaddr *)rp->ai_addr, + rp->ai_addrlen) == -1) { + fprintf (stderr, "error in sendto: %s\n", strerror(errno)); + close(sock); + continue; + } + + /* receive socket */ + struct sockaddr_in recv_addr4 = {0}; + struct sockaddr_in6 recv_addr6 = {0}; + bool is_ipv4 = rp->ai_family == AF_INET; + struct sockaddr* sock_addr = is_ipv4 ? + (struct sockaddr*)&recv_addr4 : (struct sockaddr*)&recv_addr6; + socklen_t sockaddr_len = is_ipv4 ? + sizeof(recv_addr4) : sizeof(recv_addr6); + + unsigned char buffer[NTP_PACKET_SIZE] = {0}; + if (recvfrom(sock, buffer, NTP_PACKET_SIZE, 0, + sock_addr, + &sockaddr_len) != NTP_PACKET_SIZE) { + fprintf (stderr, "error in recvfrom %s\n", strerror(errno)); + close(sock); + continue; + } + + close(sock); + + // https://tools.ietf.org/html/rfc4330#section-5 + // 1. When the IP source and destination addresses are available for + // the client request, they should match the interchanged addresses + // in the server reply. + // 2. When the UDP source and destination ports are available for the + // client request, they should match the interchanged ports in the + // server reply. + bool matches_sender = true; + if (is_ipv4) { + struct sockaddr_in *s = (struct sockaddr_in*)rp->ai_addr; + matches_sender = + (memcmp(&s->sin_addr, &recv_addr4.sin_addr, + sizeof(s->sin_addr)) == 0) && + (s->sin_port == recv_addr4.sin_port); + + } else { + struct sockaddr_in6 *s = (struct sockaddr_in6*)rp->ai_addr; + matches_sender = + (memcmp(&s->sin6_addr, &recv_addr6.sin6_addr, + sizeof(s->sin6_addr)) == 0) && + (s->sin6_port == recv_addr6.sin6_port); + } + if (!matches_sender) { + fprintf (stderr, "Recived packet doesn't match sender\n"); + continue; + } + + // https://tools.ietf.org/html/rfc4330#section-5 + // 3. The Originate Timestamp in the server reply should match the + // Transmit Timestamp used in the client request. + // + // We compare the buffers directly because {read,write}_timestamp do not + // perfectly reverse each other. + if (memcmp(send_buffer + TRANSMIT_TIME_OFFSET, + buffer + ORIGINATE_TIME_OFFSET, 8) != 0) { + fprintf(stderr, "Originate response doesn't match request time\n"); + continue; + } + + // https://tools.ietf.org/html/rfc4330#section-5 + // 4. The server reply should be discarded if any of the LI, Stratum, + // or Transmit Timestamp fields is 0 or the Mode field is not 4 + // (unicast) or 5 (broadcast). + // + // We don't care about LI (Leap (second) Indicator) because time within + // 1 second is good enough for us. + + uint8_t stratum = buffer[1]; + if (stratum == 0) { + fprintf(stderr, "Stratum is zero\n"); + continue; + } + + bool is_transmit_zero = true; + for (int i = 0; i < 8; ++i) { + if (buffer[TRANSMIT_TIME_OFFSET + i] != 0) { + is_transmit_zero = false; + break; + } + } + + if (is_transmit_zero) { + fprintf(stderr, "Transmit time is zero\n"); + continue; + } + + uint8_t mode = buffer[0] & 0x7; + if (mode != NTP_MODE_UNICAST && mode != NTP_MODE_BROADCAST) { + fprintf(stderr, "Mode is not server or broadcast: %" PRIu8 "\n", + mode); + continue; + } + + + /* receiveTime = originateTime + transit + skew + responseTime = transmitTime + transit - skew + clockOffset = ((receiveTime - originateTime) + (transmitTime + - responseTime))/2 + = ((originateTime + transit + skew - originateTime) + + (transmitTime - (transmitTime + transit - skew)))/2 + = ((transit + skew) + (transmitTime - transmitTime - + transit + skew))/2 + = (transit + skew - transit + skew)/2 + = (2 * skew)/2 = skew + */ + /* get a monotonic respond time */ + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + fprintf(stderr, "error clock_gettime: %s\n", strerror(errno)); + continue; + } + + // These must be signed values in order for the calculation to work + // correctly. Signed 64 bits of milliseconds is still a long time. + // + // >>> 2**63 / (1000 * 60 * 60 * 24 * 365) + // 292471208L + long long response_time = + request_time + (ts_to_ms(&now) - real_request_time); + long long originate_time = read_timestamp(buffer, + ORIGINATE_TIME_OFFSET); + long long receive_time = read_timestamp(buffer, RECEIVE_TIME_OFFSET); + long long transmit_time = read_timestamp(buffer, TRANSMIT_TIME_OFFSET); + + // https://tools.ietf.org/html/rfc4330#section-5 + // Timestamp Name ID When Generated + // ------------------------------------------------------------ + // Originate Timestamp T1 time request sent by client + // Receive Timestamp T2 time request received by server + // Transmit Timestamp T3 time reply sent by server + // Destination Timestamp T4 time reply received by client + // + // The roundtrip delay d and system clock offset t are defined as: + // + // d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. + + // d cannot be negative. + long long d = + (response_time - originate_time) - (transmit_time - receive_time); + if (d < 0) { + fprintf(stderr, "d is negative\n"); + continue; + } + + long long t = + (receive_time - originate_time) + (transmit_time - response_time); + t /= 2; + + long long updated_time = (long long)request_time + t; + if (updated_time < 0) { + fprintf(stderr, "Reported time is negative\n"); + continue; + } + + ms_to_tv(tv_upd, updated_time); + + struct timeval tv_build_time; + if (get_build_time(&tv_build_time) == 0) { + long long build_time_ms = tv_to_ms(&tv_build_time); + if (updated_time + MAX_BEFORE_BUILD_TIME < build_time_ms) { + fprintf(stderr, "Reported time is too old\n"); + continue; + } + } + +#if SNTP_DEBUG + int i; + for (i =0; i<NTP_PACKET_SIZE; i++) { + fprintf(stdout, "%02x", buffer[i]); + if ((i&15) == 15) { + fprintf(stdout, "\n"); + } + } + dump_timestamp(originate_time, "originate"); + dump_timestamp(receive_time, "receive"); + dump_timestamp(transmit_time, "transmit"); + dump_timestamp(request_time, "request"); + dump_timestamp(response_time, "response"); + printf("updated\n"); + dump_timestamp(updated_time, "update"); +#endif + ret = TIME_SYNC_SUCCESS; + break; + } + + freeaddrinfo(res); + return ret; +} + +typedef TimeSyncStatus (*sntp_gettime_fn)(struct timeval *tv_upd); +static TimeSyncStatus try_sync_time(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) { + struct timeval tv_upd; + + TimeSyncStatus ret = sntp_gettime_fn_ptr(&tv_upd); + if (ret != TIME_SYNC_SUCCESS) { + return ret; + } + + struct timeval tv_cur; + unsigned long long ms_cur, ms_upd, ms_abs_diff; + + /* check threshold */ + if (gettimeofday(&tv_cur, NULL)) { + fprintf(stderr, "error gettimeofday: %s\n", + strerror(errno)); + return TIME_SYNC_OTHER_FAILURE; + } + ms_cur = tv_to_ms(&tv_cur); + ms_upd = tv_to_ms(&tv_upd); + + // unsigned abs diff + if (ms_cur > ms_upd) { + ms_abs_diff = ms_cur - ms_upd; + } else { + ms_abs_diff = ms_upd - ms_cur; + } + + /* + * always sync for the first time, since we don't have + * battery-backed RTC. + * + * update system time if out-of-sync exceeds threshold. + * rarely happens after initial sync (synced==1). + */ + if (!synced || ms_abs_diff > UPDATE_THRESHOLD) { + /* + * discard spurious response from server if it diffs too much + * with local system time. + */ + if (synced && ms_abs_diff >= MAX_TIME_DRIFT) { + fprintf(stderr, "discarding %ullms time drift from server. \n", + ms_abs_diff); + } else if (settimeofday(&tv_upd, NULL)) { + fprintf(stderr, "error settimeofday: %s\n", + strerror(errno)); + return TIME_SYNC_OTHER_FAILURE; + } else { + char buf[BUF_SIZE]; + record_localtime_str(&tv_upd, buf, BUF_SIZE); + fprintf(stderr, "synced at %s\n", buf); + } + } else { + char buf[BUF_SIZE]; + record_localtime_str(&tv_cur, buf, BUF_SIZE); + fprintf(stderr, "no change, already synced as %s\n", buf); + } + + return TIME_SYNC_SUCCESS; +} + +typedef TimeSyncStatus(*sync_time_fn)(bool, sntp_gettime_fn); +static void do_sync(bool* synced, unsigned long long* retry_delay, + sync_time_fn sync_time_fn_ptr, sleep_fn sleep_fn_ptr, + sntp_gettime_fn sntp_gettime_fn_ptr) { + TimeSyncStatus sync_time_result = sync_time_fn_ptr(*synced, sntp_gettime_fn_ptr); + if (sync_time_result == TIME_SYNC_SUCCESS) { + *synced = true; + *retry_delay = RETRY_DELAY; + sleep_ms_with_jitter(UPDATE_INTERVAL, sleep_fn_ptr); + } else if (sync_time_result == TIME_SYNC_ADDRESS_RESOLUTION_FAILURE) { + /* Failure because of Internet offline, keeps retrying*/ + sleep_ms_with_jitter(*retry_delay, sleep_fn_ptr); + } else { + /* Failure because of other reasons, try exponential backoff */ + sleep_ms_with_jitter(*retry_delay, sleep_fn_ptr); + if (*retry_delay < MAX_RETRY_DELAY) { + *retry_delay *= RETRY_BACKOFF; + } + } +} + +#ifdef STANDALONE +int main(int argc, char *argv[]) +#else +int sntpd_main(int argc, char *argv[]) +#endif +{ + (void)argc; + + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap[_LINUX_CAPABILITY_U32S_3]; + bool synced = false; + + /* only can run as kernel */ + if (getuid() != 0) { + fprintf(stderr, "%s needs root to run\n", argv[0]); + return -1; + } + + /* setuid and keep capability */ + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + setgid(AID_INET); + setuid(AID_SNTPD); + + header.version = _LINUX_CAPABILITY_VERSION_3; + header.pid = 0; + memset(cap, 0, sizeof(cap)); + int idx = CAP_TO_INDEX(CAP_SYS_TIME); + cap[idx].effective = CAP_TO_MASK(CAP_SYS_TIME); + cap[idx].permitted = CAP_TO_MASK(CAP_SYS_TIME); + cap[idx].inheritable = 0; + if (capset(&header, cap)) { + fprintf(stderr, "setcap error: %s\n", strerror(errno)); + return -1; + } + + unsigned long long retry_delay = INITIAL_RETRY_DELAY; + + while (1) { + do_sync(&synced, &retry_delay, try_sync_time, nanosleep, sntp_gettime); + } + return 0; +}
diff --git a/toolbox/sntpd_unittest.c b/toolbox/sntpd_unittest.c new file mode 100644 index 0000000..df26de1 --- /dev/null +++ b/toolbox/sntpd_unittest.c
@@ -0,0 +1,169 @@ +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <sys/types.h> +#include <time.h> + +#include "sntpd.c" + +static uint64_t g_total_sleep_ms = 0; +static struct timeval g_updated_tv; +static struct timeval g_tv_to_return; + +//override nanosleep +int nanosleep_mock(const struct timespec *tv_up, struct timespec *rem) { + (void)rem; + g_total_sleep_ms += ts_to_ms(tv_up); + return 0; +} + +//override settimeofday +int settimeofday (const struct timeval* tv, + const struct timezone* tz) { + assert(tv); + g_updated_tv = *tv; + return 0; +} + +int try_sync_time_mock_error(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) { + return -1; +} + +int try_sync_time_mock_success(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) { + return 0; +} + +int sntp_gettime_mock(struct timeval *tv_upd) { + assert(tv_upd); + *tv_upd = g_tv_to_return; + return 0; +} + +void test_sync_loop_on_error() { + g_total_sleep_ms = 0; + bool synced = false; + unsigned long long retry_delay = INITIAL_RETRY_DELAY; + for(int i = 0; i < 20; i++) { + do_sync(&synced, &retry_delay, try_sync_time_mock_error, nanosleep_mock, NULL); + } + fprintf(stdout, "Total sleep time is %" PRId64 " ms.\n", g_total_sleep_ms); + assert(g_total_sleep_ms >= 10239500 && g_total_sleep_ms < 20479000); +} + +void test_sync_loop_on_success() { + g_total_sleep_ms = 0; + bool synced = false; + unsigned long long retry_delay = INITIAL_RETRY_DELAY; + for(int i = 0; i < 20; i++) { + do_sync(&synced, &retry_delay, try_sync_time_mock_success, nanosleep_mock, NULL); + } + fprintf(stdout, "Total sleep time is %" PRId64 " ms.\n", g_total_sleep_ms); + assert(g_total_sleep_ms >= 12000000 && g_total_sleep_ms < 24000000); +} + +void test_try_sync_time() { + //Set the time when synced is false. + unsigned long long time_ms; + g_updated_tv.tv_sec = 0; + g_updated_tv.tv_usec = 0; + g_tv_to_return.tv_usec = 0; + g_tv_to_return.tv_sec = 0; + int rv = try_sync_time(false, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == 0); + assert(g_updated_tv.tv_usec == 0); + + //Synced is true, and diff > MAX_TIME_DRIFT + //settimeofday should not be called. + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + g_tv_to_return.tv_sec = 0; + g_tv_to_return.tv_usec = 0; + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == 1234); + assert(g_updated_tv.tv_usec == 123456); + + //Synced is true, local clock lagging, diff < UPDATE_INTERVAL + //settimeofday should not be called. + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms += 456; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == 1234); + assert(g_updated_tv.tv_usec == 123456); + + //Synced is true, local clock ahead, diff < UPDATE_INTERVAL + //settimeofday should not be called. + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms -= 456; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == 1234); + assert(g_updated_tv.tv_usec == 123456); + + //Synced is true, local clock lagging, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms += 501; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec); + assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec); + + //Synced is true, local clock ahead, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms -= 501; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec); + assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec); + + //Synced is true, local clock ahead, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms -= 17975; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec); + assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec); + + //Synced is true, local clock lagging, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT + g_updated_tv.tv_sec = 1234; + g_updated_tv.tv_usec = 123456; + assert(!gettimeofday(&g_tv_to_return, NULL)); + time_ms = tv_to_ms(&g_tv_to_return); + time_ms += 11221; + ms_to_tv(&g_tv_to_return, time_ms); + rv = try_sync_time(true, sntp_gettime_mock); + assert(rv == 0); + assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec); + assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec); +} + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + test_sync_loop_on_success(); + test_sync_loop_on_error(); + test_try_sync_time(); + return 0; +}
diff --git a/toolbox/start.c b/toolbox/start.c new file mode 100644 index 0000000..665a941 --- /dev/null +++ b/toolbox/start.c
@@ -0,0 +1,21 @@ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <cutils/properties.h> + +int start_main(int argc, char *argv[]) +{ + char buf[1024]; + + if(argc > 1) { + property_set("ctl.start", argv[1]); + } else { + /* defaults to starting the common services stopped by stop.c */ + property_set("ctl.start", "surfaceflinger"); + property_set("ctl.start", "zygote"); + } + + return 0; +}
diff --git a/toolbox/stop.c b/toolbox/stop.c new file mode 100644 index 0000000..460f377 --- /dev/null +++ b/toolbox/stop.c
@@ -0,0 +1,19 @@ +#include <stdio.h> +#include <string.h> + +#include <cutils/properties.h> + +int stop_main(int argc, char *argv[]) +{ + char buf[1024]; + + if(argc > 1) { + property_set("ctl.stop", argv[1]); + } else{ + /* defaults to stopping the common services */ + property_set("ctl.stop", "zygote"); + property_set("ctl.stop", "surfaceflinger"); + } + + return 0; +}
diff --git a/toolbox/sudo.c b/toolbox/sudo.c new file mode 100644 index 0000000..bec08ef --- /dev/null +++ b/toolbox/sudo.c
@@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> + +#include <private/android_filesystem_config.h> + +/* we support up to 32 supplementary group ID */ +#define NR_SUPP_GIDS 32 + +static void usage(int argc, char *argv[]) +{ + fprintf(stdout, + "Usage: sudo -u UID [-g GID[:GID]...] FILE [ARG] ...\n"); +} + +int sudo_main(int argc, char *argv[]) +{ + uid_t uid = -1, myuid; + gid_t gid = -1, supp_gids[NR_SUPP_GIDS]; + int nr_supp_gids = 0, i, index; + + /* only root can sudo */ + myuid = getuid(); + if (myuid != 0) { + fprintf(stderr, "sudo: only root can sudo, %d is not allowed", myuid); + exit(-1); + } + + if(argc < 4) { + usage(argc, argv); + exit(-1); + } + + index = 1; + while (index < argc - 1) { + if (!strcmp("-u", argv[index])) { + /* parse uid */ + char *user = argv[++index]; + struct passwd *pw; + + pw = getpwnam(user); + if (pw == 0) { + fprintf(stderr, "%s not a valid user\n", user); + exit(-1); + } else { + uid = pw->pw_uid; + gid = pw->pw_gid; + setenv("HOME", pw->pw_dir, 1); + } + } else if (!strcmp("-g", argv[index])) { + /* parse (supplementary) gids */ + char * current = argv[++index], *next; + struct group *grp; + + for (i = 0; current && (i < NR_SUPP_GIDS); ++i) { + next = strchr(current, ':'); + if (next) { + *next++ = '\0'; + } + + grp = getgrnam(current); + if (grp == NULL) { + fprintf(stderr, "%s is not a valid group\n", current); + exit(-1); + } else { + supp_gids[i] = grp->gr_gid; + } + current = next; + } + nr_supp_gids = i; + } else { + break; + } + ++index; + } + + if (uid == (uid_t)-1 || uid == 0) { + usage (argc, argv); + exit(-1); + } + + /* set supplemtary groups */ + if (nr_supp_gids && setgroups(nr_supp_gids, supp_gids)) { + fprintf(stderr, "sudo: setgroups error %s\n", strerror(errno)); + exit(-1); + } + + /* set uid and gid */ + if (setgid(gid) || setuid(uid)) { + fprintf(stderr, "sudo: %s\n", strerror(errno)); + exit(-1); + } + + if (index == argc) { + usage(argc, argv); + exit(-1); + } else { + /* Copy the rest of the args from main. */ + int nr_args = argc - index; + char **exec_args = (char **)malloc(sizeof(char*) + * (nr_args + 1)); + if (!exec_args) { + fprintf(stderr, "memory alloc error %s\n", strerror(errno)); + exit(-1); + } + memcpy(exec_args, &argv[index], sizeof(*exec_args) * nr_args); + exec_args[nr_args] = NULL; + + if (execvp(argv[index], exec_args)) { + fprintf(stderr, "%s: %s exec failed\n", argv[0], argv[index]); + } + } + + return -1; +}
diff --git a/toolbox/sync.c b/toolbox/sync.c new file mode 100644 index 0000000..8284276 --- /dev/null +++ b/toolbox/sync.c
@@ -0,0 +1,7 @@ +#include <unistd.h> + +int sync_main(int argc, char **argv) +{ + sync(); + return 0; +}
diff --git a/toolbox/syren.c b/toolbox/syren.c new file mode 100644 index 0000000..06e329e --- /dev/null +++ b/toolbox/syren.c
@@ -0,0 +1,154 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <malloc.h> + +/* ioctl crap */ +#define SYREN_RD 101 +#define SYREN_WR 102 +#define SYREN_OLD_RD 108 +#define SYREN_OLD_WR 109 + +struct syren_io_args { + unsigned long page; + unsigned long addr; + unsigned long value; +}; + +typedef struct { + u_char page; + u_char addr; + const char *name; +} syren_reg; + +static syren_reg registers[] = { + { 0, 0x04, "TOGBR1" }, + { 0, 0x05, "TOGBR2" }, + { 0, 0x06, "VBDCTRL" }, + { 1, 0x07, "VBUCTRL" }, + { 1, 0x08, "VBCTRL" }, + { 1, 0x09, "PWDNRG" }, + { 1, 0x0a, "VBPOP" }, + { 1, 0x0b, "VBCTRL2" }, + { 1, 0x0f, "VAUDCTRL" }, + { 1, 0x10, "VAUSCTRL" }, + { 1, 0x11, "VAUOCTRL" }, + { 1, 0x12, "VAUDPLL" }, + { 1, 0x17, "VRPCSIMR" }, + { 0, 0, 0 } +}; + +static syren_reg *find_reg(const char *name) +{ + int i; + + for (i = 0; registers[i].name != 0; i++) { + if (!strcasecmp(registers[i].name, name)) + return ®isters[i]; + } + + return NULL; +} + +static int usage(void) +{ + fprintf(stderr, "usage: syren [r/w] [REGNAME | page:addr] (value)\n"); + return 1; +} + +int +syren_main(int argc, char **argv) +{ + int cmd = -1; + syren_reg *r; + struct syren_io_args sio; + char name[32]; + int fd; + + if (argc < 3) { + return usage(); + } + + switch(argv[1][0]) { + case 'r': + cmd = SYREN_RD; + break; + case 'w': + cmd = SYREN_WR; + break; + case 'R': + cmd = SYREN_OLD_RD; + break; + case 'W': + cmd = SYREN_OLD_WR; + break; + default: + return usage(); + } + + if (cmd == SYREN_WR || cmd == SYREN_OLD_WR) { + if (argc < 4) + return usage(); + sio.value = strtoul(argv[3], 0, 0); + } + + fd = open("/dev/eac", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "can't open /dev/eac\n"); + return 1; + } + + if (strcasecmp(argv[2], "all") == 0) { + int i; + if (cmd != SYREN_RD && cmd != SYREN_OLD_RD) { + fprintf(stderr, "can only read all registers\n"); + return 1; + } + + for (i = 0; registers[i].name; i++) { + sio.page = registers[i].page; + sio.addr = registers[i].addr; + if (ioctl(fd, cmd, &sio) < 0) { + fprintf(stderr, "%s: error\n", registers[i].name); + } else { + fprintf(stderr, "%s: %04x\n", registers[i].name, sio.value); + } + } + + close(fd); + return 0; + } + + r = find_reg(argv[2]); + if (r == NULL) { + strcpy(name, argv[2]); + char *addr_str = strchr(argv[2], ':'); + if (addr_str == NULL) + return usage(); + *addr_str++ = 0; + sio.page = strtoul(argv[2], 0, 0); + sio.addr = strtoul(addr_str, 0, 0); + } else { + strcpy(name, r->name); + sio.page = r->page; + sio.addr = r->addr; + } + + if (ioctl(fd, cmd, &sio) < 0) { + fprintf(stderr, "ioctl(%d) failed\n", cmd); + return 1; + } + + if (cmd == SYREN_RD || cmd == SYREN_OLD_RD) { + printf("%s: %04x\n", name, sio.value); + } else { + printf("wrote %04x to %s\n", sio.value, name); + } + + close(fd); + + return 0; +} +
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c new file mode 100644 index 0000000..0eac390 --- /dev/null +++ b/toolbox/toolbox.c
@@ -0,0 +1,57 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int, char **); + +static int toolbox_main(int argc, char **argv) +{ + // "toolbox foo ..." is equivalent to "foo ..." + if (argc > 1) { + return main(argc - 1, argv + 1); + } else { + printf("Toolbox!\n"); + return 0; + } +} + +#define TOOL(name) int name##_main(int, char**); +#include "tools.h" +#undef TOOL + +static struct +{ + const char *name; + int (*func)(int, char**); +} tools[] = { + { "toolbox", toolbox_main }, +#define TOOL(name) { #name, name##_main }, +#include "tools.h" +#undef TOOL + { 0, 0 }, +}; + +int main(int argc, char **argv) +{ + int i; + char *name = argv[0]; + + if((argc > 1) && (argv[1][0] == '@')) { + name = argv[1] + 1; + argc--; + argv++; + } else { + char *cmd = strrchr(argv[0], '/'); + if (cmd) + name = cmd + 1; + } + + for(i = 0; tools[i].name; i++){ + if(!strcmp(tools[i].name, name)){ + return tools[i].func(argc, argv); + } + } + + printf("%s: no such tool\n", argv[0]); + return -1; +}
diff --git a/toolbox/top.c b/toolbox/top.c new file mode 100644 index 0000000..b92948a --- /dev/null +++ b/toolbox/top.c
@@ -0,0 +1,604 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <ctype.h> +#include <dirent.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cutils/sched_policy.h> + +struct cpu_info { + long unsigned utime, ntime, stime, itime; + long unsigned iowtime, irqtime, sirqtime; +}; + +#define PROC_NAME_LEN 64 +#define THREAD_NAME_LEN 32 + +struct proc_info { + struct proc_info *next; + pid_t pid; + pid_t tid; + uid_t uid; + gid_t gid; + char name[PROC_NAME_LEN]; + char tname[THREAD_NAME_LEN]; + char state; + long unsigned utime; + long unsigned stime; + long unsigned delta_utime; + long unsigned delta_stime; + long unsigned delta_time; + long vss; + long rss; + int prs; + int num_threads; + char policy[32]; +}; + +struct proc_list { + struct proc_info **array; + int size; +}; + +#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } + +#define INIT_PROCS 50 +#define THREAD_MULT 8 +static struct proc_info **old_procs, **new_procs; +static int num_old_procs, num_new_procs; +static struct proc_info *free_procs; +static int num_used_procs, num_free_procs; + +static int max_procs, delay, iterations, threads; +static char *uid_filter, *gid_filter; + +static struct cpu_info old_cpu, new_cpu; + +static struct proc_info *alloc_proc(void); +static void free_proc(struct proc_info *proc); +static void read_procs(void); +static int read_stat(char *filename, struct proc_info *proc); +static void read_policy(int pid, struct proc_info *proc); +static void add_proc(int proc_num, struct proc_info *proc); +static int read_cmdline(char *filename, struct proc_info *proc); +static int read_status(char *filename, struct proc_info *proc); +static void print_procs(void); +static struct proc_info *find_old_proc(pid_t pid, pid_t tid); +static void free_old_procs(void); +static int (*proc_cmp)(const void *a, const void *b); +static int proc_cpu_cmp(const void *a, const void *b); +static int proc_vss_cmp(const void *a, const void *b); +static int proc_rss_cmp(const void *a, const void *b); +static int proc_thr_cmp(const void *a, const void *b); +static int numcmp(long long a, long long b); +static void usage(char *cmd); + +int top_main(int argc, char *argv[]) { + int i; + + num_used_procs = num_free_procs = 0; + + max_procs = 0; + delay = 3; + iterations = -1; + proc_cmp = &proc_cpu_cmp; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-m")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -m expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + max_procs = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-n")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -n expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + iterations = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-d")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -d expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + delay = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-u")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -u expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + uid_filter = argv[++i]; + continue; + } + if (!strcmp(argv[i], "-g")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -g expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + gid_filter = argv[++i]; + continue; + } + if (!strcmp(argv[i], "-s")) { + if (i + 1 >= argc) { + fprintf(stderr, "Option -s expects an argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + ++i; + if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; } + if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; } + if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; } + if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; } + fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]); + exit(EXIT_FAILURE); + } + if (!strcmp(argv[i], "-t")) { threads = 1; continue; } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (threads && proc_cmp == &proc_thr_cmp) { + fprintf(stderr, "Sorting by threads per thread makes no sense!\n"); + exit(EXIT_FAILURE); + } + + free_procs = NULL; + + num_new_procs = num_old_procs = 0; + new_procs = old_procs = NULL; + + read_procs(); + while ((iterations == -1) || (iterations-- > 0)) { + old_procs = new_procs; + num_old_procs = num_new_procs; + memcpy(&old_cpu, &new_cpu, sizeof(old_cpu)); + sleep(delay); + read_procs(); + print_procs(); + free_old_procs(); + } + + return 0; +} + +static struct proc_info *alloc_proc(void) { + struct proc_info *proc; + + if (free_procs) { + proc = free_procs; + free_procs = free_procs->next; + num_free_procs--; + } else { + proc = malloc(sizeof(*proc)); + if (!proc) die("Could not allocate struct process_info.\n"); + } + + num_used_procs++; + + return proc; +} + +static void free_proc(struct proc_info *proc) { + proc->next = free_procs; + free_procs = proc; + + num_used_procs--; + num_free_procs++; +} + +#define MAX_LINE 256 + +static void read_procs(void) { + DIR *proc_dir, *task_dir; + struct dirent *pid_dir, *tid_dir; + char filename[64]; + FILE *file; + int proc_num; + struct proc_info *proc; + pid_t pid, tid; + + int i; + + proc_dir = opendir("/proc"); + if (!proc_dir) die("Could not open /proc.\n"); + + new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *)); + num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1); + + file = fopen("/proc/stat", "r"); + if (!file) die("Could not open /proc/stat.\n"); + fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime, + &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime); + fclose(file); + + proc_num = 0; + while ((pid_dir = readdir(proc_dir))) { + if (!isdigit(pid_dir->d_name[0])) + continue; + + pid = atoi(pid_dir->d_name); + + struct proc_info cur_proc; + + if (!threads) { + proc = alloc_proc(); + + proc->pid = proc->tid = pid; + + sprintf(filename, "/proc/%d/stat", pid); + read_stat(filename, proc); + + sprintf(filename, "/proc/%d/cmdline", pid); + read_cmdline(filename, proc); + + sprintf(filename, "/proc/%d/status", pid); + read_status(filename, proc); + + read_policy(pid, proc); + + proc->num_threads = 0; + } else { + sprintf(filename, "/proc/%d/cmdline", pid); + read_cmdline(filename, &cur_proc); + + sprintf(filename, "/proc/%d/status", pid); + read_status(filename, &cur_proc); + + proc = NULL; + } + + sprintf(filename, "/proc/%d/task", pid); + task_dir = opendir(filename); + if (!task_dir) continue; + + while ((tid_dir = readdir(task_dir))) { + if (!isdigit(tid_dir->d_name[0])) + continue; + + if (threads) { + tid = atoi(tid_dir->d_name); + + proc = alloc_proc(); + + proc->pid = pid; proc->tid = tid; + + sprintf(filename, "/proc/%d/task/%d/stat", pid, tid); + read_stat(filename, proc); + + read_policy(tid, proc); + + strcpy(proc->name, cur_proc.name); + proc->uid = cur_proc.uid; + proc->gid = cur_proc.gid; + + add_proc(proc_num++, proc); + } else { + proc->num_threads++; + } + } + + closedir(task_dir); + + if (!threads) + add_proc(proc_num++, proc); + } + + for (i = proc_num; i < num_new_procs; i++) + new_procs[i] = NULL; + + closedir(proc_dir); +} + +static int read_stat(char *filename, struct proc_info *proc) { + FILE *file; + char buf[MAX_LINE], *open_paren, *close_paren; + int res, idx; + + file = fopen(filename, "r"); + if (!file) return 1; + fgets(buf, MAX_LINE, file); + fclose(file); + + /* Split at first '(' and last ')' to get process name. */ + open_paren = strchr(buf, '('); + close_paren = strrchr(buf, ')'); + if (!open_paren || !close_paren) return 1; + + *open_paren = *close_paren = '\0'; + strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN); + proc->tname[THREAD_NAME_LEN-1] = 0; + + /* Scan rest of string. */ + sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d", + &proc->state, &proc->utime, &proc->stime, &proc->vss, &proc->rss, &proc->prs); + + return 0; +} + +static void add_proc(int proc_num, struct proc_info *proc) { + int i; + + if (proc_num >= num_new_procs) { + new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *)); + if (!new_procs) die("Could not expand procs array.\n"); + for (i = num_new_procs; i < 2 * num_new_procs; i++) + new_procs[i] = NULL; + num_new_procs = 2 * num_new_procs; + } + new_procs[proc_num] = proc; +} + +static int read_cmdline(char *filename, struct proc_info *proc) { + FILE *file; + char line[MAX_LINE]; + + line[0] = '\0'; + file = fopen(filename, "r"); + if (!file) return 1; + fgets(line, MAX_LINE, file); + fclose(file); + if (strlen(line) > 0) { + strncpy(proc->name, line, PROC_NAME_LEN); + proc->name[PROC_NAME_LEN-1] = 0; + } else + proc->name[0] = 0; + return 0; +} + +static void read_policy(int pid, struct proc_info *proc) { + SchedPolicy p; + if (get_sched_policy(pid, &p) < 0) + strcpy(proc->policy, "unk"); + else { + if (p == SP_BACKGROUND) + strcpy(proc->policy, "bg"); + else if (p == SP_FOREGROUND) + strcpy(proc->policy, "fg"); + else + strcpy(proc->policy, "er"); + } +} + +static int read_status(char *filename, struct proc_info *proc) { + FILE *file; + char line[MAX_LINE]; + unsigned int uid, gid; + + file = fopen(filename, "r"); + if (!file) return 1; + while (fgets(line, MAX_LINE, file)) { + sscanf(line, "Uid: %u", &uid); + sscanf(line, "Gid: %u", &gid); + } + fclose(file); + proc->uid = uid; proc->gid = gid; + return 0; +} + +static void print_procs(void) { + int i; + struct proc_info *old_proc, *proc; + long unsigned total_delta_time; + struct passwd *user; + struct group *group; + char *user_str, user_buf[20]; + char *group_str, group_buf[20]; + + for (i = 0; i < num_new_procs; i++) { + if (new_procs[i]) { + old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid); + if (old_proc) { + new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime; + new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime; + } else { + new_procs[i]->delta_utime = 0; + new_procs[i]->delta_stime = 0; + } + new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime; + } + } + + total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime + + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime) + - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime + + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime); + + qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp); + + printf("\n\n\n"); + printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n", + ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time, + ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time, + ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time, + ((new_cpu.irqtime + new_cpu.sirqtime) + - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time); + printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n", + new_cpu.utime - old_cpu.utime, + new_cpu.ntime - old_cpu.ntime, + new_cpu.stime - old_cpu.stime, + new_cpu.itime - old_cpu.itime, + new_cpu.iowtime - old_cpu.iowtime, + new_cpu.irqtime - old_cpu.irqtime, + new_cpu.sirqtime - old_cpu.sirqtime, + total_delta_time); + printf("\n"); + if (!threads) + printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name"); + else + printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc"); + + for (i = 0; i < num_new_procs; i++) { + proc = new_procs[i]; + + if (!proc || (max_procs && (i >= max_procs))) + break; + user = getpwuid(proc->uid); + group = getgrgid(proc->gid); + snprintf(user_buf, 20, "%d", proc->uid); + if (user && user->pw_name) { + user_str = user->pw_name; + } else { + user_str = user_buf; + } + if (uid_filter && (strcmp(uid_filter,user_str) && + strcmp(uid_filter,user_buf))) { + continue; + } + snprintf(group_buf, 20, "%d", proc->gid); + if (group && group->gr_name) { + group_str = group->gr_name; + } else { + group_str = group_buf; + } + if (gid_filter && (strcmp(gid_filter,group_str) && + strcmp(gid_filter,group_buf))) { + continue; + } + if (!threads) + printf("%5d %2d %3ld%% %c %5d %6ldK %6ldK %3s %-8.8s %s\n", proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads, + proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname); + else + printf("%5d %5d %2d %3ld%% %c %6ldK %6ldK %3s %-8.8s %-15s %s\n", proc->pid, proc->tid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, + proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->tname, proc->name); + } +} + +static struct proc_info *find_old_proc(pid_t pid, pid_t tid) { + int i; + + for (i = 0; i < num_old_procs; i++) + if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid)) + return old_procs[i]; + + return NULL; +} + +static void free_old_procs(void) { + int i; + + for (i = 0; i < num_old_procs; i++) + if (old_procs[i]) + free_proc(old_procs[i]); + + free(old_procs); +} + +static int proc_cpu_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->delta_time, pb->delta_time); +} + +static int proc_vss_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->vss, pb->vss); +} + +static int proc_rss_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->rss, pb->rss); +} + +static int proc_thr_cmp(const void *a, const void *b) { + struct proc_info *pa, *pb; + + pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); + + if (!pa && !pb) return 0; + if (!pa) return 1; + if (!pb) return -1; + + return -numcmp(pa->num_threads, pb->num_threads); +} + +static int numcmp(long long a, long long b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static void usage(char *cmd) { + fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ]" + " [ -s sort_column ] [-u user] [-g group] [ -t ] [ -h ]\n" + " -m num Maximum number of processes to display.\n" + " -n num Updates to show before exiting.\n" + " -d num Seconds to wait between updates.\n" + " -s col Column to sort by (cpu,vss,rss,thr).\n" + " -u user Only show prcessors with the given user ID.\n" + " -g group Only show processes with the given group ID.\n" + " -t Show threads instead of processes.\n" + " -h Display this help screen.\n", + cmd); +}
diff --git a/toolbox/touch.c b/toolbox/touch.c new file mode 100644 index 0000000..b8ab310 --- /dev/null +++ b/toolbox/touch.c
@@ -0,0 +1,87 @@ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> + +static void usage(void) +{ + fprintf(stderr, "touch: usage: touch [-alm] [-t time_t] <file>\n"); + exit(1); +} + +int touch_main(int argc, char *argv[]) +{ + int i, fd, aflag = 0, mflag = 0, debug = 0, flags = 0; + struct timespec specified_time, times[2]; + char *file = 0; + + specified_time.tv_nsec = UTIME_NOW; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* an option */ + const char *arg = argv[i]+1; + while (arg[0]) { + switch (arg[0]) { + case 'a': aflag = 1; break; + case 'm': mflag = 1; break; + case 't': + if ((i+1) >= argc) + usage(); + specified_time.tv_sec = atol(argv[++i]); + if (specified_time.tv_sec == 0) { + fprintf(stderr, "touch: invalid time_t\n"); + exit(1); + } + specified_time.tv_nsec = 0; + break; + case 'l': flags |= AT_SYMLINK_NOFOLLOW; break; + case 'd': debug = 1; break; + default: + usage(); + } + arg++; + } + } else { + /* not an option, and only accept one filename */ + if (i+1 != argc) + usage(); + file = argv[i]; + } + } + + if (! file) { + fprintf(stderr, "touch: no file specified\n"); + exit(1); + } + + if (access(file, F_OK)) + if ((fd=creat(file, 0666)) != -1) + close(fd); + + if ((mflag == 0) && (aflag == 0)) + aflag = mflag = 1; + + if (aflag) + times[0] = specified_time; + else + times[0].tv_nsec = UTIME_OMIT; + + if (mflag) + times[1] = specified_time; + else + times[1].tv_nsec = UTIME_OMIT; + + if (debug) { + fprintf(stderr, "file = %s\n", file); + fprintf(stderr, "times[0].tv_sec = %ld, times[0].tv_nsec = %ld\n", times[0].tv_sec, times[0].tv_nsec); + fprintf(stderr, "times[1].tv_sec = %ld, times[1].tv_nsec = %ld\n", times[1].tv_sec, times[1].tv_nsec); + fprintf(stderr, "flags = 0x%8.8x\n", flags); + } + + return utimensat(AT_FDCWD, file, times, flags); +} +
diff --git a/toolbox/umount.c b/toolbox/umount.c new file mode 100644 index 0000000..15b7003 --- /dev/null +++ b/toolbox/umount.c
@@ -0,0 +1,94 @@ + +#include <sys/mount.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <linux/loop.h> +#include <errno.h> + +// if glibc version >= 2.25 +#include <sys/sysmacros.h> + +#define LOOPDEV_MAXLEN 64 +#define LOOP_MAJOR 7 + +static int is_loop(char *dev) +{ + struct stat st; + int ret = 0; + + if (stat(dev, &st) == 0) { + if (S_ISBLK(st.st_mode) && (major(st.st_rdev) == LOOP_MAJOR)) { + ret = 1; + } + } + + return ret; +} + +static int is_loop_mount(const char* path, char *loopdev) +{ + FILE* f; + int count; + char device[256]; + char mount_path[256]; + char rest[256]; + int result = 0; + int path_length = strlen(path); + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stdout, "could not open /proc/mounts: %s\n", strerror(errno)); + return -1; + } + + do { + count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest); + if (count == 3) { + if (is_loop(device) && strcmp(path, mount_path) == 0) { + strlcpy(loopdev, device, LOOPDEV_MAXLEN); + result = 1; + break; + } + } + } while (count == 3); + + fclose(f); + return result; +} + +int umount_main(int argc, char *argv[]) +{ + int loop, loop_fd; + char loopdev[LOOPDEV_MAXLEN]; + + if(argc != 2) { + fprintf(stderr,"umount <path>\n"); + return 1; + } + + loop = is_loop_mount(argv[1], loopdev); + if (umount(argv[1])) { + fprintf(stderr, "failed: %s\n", strerror(errno)); + return 1; + } + + if (loop) { + // free the loop device + loop_fd = open(loopdev, O_RDONLY); + if (loop_fd < 0) { + fprintf(stderr, "open loop device failed: %s\n", strerror(errno)); + return 1; + } + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + fprintf(stderr, "ioctl LOOP_CLR_FD failed: %s\n", strerror(errno)); + return 1; + } + + close(loop_fd); + } + + return 0; +}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c new file mode 100644 index 0000000..ebf3672 --- /dev/null +++ b/toolbox/uptime.c
@@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/time.h> +#include <linux/ioctl.h> +#include <linux/rtc.h> +#include <linux/android_alarm.h> +#include <fcntl.h> +#include <stdio.h> +#include <time.h> + + +static void format_time(int time, char* buffer) { + int seconds, minutes, hours, days; + + seconds = time % 60; + time /= 60; + minutes = time % 60; + time /= 60; + hours = time % 24; + days = time / 24; + + if (days > 0) + sprintf(buffer, "%d days, %02d:%02d:%02d", days, hours, minutes, seconds); + else + sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds); +} + +int uptime_main(int argc, char *argv[]) +{ + float up_time, idle_time; + char up_string[100], idle_string[100], sleep_string[100]; + int elapsed; + + FILE* file = fopen("/proc/uptime", "r"); + if (!file) { + fprintf(stderr, "Could not open /proc/uptime\n"); + return -1; + } + if (fscanf(file, "%f %f", &up_time, &idle_time) != 2) { + fprintf(stderr, "Could not parse /proc/uptime\n"); + fclose(file); + return -1; + } + fclose(file); + + format_time((int)up_time, up_string); + format_time((int)idle_time, idle_string); + printf("up time: %s, idle time: %s\n", up_string, idle_string); + + return 0; +}
diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c new file mode 100644 index 0000000..600f136 --- /dev/null +++ b/toolbox/vmstat.c
@@ -0,0 +1,247 @@ +/* + * Copyright (c) 2008, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> +#include <unistd.h> + +struct state { + long procs_r; + long procs_b; + + long mem_free; + long mem_mapped; + long mem_anon; + long mem_slab; + + long sys_in; + long sys_cs; + long sys_flt; + + long cpu_us; + long cpu_ni; + long cpu_sy; + long cpu_id; + long cpu_wa; + long cpu_ir; + long cpu_si; +}; + +#define MAX_LINE 256 + +char line[MAX_LINE]; + +static void read_state(struct state *s); +static int read_meminfo(struct state *s); +static int read_stat(struct state *s); +static int read_vmstat(struct state *s); +static void print_header(void); +static void print_line(struct state *old, struct state *new); +static void usage(char *cmd); + +int vmstat_main(int argc, char *argv[]) { + struct state s[2]; + int iterations, delay, header_interval; + int toggle, count; + int i; + + iterations = 0; + delay = 1; + header_interval = 20; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-n")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -n requires an argument.\n"); + exit(EXIT_FAILURE); + } + iterations = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-d")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -d requires an argument.\n"); + exit(EXIT_FAILURE); + } + delay = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-r")) { + if (i >= argc - 1) { + fprintf(stderr, "Option -r requires an argument.\n"); + exit(EXIT_FAILURE); + } + header_interval = atoi(argv[++i]); + continue; + } + if (!strcmp(argv[i], "-h")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + toggle = 0; + count = 0; + + if (!header_interval) + print_header(); + read_state(&s[1 - toggle]); + while ((iterations == 0) || (iterations-- > 0)) { + sleep(delay); + read_state(&s[toggle]); + if (header_interval) { + if (count == 0) + print_header(); + count = (count + 1) % header_interval; + } + print_line(&s[1 - toggle], &s[toggle]); + toggle = 1 - toggle; + } + + return 0; +} + +static void read_state(struct state *s) { + int error; + + error = read_meminfo(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/meminfo: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } + + error = read_stat(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/stat: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } + + error = read_vmstat(s); + if (error) { + fprintf(stderr, "vmstat: could not read /proc/vmstat: %s\n", strerror(error)); + exit(EXIT_FAILURE); + } +} + +static int read_meminfo(struct state *s) { + FILE *f; + + f = fopen("/proc/meminfo", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + sscanf(line, "MemFree: %ld kB", &s->mem_free); + sscanf(line, "AnonPages: %ld kB", &s->mem_anon); + sscanf(line, "Mapped: %ld kB", &s->mem_mapped); + sscanf(line, "Slab: %ld kB", &s->mem_slab); + } + + fclose(f); + + return 0; +} + +static int read_stat(struct state *s) { + FILE *f; + + f = fopen("/proc/stat", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + if (!strncmp(line, "cpu ", 4)) { + sscanf(line, "cpu %ld %ld %ld %ld %ld %ld %ld", + &s->cpu_us, &s->cpu_ni, &s->cpu_sy, &s->cpu_id, &s->cpu_wa, + &s->cpu_ir, &s->cpu_si); + } + sscanf(line, "intr %ld", &s->sys_in); + sscanf(line, "ctxt %ld", &s->sys_cs); + sscanf(line, "procs_running %ld", &s->procs_r); + sscanf(line, "procs_blocked %ld", &s->procs_b); + } + + fclose(f); + + return 0; +} + +static int read_vmstat(struct state *s) { + FILE *f; + + f = fopen("/proc/vmstat", "r"); + if (!f) return errno; + + while (fgets(line, MAX_LINE, f)) { + sscanf(line, "pgmajfault %ld", &s->sys_flt); + } + + fclose(f); + + return 0; +} + +static void print_header(void) { + printf("%-5s %-27s %-14s %-17s\n", "procs", "memory", "system", "cpu"); + printf("%2s %2s %6s %6s %6s %6s %4s %4s %4s %2s %2s %2s %2s %2s %2s\n", "r", "b", "free", "mapped", "anon", "slab", "in", "cs", "flt", "us", "ni", "sy", "id", "wa", "ir"); +} + +/* Jiffies to percent conversion */ +#define JP(jif) ((jif) * 100 / (HZ)) +#define NORM(var) ((var) = (((var) > 99) ? (99) : (var))) + +static void print_line(struct state *old, struct state *new) { + int us, ni, sy, id, wa, ir; + us = JP(new->cpu_us - old->cpu_us); NORM(us); + ni = JP(new->cpu_ni - old->cpu_ni); NORM(ni); + sy = JP(new->cpu_sy - old->cpu_sy); NORM(sy); + id = JP(new->cpu_id - old->cpu_id); NORM(id); + wa = JP(new->cpu_wa - old->cpu_wa); NORM(wa); + ir = JP(new->cpu_ir - old->cpu_ir); NORM(ir); + printf("%2ld %2ld %6ld %6ld %6ld %6ld %4ld %4ld %4ld %2d %2d %2d %2d %2d %2d\n", + new->procs_r ? (new->procs_r - 1) : 0, new->procs_b, + new->mem_free, new->mem_mapped, new->mem_anon, new->mem_slab, + new->sys_in - old->sys_in, new->sys_cs - old->sys_cs, new->sys_flt - old->sys_flt, + us, ni, sy, id, wa, ir); +} + +static void usage(char *cmd) { + fprintf(stderr, "Usage: %s [ -h ] [ -n iterations ] [ -d delay ] [ -r header_repeat ]\n" + " -n iterations How many rows of data to print.\n" + " -d delay How long to sleep between rows.\n" + " -r header_repeat How many rows to print before repeating\n" + " the header. Zero means never repeat.\n" + " -h Displays this help screen.\n", + cmd); +}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c new file mode 100644 index 0000000..d311992 --- /dev/null +++ b/toolbox/watchprops.c
@@ -0,0 +1,76 @@ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include <cutils/properties.h> + +#include <sys/atomics.h> + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + + +extern prop_area *__system_property_area__; + +typedef struct pwatch pwatch; + +struct pwatch +{ + const prop_info *pi; + unsigned serial; +}; + +static pwatch watchlist[1024]; + +static void announce(const prop_info *pi) +{ + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + char *x; + + __system_property_read(pi, name, value); + + for(x = value; *x; x++) { + if((*x < 32) || (*x > 127)) *x = '.'; + } + + fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value); +} + +int watchprops_main(int argc, char *argv[]) +{ + prop_area *pa = __system_property_area__; + unsigned serial = pa->serial; + unsigned count = pa->count; + unsigned n; + + if(count >= 1024) exit(1); + + for(n = 0; n < count; n++) { + watchlist[n].pi = __system_property_find_nth(n); + watchlist[n].serial = watchlist[n].pi->serial; + } + + for(;;) { + do { + __futex_wait(&pa->serial, serial, 0); + } while(pa->serial == serial); + + while(count < pa->count){ + watchlist[count].pi = __system_property_find_nth(count); + watchlist[count].serial = watchlist[n].pi->serial; + announce(watchlist[count].pi); + count++; + if(count == 1024) exit(1); + } + + for(n = 0; n < count; n++){ + unsigned tmp = watchlist[n].pi->serial; + if(watchlist[n].serial != tmp) { + announce(watchlist[n].pi); + watchlist[n].serial = tmp; + } + } + } + return 0; +}
diff --git a/toolbox/wipe.c b/toolbox/wipe.c new file mode 100644 index 0000000..650a0d6 --- /dev/null +++ b/toolbox/wipe.c
@@ -0,0 +1,176 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <cutils/android_reboot.h> +#include <sys/stat.h> + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + +/* Directories created by init defined in system/rootdir/init.rc */ +static char *INIT_DIRS[] = { + "/system/etc/ppp", + "/data/misc", + "/data/local", + "/data/local/tmp", + "/data/data", + "/data/app_private", + "/data/app", + NULL +}; + +static void wipe (const char *path); + +static int usage() +{ + fprintf(stderr, "wipe <system|data|all>\n\n" + "system means '/system'\n" + "data means '/data'\n"); + + return -1; +} + +int wipe_main (int argc, char *argv[]) +{ + char *whatToWipe; + + if (argc != 2) return usage(); + + whatToWipe = argv[1]; + + if (0 == strcmp (whatToWipe, "system")) { + fprintf(stdout, "Wiping /system\n"); + wipe ("/system"); + fprintf(stdout, "Done wiping /android\n"); + } else if (0 == strcmp (whatToWipe, "data")) { + fprintf(stdout, "Wiping /data\n"); + wipe ("/data"); + fprintf(stdout, "Done wiping /data\n"); + } else if (0 == strcmp (whatToWipe, "all")) { + fprintf(stdout, "Wiping /system and /data\n"); + wipe ("/system"); + wipe ("/data"); + fprintf(stdout, "Done wiping /system and /data\n"); + } else if (0 == strcmp(whatToWipe, "nuke")) { + int ret; + fprintf(stdout, "Nuking the device...\n"); + wipe ("/system"); + wipe ("/data"); + fprintf(stdout, "Device nuked! Rebooting...\n"); + ret = android_reboot(ANDROID_RB_RESTART, 0, 0); + if (ret < 0) { + fprintf(stderr, "Reboot failed, %s\n", strerror(errno)); + return 1; + } + } else { + return usage(); + } + + return 0; +} + +static char nameBuffer[PATH_MAX]; +static struct stat statBuffer; + +static void wipe (const char *path) +{ + DIR *dir; + struct dirent *de; + int ret; + + dir = opendir(path); + + if (dir == NULL) { + fprintf (stderr, "Error opendir'ing %s '%s'\n", + path, strerror(errno)); + return; + } + + char *filenameOffset; + + strcpy(nameBuffer, path); + strcat(nameBuffer, "/"); + + filenameOffset = nameBuffer + strlen(nameBuffer); + + for (;;) { + de = readdir(dir); + + if (de == NULL) { + break; + } + + if (0 == strcmp(de->d_name, ".") + || 0 == strcmp(de->d_name, "..") + || 0 == strcmp(de->d_name, "lost+found") + ) { + continue; + } + + strcpy(filenameOffset, de->d_name); + + ret = lstat (nameBuffer, &statBuffer); + + if (ret != 0) { + fprintf(stderr, "stat() error on '%s' '%s'\n", + nameBuffer, strerror(errno)); + } + + if(S_ISDIR(statBuffer.st_mode)) { + int i; + char *newpath; + +#if 0 + closedir(dir); +#endif + + newpath = strdup(nameBuffer); + wipe(newpath); + + /* Leave directories created by init, they have special permissions. */ + for (i = 0; INIT_DIRS[i]; i++) { + if (strcmp(INIT_DIRS[i], newpath) == 0) { + break; + } + } + if (INIT_DIRS[i] == NULL) { + ret = rmdir(newpath); + if (ret != 0) { + fprintf(stderr, "rmdir() error on '%s' '%s'\n", + newpath, strerror(errno)); + } + } + + free(newpath); + +#if 0 + dir = opendir(path); + if (dir == NULL) { + fprintf (stderr, "Error opendir'ing %s '%s'\n", + path, strerror(errno)); + return; + } +#endif + + strcpy(nameBuffer, path); + strcat(nameBuffer, "/"); + + } else { + ret = unlink(nameBuffer); + + if (ret != 0) { + fprintf(stderr, "unlink() error on '%s' '%s'\n", + nameBuffer, strerror(errno)); + } + } + } + + closedir(dir); + +}