From 3fb9a9398af222d6e1e8f806ecaba4c8ce5ce631 Mon Sep 17 00:00:00 2001 From: cfig Date: Thu, 14 Apr 2016 21:14:12 +0800 Subject: [PATCH] initial commit --- boot.mk | 93 ++ build.gradle | 71 ++ src/abootimg/c/abootimg.c | 952 ++++++++++++++++++ src/abootimg/headers/bootimg.h | 97 ++ src/abootimg/headers/version.h | 2 + src/main/resources/mkbootfs | Bin 0 -> 31456 bytes src/main/resources/mkbootimg | Bin 0 -> 30808 bytes src/mkbootfs/c/fs_config.c | 288 ++++++ src/mkbootfs/c/mkbootfs.c | 363 +++++++ .../private/android_filesystem_capability.h | 119 +++ .../private/android_filesystem_config.h | 223 ++++ 11 files changed, 2208 insertions(+) create mode 100644 boot.mk create mode 100644 build.gradle create mode 100644 src/abootimg/c/abootimg.c create mode 100644 src/abootimg/headers/bootimg.h create mode 100644 src/abootimg/headers/version.h create mode 100755 src/main/resources/mkbootfs create mode 100755 src/main/resources/mkbootimg create mode 100644 src/mkbootfs/c/fs_config.c create mode 100644 src/mkbootfs/c/mkbootfs.c create mode 100644 src/mkbootfs/headers/private/android_filesystem_capability.h create mode 100644 src/mkbootfs/headers/private/android_filesystem_config.h diff --git a/boot.mk b/boot.mk new file mode 100644 index 0000000..4137f8a --- /dev/null +++ b/boot.mk @@ -0,0 +1,93 @@ +.DEFAULT_GOAL := flat + +SHELL := /bin/bash +WORK_DIR := unzip_boot + +help: + @echo "flat : boot.subimg -> unzip_boot/*" + @echo "boot.img : unsigned boot image" + @echo "boot.subimg : signed boot image" + @echo "addon : (recovery only) add additional tools" + +.PHONY: flat +flat: + rm -fr $(WORK_DIR) + mkdir -p $(WORK_DIR)/root + abootimg -x boot.subimg $(WORK_DIR)/bootimg.cfg $(this_kernel) $(this_ramdisk).gz + gzip -c -d $(this_ramdisk).gz > $(this_ramdisk) + rm $(this_ramdisk).gz + cd $(WORK_DIR)/root && \ + cpio -i -F ../ramdisk.img + @rm $(WORK_DIR)/ramdisk.img + @echo && echo "===================================" && file $(WORK_DIR)/* && echo "===================================" +kernel_cmdline := "$(shell grep -Po '(?<=cmdline = ).*' $(WORK_DIR)/bootimg.cfg)" +this_root := $(WORK_DIR)/root +this_kernel := $(WORK_DIR)/kernel +this_ramdisk := $(WORK_DIR)/ramdisk.img +ifeq '$(TARGET_PRODUCT)' '' +$(warning NON-android) +this_verity_key := tools/security/verity +else +$(warning android) +this_verity_key := build/target/product/security/verity +endif + +.INTERMEDIATE: $(this_ramdisk).gz boot.img +$(this_ramdisk).gz: $(this_root) + mkbootfs $< | gzip > $@ +boot.img: $(this_ramdisk).gz $(this_kernel) + mkbootimg \ + --kernel $(this_kernel) \ + --ramdisk $(this_ramdisk).gz \ + --cmdline "$(shell echo $(kernel_cmdline))" \ + --base 0x01000000 \ + --output $@ +boot.subimg: boot.img + $(call signer,/boot,$<,$@) + +define signer + boot_signer $(1) $(2) $(this_verity_key).pk8 $(this_verity_key).x509.pem $(3) +endef + +mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) +real_mkfile_path := $(shell readlink $(mkfile_path)) + +libs := libc.so libcrypto.so libcutils.so libm.so libselinux.so libstdc++.so libpcre.so liblog.so libnetutils.so libsysutils.so libutils.so libbacktrace.so libstlport.so libgccdemangle.so libunwind.so libunwind-ptrace.so +bins := toolbox sh linker netcfg logd logcat +addon: | unzip_boot/root/system/bin +addon: | unzip_boot/root/system/lib +addon: INITRC := unzip_boot/root/init.recovery.marvellberlin.rc +addon: + #initrc + echo "service console /system/bin/sh" > $(INITRC) + echo " console" >> $(INITRC) + echo " user root" >> $(INITRC) + echo " group root" >> $(INITRC) + echo >> $(INITRC) + echo "service logd /system/bin/logd" >> $(INITRC) + echo " socket logd stream 0666 logd logd" >> $(INITRC) + echo " socket logdr seqpacket 0666 logd logd" >> $(INITRC) + echo " socket logdw dgram 0222 logd logd" >> $(INITRC) + echo " seclabel u:r:logd:s0" >> $(INITRC) + #recovery + #cp out/target/product/$(TARGET_PRODUCT)/system/bin/recovery unzip_boot/root/sbin/ + #@cp -v out/target/product/$(TARGET_PRODUCT)/obj/EXECUTABLES/recovery_intermediates/recovery unzip_boot/root/sbin/ + #bin + @$(foreach item,$(bins), \ + cp -v out/target/product/$(TARGET_PRODUCT)/system/bin/$(item) unzip_boot/root/system/bin/; $(newline)) + #lib + @$(foreach item,$(libs), \ + cp -v out/target/product/$(TARGET_PRODUCT)/system/lib/$(item) unzip_boot/root/system/lib/; $(newline)) + #@cp -v out/target/product/$(TARGET_PRODUCT)/system/etc/sepolicy.recovery unzip_boot/root/sepolicy + @cp -v out/target/product/$(TARGET_PRODUCT)/obj/ETC/sepolicy.recovery_intermediates/sepolicy.recovery unzip_boot/root/sepolicy + + +unzip_boot/root/system/bin: + mkdir $@ +unzip_boot/root/system/lib: + mkdir $@ + +#service console /system/bin/sh +# console +# user root +# group root diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..1c01617 --- /dev/null +++ b/build.gradle @@ -0,0 +1,71 @@ +apply plugin: 'c' +apply plugin: 'java' + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +model { + buildTypes { + release + } + + components { + abootimg(NativeExecutableSpec) { + binaries.all { + cppCompiler.define 'HAS_BLKID' + linker.args '-lblkid' + } + } + } + + components { + mkbootfs(NativeExecutableSpec) { + binaries.all { + } + } + } +} + +def workdir='unzip_boot' +task unpack_bootimg(type: Exec, dependsOn: 'abootimgExecutable') { + new File(workdir + '/root').mkdirs() + workingDir '.' + executable 'build/exe/abootimg/abootimg' + args = ['-x', 'boot.img', workdir+'/bootimg.cfg', workdir+'/kernel', workdir+'/ramdisk.img.gz'] +} + +task unpack_ramdisk_gz << { + unGunzipFile(workdir+"/ramdisk.img.gz", workdir + "/ramdisk.img") +} +unpack_ramdisk_gz.dependsOn(unpack_bootimg) + +task unpack_cpio(type: Exec, dependsOn: unpack_ramdisk_gz) { + workingDir workdir + "/root" + executable 'cpio' + args = ['-i', '-F', '../ramdisk.img'] +} + +public void unGunzipFile(String compressedFile, String decompressedFile) throws IOException { + byte[] buffer = new byte[1024]; + try { + FileInputStream fileIn = new FileInputStream(compressedFile); + GZIPInputStream gZIPInputStream = new GZIPInputStream(fileIn); + FileOutputStream fileOutputStream = new FileOutputStream(decompressedFile); + int bytes_read; + while ((bytes_read = gZIPInputStream.read(buffer)) > 0) { + fileOutputStream.write(buffer, 0, bytes_read); + } + gZIPInputStream.close(); + fileOutputStream.close(); + System.out.println("The file was decompressed successfully!"); + } catch (IOException ex) { + throw ex; + } +} + +task unpack(type: Delete, dependsOn: unpack_cpio) { + delete workdir + "/ramdisk.img.gz" +} diff --git a/src/abootimg/c/abootimg.c b/src/abootimg/c/abootimg.c new file mode 100644 index 0000000..a55c5c8 --- /dev/null +++ b/src/abootimg/c/abootimg.c @@ -0,0 +1,952 @@ +/* abootimg - Manipulate (read, modify, create) Android Boot Images + * Copyright (c) 2010-2011 Gilles Grandou + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef __linux__ +#include +#include /* BLKGETSIZE64 */ +#endif + +#ifdef __CYGWIN__ +#include +#include /* BLKGETSIZE64 */ +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include /* DIOCGMEDIASIZE */ +#include +#endif + +#if defined(__APPLE__) +# include /* DKIOCGETBLOCKCOUNT */ +#endif + + +#ifdef HAS_BLKID +#include +#endif + +#include "version.h" +#include "bootimg.h" + + +enum command { + none, + help, + info, + extract, + update, + create +}; + + +typedef struct +{ + unsigned size; + int is_blkdev; + + char* fname; + char* config_fname; + char* kernel_fname; + char* ramdisk_fname; + char* second_fname; + + FILE* stream; + + boot_img_hdr header; + + char* kernel; + char* ramdisk; + char* second; +} t_abootimg; + + +#define MAX_CONF_LEN 4096 +char config_args[MAX_CONF_LEN] = ""; + + + +void abort_perror(char* str) +{ + perror(str); + exit(errno); +} + +void abort_printf(char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + + +int blkgetsize(int fd, unsigned long long *pbsize) +{ +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + return ioctl(fd, DIOCGMEDIASIZE, pbsize); +# elif defined(__APPLE__) + return ioctl(fd, DKIOCGETBLOCKCOUNT, pbsize); +# elif defined(__NetBSD__) + // does a suitable ioctl exist? + // return (ioctl(fd, DIOCGDINFO, &label) == -1); + return 1; +# elif defined(__linux__) || defined(__CYGWIN__) + return ioctl(fd, BLKGETSIZE64, pbsize); +# elif defined(__GNU__) + // does a suitable ioctl for HURD exist? + return 1; +# else + return 1; +# endif + +} + +void print_usage(void) +{ + printf ( + " abootimg - manipulate Android Boot Images.\n" + " (c) 2010-2011 Gilles Grandou \n" + " " VERSION_STR "\n" + "\n" + " abootimg [-h]\n" + "\n" + " print usage\n" + "\n" + " abootimg -i \n" + "\n" + " print boot image information\n" + "\n" + " abootimg -x [ [ [ []]]]\n" + "\n" + " extract objects from boot image:\n" + " - config file (default name bootimg.cfg)\n" + " - kernel image (default name zImage)\n" + " - ramdisk image (default name initrd.img)\n" + " - second stage image (default name stage2.img)\n" + "\n" + " abootimg -u [-c \"param=value\"] [-f ] [-k ] [-r ] [-s ]\n" + "\n" + " update a current boot image with objects given in command line\n" + " - header informations given in arguments (several can be provided)\n" + " - header informations given in config file\n" + " - kernel image\n" + " - ramdisk image\n" + " - second stage image\n" + "\n" + " bootimg has to be valid Android Boot Image, or the update will abort.\n" + "\n" + " abootimg --create [-c \"param=value\"] [-f ] -k -r [-s ]\n" + "\n" + " create a new image from scratch.\n" + " if the boot image file is a block device, sanity check will be performed to avoid overwriting a existing\n" + " filesystem.\n" + "\n" + " argurments are the same than for -u.\n" + " kernel and ramdisk are mandatory.\n" + "\n" + ); +} + + +enum command parse_args(int argc, char** argv, t_abootimg* img) +{ + enum command cmd = none; + int i; + + if (argc<2) + return none; + + if (!strcmp(argv[1], "-h")) { + return help; + } + else if (!strcmp(argv[1], "-i")) { + cmd=info; + } + else if (!strcmp(argv[1], "-x")) { + cmd=extract; + } + else if (!strcmp(argv[1], "-u")) { + cmd=update; + } + else if (!strcmp(argv[1], "--create")) { + cmd=create; + } + else + return none; + + switch(cmd) { + case none: + case help: + break; + + case info: + if (argc != 3) + return none; + img->fname = argv[2]; + break; + + case extract: + if ((argc < 3) || (argc > 7)) + return none; + img->fname = argv[2]; + if (argc >= 4) + img->config_fname = argv[3]; + if (argc >= 5) + img->kernel_fname = argv[4]; + if (argc >= 6) + img->ramdisk_fname = argv[5]; + if (argc >= 7) + img->second_fname = argv[6]; + break; + + case update: + case create: + if (argc < 3) + return none; + img->fname = argv[2]; + img->config_fname = NULL; + img->kernel_fname = NULL; + img->ramdisk_fname = NULL; + img->second_fname = NULL; + for(i=3; i= argc) + return none; + unsigned len = strlen(argv[i]); + if (strlen(config_args)+len+1 >= MAX_CONF_LEN) + abort_printf("too many config parameters.\n"); + strcat(config_args, argv[i]); + strcat(config_args, "\n"); + } + else if (!strcmp(argv[i], "-f")) { + if (++i >= argc) + return none; + img->config_fname = argv[i]; + } + else if (!strcmp(argv[i], "-k")) { + if (++i >= argc) + return none; + img->kernel_fname = argv[i]; + } + else if (!strcmp(argv[i], "-r")) { + if (++i >= argc) + return none; + img->ramdisk_fname = argv[i]; + } + else if (!strcmp(argv[i], "-s")) { + if (++i >= argc) + return none; + img->second_fname = argv[i]; + } + else + return none; + } + break; + } + + return cmd; +} + + + +int check_boot_img_header(t_abootimg* img) +{ + if (strncmp((char*)(img->header.magic), BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + fprintf(stderr, "%s: no Android Magic Value\n", img->fname); + return 1; + } + + if (!(img->header.kernel_size)) { + fprintf(stderr, "%s: kernel size is null\n", img->fname); + return 1; + } + + if (!(img->header.ramdisk_size)) { + fprintf(stderr, "%s: ramdisk size is null\n", img->fname); + return 1; + } + + unsigned page_size = img->header.page_size; + if (!page_size) { + fprintf(stderr, "%s: Image page size is null\n", img->fname); + return 1; + } + + unsigned n = (img->header.kernel_size + page_size - 1) / page_size; + unsigned m = (img->header.ramdisk_size + page_size - 1) / page_size; + unsigned o = (img->header.second_size + page_size - 1) / page_size; + + unsigned total_size = (1+n+m+o)*page_size; + + if (total_size > img->size) { + fprintf(stderr, "%s: sizes mismatches in boot image\n", img->fname); + return 1; + } + + return 0; +} + + + +void check_if_block_device(t_abootimg* img) +{ + struct stat st; + + if (stat(img->fname, &st)) + if (errno != ENOENT) { + printf("errno=%d\n", errno); + abort_perror(img->fname); + } + +#ifdef HAS_BLKID + if (S_ISBLK(st.st_mode)) { + img->is_blkdev = 1; + + char* type = blkid_get_tag_value(NULL, "TYPE", img->fname); + if (type) + abort_printf("%s: refuse to write on a valid partition type (%s)\n", img->fname, type); + + int fd = open(img->fname, O_RDONLY); + if (fd == -1) + abort_perror(img->fname); + + unsigned long long bsize = 0; + if (blkgetsize(fd, &bsize)) + abort_perror(img->fname); + img->size = bsize; + + close(fd); + } +#endif +} + + + +void open_bootimg(t_abootimg* img, char* mode) +{ + img->stream = fopen(img->fname, mode); + if (!img->stream) + abort_perror(img->fname); +} + + + +void read_header(t_abootimg* img) +{ + size_t rb = fread(&img->header, sizeof(boot_img_hdr), 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + else if (feof(img->stream)) + abort_printf("%s: cannot read image header\n", img->fname); + + struct stat s; + int fd = fileno(img->stream); + if (fstat(fd, &s)) + abort_perror(img->fname); + + if (S_ISBLK(s.st_mode)) { + unsigned long long bsize = 0; + + if (blkgetsize(fd, &bsize)) + abort_perror(img->fname); + img->size = bsize; + img->is_blkdev = 1; + } + else { + img->size = s.st_size; + img->is_blkdev = 0; + } + + if (check_boot_img_header(img)) + abort_printf("%s: not a valid Android Boot Image.\n", img->fname); +} + + + +void update_header_entry(t_abootimg* img, char* cmd) +{ + char *p; + char *token; + char *endtoken; + char *value; + + p = strchr(cmd, '\n'); + if (p) + *p = '\0'; + + p = cmd; + p += strspn(p, " \t"); + token = p; + + p += strcspn(p, " =\t"); + endtoken = p; + p += strspn(p, " \t"); + + if (*p++ != '=') + goto err; + + p += strspn(p, " \t"); + value = p; + + *endtoken = '\0'; + + unsigned valuenum = strtoul(value, NULL, 0); + + if (!strcmp(token, "cmdline")) { + unsigned len = strlen(value); + if (len >= BOOT_ARGS_SIZE) + abort_printf("cmdline length (%d) is too long (max %d)", len, BOOT_ARGS_SIZE-1); + memset(img->header.cmdline, 0, BOOT_ARGS_SIZE); + strcpy((char*)(img->header.cmdline), value); + } + else if (!strncmp(token, "name", 4)) { + strncpy((char*)(img->header.name), value, BOOT_NAME_SIZE); + img->header.name[BOOT_NAME_SIZE-1] = '\0'; + } + else if (!strncmp(token, "bootsize", 8)) { + if (img->is_blkdev && (img->size != valuenum)) + abort_printf("%s: cannot change Boot Image size for a block device\n", img->fname); + img->size = valuenum; + } + else if (!strncmp(token, "pagesize", 8)) { + img->header.page_size = valuenum; + } + else if (!strncmp(token, "kerneladdr", 10)) { + img->header.kernel_addr = valuenum; + } + else if (!strncmp(token, "ramdiskaddr", 11)) { + img->header.ramdisk_addr = valuenum; + } + else if (!strncmp(token, "secondaddr", 10)) { + img->header.second_addr = valuenum; + } + else if (!strncmp(token, "tagsaddr", 8)) { + img->header.tags_addr = valuenum; + } + else + goto err; + return; + +err: + abort_printf("%s: bad config entry\n", token); +} + + +void update_header(t_abootimg* img) +{ + if (img->config_fname) { + FILE* config_file = fopen(img->config_fname, "r"); + if (!config_file) + abort_perror(img->config_fname); + + printf("reading config file %s\n", img->config_fname); + + char* line = NULL; + size_t len = 0; + int read; + + while ((read = getline(&line, &len, config_file)) != -1) { + update_header_entry(img, line); + free(line); + line = NULL; + } + if (ferror(config_file)) + abort_perror(img->config_fname); + } + + unsigned len = strlen(config_args); + if (len) { + FILE* config_file = fmemopen(config_args, len, "r"); + if (!config_file) + abort_perror("-c args"); + + printf("reading config args\n"); + + char* line = NULL; + size_t len = 0; + int read; + + while ((read = getline(&line, &len, config_file)) != -1) { + update_header_entry(img, line); + free(line); + line = NULL; + } + if (ferror(config_file)) + abort_perror("-c args"); + } +} + + + +void update_images(t_abootimg *img) +{ + unsigned page_size = img->header.page_size; + unsigned ksize = img->header.kernel_size; + unsigned rsize = img->header.ramdisk_size; + unsigned ssize = img->header.second_size; + + if (!page_size) + abort_printf("%s: Image page size is null\n", img->fname); + + unsigned n = (ksize + page_size - 1) / page_size; + unsigned m = (rsize + page_size - 1) / page_size; + unsigned o = (ssize + page_size - 1) / page_size; + + unsigned roffset = (1+n)*page_size; + unsigned soffset = (1+n+m)*page_size; + + if (img->kernel_fname) { + printf("reading kernel from %s\n", img->kernel_fname); + FILE* stream = fopen(img->kernel_fname, "r"); + if (!stream) + abort_perror(img->kernel_fname); + struct stat st; + if (fstat(fileno(stream), &st)) + abort_perror(img->kernel_fname); + ksize = st.st_size; + char* k = malloc(ksize); + if (!k) + abort_perror(""); + size_t rb = fread(k, ksize, 1, stream); + if ((rb!=1) || ferror(stream)) + abort_perror(img->kernel_fname); + else if (feof(stream)) + abort_printf("%s: cannot read kernel\n", img->kernel_fname); + fclose(stream); + img->header.kernel_size = ksize; + img->kernel = k; + } + + if (img->ramdisk_fname) { + printf("reading ramdisk from %s\n", img->ramdisk_fname); + FILE* stream = fopen(img->ramdisk_fname, "r"); + if (!stream) + abort_perror(img->ramdisk_fname); + struct stat st; + if (fstat(fileno(stream), &st)) + abort_perror(img->ramdisk_fname); + rsize = st.st_size; + char* r = malloc(rsize); + if (!r) + abort_perror(""); + size_t rb = fread(r, rsize, 1, stream); + if ((rb!=1) || ferror(stream)) + abort_perror(img->ramdisk_fname); + else if (feof(stream)) + abort_printf("%s: cannot read ramdisk\n", img->ramdisk_fname); + fclose(stream); + img->header.ramdisk_size = rsize; + img->ramdisk = r; + } + else if (img->kernel) { + // if kernel is updated, copy the ramdisk from original image + char* r = malloc(rsize); + if (!r) + abort_perror(""); + if (fseek(img->stream, roffset, SEEK_SET)) + abort_perror(img->fname); + size_t rb = fread(r, rsize, 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + else if (feof(img->stream)) + abort_printf("%s: cannot read ramdisk\n", img->fname); + img->ramdisk = r; + } + + if (img->second_fname) { + printf("reading second stage from %s\n", img->second_fname); + FILE* stream = fopen(img->second_fname, "r"); + if (!stream) + abort_perror(img->second_fname); + struct stat st; + if (fstat(fileno(stream), &st)) + abort_perror(img->second_fname); + ssize = st.st_size; + char* s = malloc(ssize); + if (!s) + abort_perror(""); + size_t rb = fread(s, ssize, 1, stream); + if ((rb!=1) || ferror(stream)) + abort_perror(img->second_fname); + else if (feof(stream)) + abort_printf("%s: cannot read second stage\n", img->second_fname); + fclose(stream); + img->header.second_size = ssize; + img->second = s; + } + else if (img->ramdisk && img->header.second_size) { + // if ramdisk is updated, copy the second stage from original image + char* s = malloc(ssize); + if (!s) + abort_perror(""); + if (fseek(img->stream, soffset, SEEK_SET)) + abort_perror(img->fname); + size_t rb = fread(s, ssize, 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + else if (feof(img->stream)) + abort_printf("%s: cannot read second stage\n", img->fname); + img->second = s; + } + + n = (img->header.kernel_size + page_size - 1) / page_size; + m = (img->header.ramdisk_size + page_size - 1) / page_size; + o = (img->header.second_size + page_size - 1) / page_size; + unsigned total_size = (1+n+m+o)*page_size; + + if (!img->size) + img->size = total_size; + else if (total_size > img->size) + abort_printf("%s: updated is too big for the Boot Image (%u vs %u bytes)\n", img->fname, total_size, img->size); +} + + + +void write_bootimg(t_abootimg* img) +{ + unsigned psize; + char* padding; + + printf ("Writing Boot Image %s\n", img->fname); + + psize = img->header.page_size; + padding = calloc(psize, 1); + if (!padding) + abort_perror(""); + + unsigned n = (img->header.kernel_size + psize - 1) / psize; + unsigned m = (img->header.ramdisk_size + psize - 1) / psize; + //unsigned o = (img->header.second_size + psize - 1) / psize; + + if (fseek(img->stream, 0, SEEK_SET)) + abort_perror(img->fname); + + fwrite(&img->header, sizeof(img->header), 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + + fwrite(padding, psize - sizeof(img->header), 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + + if (img->kernel) { + fwrite(img->kernel, img->header.kernel_size, 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + + fwrite(padding, psize - (img->header.kernel_size % psize), 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + } + + if (img->ramdisk) { + if (fseek(img->stream, (1+n)*psize, SEEK_SET)) + abort_perror(img->fname); + + fwrite(img->ramdisk, img->header.ramdisk_size, 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + + fwrite(padding, psize - (img->header.ramdisk_size % psize), 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + } + + if (img->header.second_size) { + if (fseek(img->stream, (1+n+m)*psize, SEEK_SET)) + abort_perror(img->fname); + + fwrite(img->second, img->header.second_size, 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + + fwrite(padding, psize - (img->header.second_size % psize), 1, img->stream); + if (ferror(img->stream)) + abort_perror(img->fname); + } + + ftruncate (fileno(img->stream), img->size); + + free(padding); +} + + + +void print_bootimg_info(t_abootimg* img) +{ + printf ("\nAndroid Boot Image Info:\n\n"); + + printf ("* file name = %s %s\n\n", img->fname, img->is_blkdev ? "[block device]":""); + + printf ("* image size = %u bytes (%.2f MB)\n", img->size, (double)img->size/0x100000); + printf (" page size = %u bytes\n\n", img->header.page_size); + + printf ("* Boot Name = \"%s\"\n\n", img->header.name); + + unsigned kernel_size = img->header.kernel_size; + unsigned ramdisk_size = img->header.ramdisk_size; + unsigned second_size = img->header.second_size; + + printf ("* kernel size = %u bytes (%.2f MB)\n", kernel_size, (double)kernel_size/0x100000); + printf (" ramdisk size = %u bytes (%.2f MB)\n", ramdisk_size, (double)ramdisk_size/0x100000); + if (second_size) + printf (" second stage size = %u bytes (%.2f MB)\n", ramdisk_size, (double)ramdisk_size/0x100000); + + printf ("\n* load addresses:\n"); + printf (" kernel: 0x%08x\n", img->header.kernel_addr); + printf (" ramdisk: 0x%08x\n", img->header.ramdisk_addr); + if (second_size) + printf (" second stage: 0x%08x\n", img->header.second_addr); + printf (" tags: 0x%08x\n\n", img->header.tags_addr); + + if (img->header.cmdline[0]) + printf ("* cmdline = %s\n\n", img->header.cmdline); + else + printf ("* empty cmdline\n"); + + printf ("* id = "); + int i; + for (i=0; i<8; i++) + printf ("0x%08x ", img->header.id[i]); + printf ("\n\n"); +} + + + +void write_bootimg_config(t_abootimg* img) +{ + printf ("writing boot image config in %s\n", img->config_fname); + + FILE* config_file = fopen(img->config_fname, "w"); + if (!config_file) + abort_perror(img->config_fname); + + fprintf(config_file, "bootsize = 0x%x\n", img->size); + fprintf(config_file, "pagesize = 0x%x\n", img->header.page_size); + + fprintf(config_file, "kerneladdr = 0x%x\n", img->header.kernel_addr); + fprintf(config_file, "ramdiskaddr = 0x%x\n", img->header.ramdisk_addr); + fprintf(config_file, "secondaddr = 0x%x\n", img->header.second_addr); + fprintf(config_file, "tagsaddr = 0x%x\n", img->header.tags_addr); + + fprintf(config_file, "name = %s\n", img->header.name); + fprintf(config_file, "cmdline = %s\n", img->header.cmdline); + + fclose(config_file); +} + + + +void extract_kernel(t_abootimg* img) +{ + unsigned psize = img->header.page_size; + unsigned ksize = img->header.kernel_size; + + printf ("extracting kernel in %s\n", img->kernel_fname); + + void* k = malloc(ksize); + if (!k) + abort_perror(NULL); + + if (fseek(img->stream, psize, SEEK_SET)) + abort_perror(img->fname); + + size_t rb = fread(k, ksize, 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + + FILE* kernel_file = fopen(img->kernel_fname, "w"); + if (!kernel_file) + abort_perror(img->kernel_fname); + + fwrite(k, ksize, 1, kernel_file); + if (ferror(kernel_file)) + abort_perror(img->kernel_fname); + + fclose(kernel_file); + free(k); +} + + + +void extract_ramdisk(t_abootimg* img) +{ + unsigned psize = img->header.page_size; + unsigned ksize = img->header.kernel_size; + unsigned rsize = img->header.ramdisk_size; + + unsigned n = (ksize + psize - 1) / psize; + unsigned roffset = (1+n)*psize; + + printf ("extracting ramdisk in %s\n", img->ramdisk_fname); + + void* r = malloc(rsize); + if (!r) + abort_perror(NULL); + + if (fseek(img->stream, roffset, SEEK_SET)) + abort_perror(img->fname); + + size_t rb = fread(r, rsize, 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + + FILE* ramdisk_file = fopen(img->ramdisk_fname, "w"); + if (!ramdisk_file) + abort_perror(img->ramdisk_fname); + + fwrite(r, rsize, 1, ramdisk_file); + if (ferror(ramdisk_file)) + abort_perror(img->ramdisk_fname); + + fclose(ramdisk_file); + free(r); +} + + + +void extract_second(t_abootimg* img) +{ + unsigned psize = img->header.page_size; + unsigned ksize = img->header.kernel_size; + unsigned rsize = img->header.ramdisk_size; + unsigned ssize = img->header.second_size; + + if (!ssize) // Second Stage not present + return; + + unsigned n = (rsize + ksize + psize - 1) / psize; + unsigned soffset = (1+n)*psize; + + printf ("extracting second stage image in %s\n", img->second_fname); + + void* s = malloc(ksize); + if (!s) + abort_perror(NULL); + + if (fseek(img->stream, soffset, SEEK_SET)) + abort_perror(img->fname); + + size_t rb = fread(s, ssize, 1, img->stream); + if ((rb!=1) || ferror(img->stream)) + abort_perror(img->fname); + + FILE* second_file = fopen(img->second_fname, "w"); + if (!second_file) + abort_perror(img->second_fname); + + fwrite(s, ssize, 1, second_file); + if (ferror(second_file)) + abort_perror(img->second_fname); + + fclose(second_file); + free(s); +} + + + +t_abootimg* new_bootimg() +{ + t_abootimg* img; + + img = calloc(sizeof(t_abootimg), 1); + if (!img) + abort_perror(NULL); + + img->config_fname = "bootimg.cfg"; + img->kernel_fname = "zImage"; + img->ramdisk_fname = "initrd.img"; + img->second_fname = "stage2.img"; + + memcpy(img->header.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); + img->header.page_size = 2048; // a sensible default page size + + return img; +} + + +int main(int argc, char** argv) +{ + t_abootimg* bootimg = new_bootimg(); + + switch(parse_args(argc, argv, bootimg)) + { + case none: + printf("error - bad arguments\n\n"); + print_usage(); + break; + + case help: + print_usage(); + break; + + case info: + open_bootimg(bootimg, "r"); + read_header(bootimg); + print_bootimg_info(bootimg); + break; + + case extract: + open_bootimg(bootimg, "r"); + read_header(bootimg); + write_bootimg_config(bootimg); + extract_kernel(bootimg); + extract_ramdisk(bootimg); + extract_second(bootimg); + break; + + case update: + open_bootimg(bootimg, "r+"); + read_header(bootimg); + update_header(bootimg); + update_images(bootimg); + write_bootimg(bootimg); + break; + + case create: + if (!bootimg->kernel_fname || !bootimg->ramdisk_fname) { + print_usage(); + break; + } + check_if_block_device(bootimg); + open_bootimg(bootimg, "w"); + update_header(bootimg); + update_images(bootimg); + if (check_boot_img_header(bootimg)) + abort_printf("%s: Sanity cheks failed", bootimg->fname); + write_bootimg(bootimg); + break; + } + + return 0; +} + + diff --git a/src/abootimg/headers/bootimg.h b/src/abootimg/headers/bootimg.h new file mode 100644 index 0000000..242ab35 --- /dev/null +++ b/src/abootimg/headers/bootimg.h @@ -0,0 +1,97 @@ +/* tools/mkbootimg/bootimg.h +** +** Copyright 2007, 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 _BOOT_IMAGE_H_ +#define _BOOT_IMAGE_H_ + +typedef struct boot_img_hdr boot_img_hdr; + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 + +struct boot_img_hdr +{ + unsigned char magic[BOOT_MAGIC_SIZE]; + + unsigned kernel_size; /* size in bytes */ + unsigned kernel_addr; /* physical load addr */ + + unsigned ramdisk_size; /* size in bytes */ + unsigned ramdisk_addr; /* physical load addr */ + + unsigned second_size; /* size in bytes */ + unsigned second_addr; /* physical load addr */ + + unsigned tags_addr; /* physical addr for kernel tags */ + unsigned page_size; /* flash page size we assume */ + unsigned unused[2]; /* future expansion: should be 0 */ + + unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ + + unsigned char cmdline[BOOT_ARGS_SIZE]; + + unsigned id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +#if 0 +typedef struct ptentry ptentry; + +struct ptentry { + char name[16]; /* asciiz partition name */ + unsigned start; /* starting block number */ + unsigned length; /* length in blocks */ + unsigned flags; /* set to zero */ +}; + +/* MSM Partition Table ATAG +** +** length: 2 + 7 * n +** atag: 0x4d534d70 +** x n +*/ +#endif + +#endif diff --git a/src/abootimg/headers/version.h b/src/abootimg/headers/version.h new file mode 100644 index 0000000..242218b --- /dev/null +++ b/src/abootimg/headers/version.h @@ -0,0 +1,2 @@ +//https://gitorious.org/ac100/abootimg +#define VERSION_STR "gitorious-7e127fee6a3981f6b0a50ce9910267cd501e09d4" diff --git a/src/main/resources/mkbootfs b/src/main/resources/mkbootfs new file mode 100755 index 0000000000000000000000000000000000000000..61299193afdc64e759afbd6a2527029dac24554c GIT binary patch literal 31456 zcmeHwdwf*Ywf{buIg`n2CLufm1Q=i-L16MAU{KHuBrpjRj1VMPV8~+vZOheGxvf{J7j;s!Ahv)na=&Yzy=M-GOz-dB z-{<~5zdyS3nOSRn_gZ_e{od!CeI~b7c~_(;3X{Rau3{wZ8>1l=B4S^+U_dHZIZMO$ z99GCIkj)&^;wv~VlT_MW3p?`jzZ=#FyJYopT-1xCvV27y?M;iyiaJ{6ceHm!dgk|(FPguo z%oPf_N_n}d`l!xVu3pDolJ%GeiF_W54<$|UyJL6%dTe9+a>s4oDILGL{_C4&JpNZi zjT$IT(KRoMpPB?O0G@M^rY5+O=vO4cS0=%)0q(%Z7&ZeKE#GaxNAtfY34Uu5{C40D ze2n1{0HdYU+bGUNmJjHdrDN${6}&%nnR{s>^Sbj|~J7?mMsDx>R~f`2;+{{1BQkCNceB*C9cf*(wR z|4S15cS-PffoHQ1M)E2y?nEC8yuyI{p*DtPvT`whORGO8Nhiql?d;L%Qd7`SI2aCe zu$A8GWy^h~u2R=RhAFR^X1GOcduw;Fy(`?xLgAJ`B+Pt1it~lSjlr<5v$4Gk5y9rI zK^F8kc60=qq0rVD=n@J(GV=$6%-7!49wyZee;4z$LLwm?*v8E}yP2=LzSaj-e_MMf z><`x0F7F6*`Rf~-I{aLvd#9!{szRXK-__C{WWrUSueqm@GHL8+ztPVqc2OCXsXM$C zZnjX#ikYuwyKjxiZh1#zDC7@OAzF|Sw28xfzm0u96bPc&)z|pCBjIL_qm0ero!x$4 zlaER)D{cXkb%jdmK@EkGPAj+aQGGOT^EGeX=4)+iPgFJ(+Wg@VQbcvMM7mk4@U~g2 z&DI^k_OPEdcLYLy)~R7!pyIX${eI@}LB+Oe3aueubD*oWy^VGHJDX8u(zTK@=JU5S zh8vMlQz)dh4xp|U-qewBdq)Vh!iBkW1=p$d0MuJsGg_Ra66U;SP4&v^)kQ9swk}>F z0jQz42(+9R<1-nbarlVs1h|VsDnwdi9;4=-MB7A6_%*RK?ht|OVP7qrGzJBZN? zQ}DThW`H^buU|);0-Fd*Yk46)69h(NuHeZx;>ic{XYY_hVdjc7!lBc}wmx7j_g{Mby z&sFrT^&z6Wrqeg50YB~FFxzXu>qMuqR%r2ZzbEA(p-*)p`vYkX>Tv4AGCZZjWm**X zfDV^^f$)7gocg&8kLqy!zG}Y?mwkzBjtiW8k$YsqV>(=)&S@Pk#~$gQ*WuK*@o=@q zQTjBVWU%UR>aQ|n>u@|*%2L1 zYlRF)bvUgbG91(4`g!xX4wvfy*~D}>tr0Su*5R~X$Z%eV%Q=VQJiYH(WBnKru?VJt z*kX(&PwzqNfsutbVp$oPbrZe^5k_W_yp>FbkB=bC3X)7!H++=KH;_z2Yj}vuO(fIQ zGQ6M5*ON>`cz7R|*N{wAIXuYa8j`824)5afGLmUn4tI0;3X*B+8Q#L>Qj%$?4cBq` zGLot3hby=|gJkN;!^K>lL^4fL!wxQwA(@8oa5k6INw$&9xST>V4b9=x7XZxq7|YLA zs=DFhTs}iGO+CX$x%?-Rsp*D?xcnB$G?fkS=ko7KrlC8$kIOHUOkH4jkjpQUOhb5h z7ngrQGIgopZZ1DVGIfFBEnNOF$utCp>$v=Vl4)oSS8(|elBw$r7jyY*B-0c(?BMdf zB-4~MoXzDgl1xKwm~nYG$*~Gz>MyXQd-}5uaI*9uNbD<6{LA1V^+`{^`ZUG#4TV!i zj`A)!=owJUkf>*1>1>d7o;l||(YHOl=WW~VP`_8Gn>LLc-*f=Fil+N9wrEdNzvKqb zpEr@wS3T!GLfW3_p)hR6ZRWNwhhh8HrUSc{z5p8*@vrmrF0DZngNeI-Z8+1@zx3;f zie310WW+Nt?x&a`kkL~_<{Yh#{?XJ*$$sd07XLn!?HT!nCpzvXi0+ZGzdd;( zwh*=XIrqj*ht$mgl>^A+W~9KvlTeBim}IHYwsk0G8zmfld*tY*;dwA>ZEbbgr|p3% z(zz&!2XxM4>449ps=pN?~lsp4_oX3emehT#P9_LR$ ziZymWh!r1C|CBQFyFUX?yRsO2mc}hzYO>gWe1fX#ztS`CB`2wSqi18sIkez88mv5? zE-+=A9|waTRi=lv#6dt(L0@1z}|tMIGKZzJco9SJp*&^z!Il7 zI^v1G5&Hp&(Q~mUD6{ARps`0a$Q!*7{eATL7)_5z)T||X_gH@HB^$yJ1n9115ZclV0nqwgA z1ZKpNv(IR?e3LUTwg9%nzd#1n&4;TOAG&Eql`>M@e9rUyJ0?#*Da^Pi-w8b5Q?YzP z`_axdQS%4fqhplD$f1)n?9(dIpX^Uo<@TO;g>$QWU&!@FPkE#7#Lj&@GSW8``LYK- z{{Xz^bL15&NFDdEw)p~_yc$j>TtxlG2B2NlH{$K@a9Ztm&ou^lxdE zRzH8j;i0ZcmCmY_gVoP}3cH(-di1SW75wPE$?0XcT($gABq2H zHZZoF7{{`K%yJKv{m&;|7;U=ohRk@SycIhGFA`Q~yE zh$~|+lYZ|3%xIO)x{)JP@H%@Rjc2XBe$3Om39a3GumSUedlU7%O~cg^`x-Kg6jS%Ofu?rY^mM7hdx7J%X9Z$fazpz zS_OTwt&N_ljDBeE>yD?;YVTVIfl{&et>Vf7>vKaWd`zLTMAcHHiAuQ|r9~6Q`hiwQ z57qXaweRV}HpDaVb7!#wQ&USejl?st8=+8D@;)C6ccSl9eDN`^P_gHI`<^cVzzEEw zp6;Mtn|-*_Igdu+6G~Odi2I+B2#c-cIb(=>2Ns?{1$qa*&6BWCV`mR2;S9`+-#`tY zENdM^mQDJKx?9vb5ECyoSaR)Ro7p6UwD>=MC^H~Cd|JH8B+@C2^DoX_G5_4 zkjnWI46d`W-iu?uBw|0+V*OeypN?R=oyVHzR*xJO_4=--zuxJHO+kdG+3TEFz4$lw zZycZ%Z5%bWEF4vG-!benYMW0%<>mq!I@#WS)Ha4Z)>*X~R}UxM?&uq}(a%n9(>lb- zTB^p8Ayi={qIFd`|19qVE}$2uv}(ZH$vlV6M^IWc`?=UF(Cj^Mp}O~QHqF-?QAK@! z(6X`L)dwZ_$RV%jE8c+?XEBx}%#YRmo7l4$*Ql~LV-Hhq*gXu)_gk=TXL+J8ct&10 zq-lxuXYOBVvU-;Wu7k59u|iP36uldpqCuEs`BL-{1}3V<>K^$uhJ$kJJ@)lO_Bnf; z`yI4y?{V%EbWqS;f_4kKMbJ7yD+DbD#h6%yXPyb=yIjCK40aftcqn zDDS}1St_5QYN$&LEd3^G40$hsr0=hu{;NIxi-!APqMLuP+TQlwK3p`oqrZ;6QcVl} zXNlY~1wDW`&%jtG@9tFBPI!v-wr{e6vei5DqDN=3(~K=;@GZQ2Dcc8cn>^S#3^Gif zbW4FIiwnpWR(s8`@?a0)8Mu=xU@5DNo^eNqV)GC){4Ll}G_O7_E=Fq`Uox<%sEmFP zdj$nMdGk`X3su?o5DZGr()4{Q_I>2xjvlP)J7M4Z9ZCoty_kBF`$Os$FL?Se?>vZz zO5STc(cj1JpbEs8DE2HqZST96jQ9qJobp76$*(UFh}1if2n=H_FiF%9+J|Z=av55O zs*zfU78h)fmsUYOIUCH6!DuVYKcMnr_nzVH)JxH+h%Svs??p6#D+TaIY=;Ic=Rm9- zQhfiWTR(c=>#2$!rcLG+H@3*pzg9;->sP-pov}wv;hE@m%%Sx+x|-1X+iH}iUv1zj zkrUqjh3mj#Dh~%}yR@;oe<~*7i{?92BsG5(oTu&9-KdsbS7N}Q4x7Bu^VR*E^QwCf zVJr3r>>eU7_j|1FXl;JKClkc+G&_x^nOU2c;mL-aS3R&PuM+do#*-0Z-ThU0n7BI< z9m)QsA^Vf3vC01-Jg*+8&95D(a(D(Ro!GXSo!$YjvxC|s24^r~b-1Hdc_%M(_pd;v zRr#REwaP(BKc(edmFLb_fvl^vZ2QMug>(m&DN%JHnEnhmlE#Lp1KV=!ny3nBIjTb$ z8EA!|#exJXIQHh>;Y+U&5|seqsve~Z!otdg3FZRb!`$uBV`J>;+Y7Owf+0~^hO z=#pT(EQjAvZ)3j>i0&$}gSC8+Nd*{6Ej($cP_UAt{RYzZonUE`n zTsXhv546AcMnA^tR~bEpi?r9VT~j=ZPe%SkV-y>=4qBR8u*`#V&>~exi%c=REk7XU z%MTB<4z9zs+FH-RltEmDukrMoaA`Ucw$jz>N$bf|4WHWt9e%&%>!=-mNlTm76oyAg zf#4Mm))jEz!H2&&90<1iLymC3(H#sl`$M6t(fgYlySf5l2R%TEYwdIt%nB7SdgkJ9 zA$|S`r7`R$4M$gFCsJt)1{-%e3PX5SJG!{wDC`Wh@Zw?jH>-G2&#dC||NbX5tC_i%ubR&A>?vM7yuQgZRB$V* znK{x~{Tg?*w`$Gw>1Ys8N!ezE%x2?R=HmLNb-f0<7?=s(Qx}B6XyKjI# zg@MZt!dz;%(smjd84RgBzs2V?meCAQ8E~hBy@yW>_)lpD)2HXQ6|b}%P3yJX8FzvF z&WBzPc2;YX`Mv*=>|uqroNT(X5PMVFA)8N*jO+pb?LUu<90c8i`aA(T7WI~iYdABl zyP#qCbra|w&@X{jBHx3c-^O)!CG^X1@zw##Gx+=0-)rFSHSqTu_h(a9JxwkwAGxkEh0ib<2;tB#nXCCfo}Lyko2fny!k~3a4W6Z2)Ike1HUK% zhh;KAfSIG(nHs@zg1ZGgJRlsAdje^XdkZ||iHvE@rcf*NWxD)HA@ILm(DFMX6op%C zL@-j$6!ub%@lZ^wuWpM^mak-7xcS= zJ}KxSL0=X09YN0uYRS>k8!zZ=K^Frm?Om(iM&rd2y~V7=Y>`*Mj*a5e_aSQ^=zkhj!vXV`ouw zAm}e@?rskhb#80I1!il=)y#^v20HykJ8#?>DcTVTZVPocHv5Y@88UC81AjPp$-4YK zVVs`C)fhk zg@Zd84shb+p$}&|ecg@WtvKPhHqzbg$5GAdwH<*S8q?U(y%lE$tJk))x8ax}n4UPt zoh68$x+JD8*x1bzYC;umBcvHwb#R?XS2MS7?C6SgGMw#fb6iv$Du^$fC?YR_&quE7 z;m}q(jY${4KJAiImYS|SdC778ixZa~ex5Rl)o=SI^ zN-P9A4;dGl8qoqI@gtZ%gDCB^NNLyg}_cgFF+}_E;+kGKF z6&M2bXvkdzkQnL)gG4(?5ww{oT;jC`pb_QA*<%{gIC;!E2%!VbJcfLv;Y(+F!_;!9 zC(bjDH#c@SHnn%OhjG6Q6G9gZk-NXMJG_${%JG8}*uGGSXpT@RXIk3n%rSK}(HK5D zb=(~2>~0KVXvh}w@z!9C-K?!!_L9~(7rbF?!p4yvK9N=iAV zYf1D7UITon0L9Hjo`8=}7cD+MhtNGnntJK>AZuq~bjG%3`l2otDq%e!L4PRHff&}r zc&)dvFpjbFGuHIQdxum>U#5vQ=tl06WI>ZPHi#|3jnP$VST9I@|FSe;}Y zaVTj*r{W3KOEzJ(RbZOByX*!8-Sx$OchJuM*d!Jbfzm9{x`^-|1$+!+Tq-)EM>PvhX)`^<-g@TWKk&3DDc_@4wdF=$HRTpWaEg}5586qIWwb1^LP zP*}ZW2jkEt$x8PW=6liL9I9Ko_P+1}T9Q2zhc-!8`U)-Sp`?Ib6;8h?=qRoE4dKC$ z;(pc{OLy!NCofS^#m$S);#HSpeOL;Xc-7V2XD$@NL2+Ba#Gq-+Q6^U+lErdlR4Eiu zYvL-K-w>J08pM(cy{%Y&5v9868ah^t^Ojc_@XL>MZzaO0Hvc3>%nBnorx!h zWlajw4HLs4H(J_7vQ9x$*yoKZ%&0R;A2q5_GMhz`vINvvHw$bqo?V1}B6z!~uF)EG zjUg=)!vHfn`m|6=6nNvMN-rCfMk&lb;mC9G_FE^}*To&XQKlDZ_4Bw@y<~Ta8;XA; zucw6-?+8+mb|yATL1cbU#`9Yv*|*|}?KsZf6T)QSI`#s`*-Rlkst128ga+X**OWed zOAr1~2*1&T#^#U?{wVHE!+mBcd=>|>4oX4i;_Go2{RtOeXHt-^@?}&CvT@+|>r4u- z#$6m`!8M7r=L(7jatXFA0U6UDr5Qb!SY5K22ndxPL3tP6Cj^;A3fpO{M)nn<^O&G1 ztosrjB}bWM%T-Ob>xO=!s2fHbTs%g-tSnnp^*q?ZXM zBZDW8_e|}K?6PtdlPx1dbHkPu_o*Do*v*QJ$u2}pKCVV{#=}g@7VneKMqzVj^MuCQ z!H*lo`KvYe$HN0rAj)?F?ZWbO>g44_(jjINxtl25+slQ3Q&6jol5mX z<)GxJxlk+!Dz7{T3gb|Keq2MawIj(@fbF5D+X*xER5; zin5^!lc42#h;*r=Z0LoiWe{QzwW4gG^erz!#2d~sb{jHRxk$;TzJe{2gVJxZGQMp} zr75(I5Pup*8lObd)`OvGE&T~Fi@{{9$FV&MtMEy`5oiRcNN*Qe?-uDX6U}3(X7vUT zUf|2qOvM!t5LDWHRgwDKG!va8rjS_!AnR-3@|4-OUQjBQQow8j;CT{-R@*&sKGXK) zIG@cj=@yDNgCHnvobM{L_tl7UKlAZkq5l426 z$Pml^faoqI`(sX1vQ6YnHvIrfO7=K>o3p3l+sblM(Qb>unyxYxfTUV8bAgP>oQUsS zy67`ikfM@#83E+x*vwgAbEqL!<74hOtp`a>$t0i0$?ihuxRmw3qW&-m4aZnJHSQ!b z9dEj4zw!yi)2hMJ#y#y`Fsr~!G~csph^;3sNQr#^*aQ-2m`F|oD+QSueVu~TEn|LJs>MsX@GrFu_YEv+jUkP0h4J%$bC*0_a*x zW={n6+8-oQc0blSPTA13VBL)0ln+RVO zVZr-S$XALBK9Rx{rM$q33gih)RVoU`O2MIc3T8-Qx>8p_t;p48C=CVl>du3x<5Z`z zrJx1EZ%CM}Y%SOZfqPx3bQgrAFi!~=d;!95NzJA76wvef3Vc#am0bnj2J;TZEKqhA zJO|-@63Ud`f?q=Th=g)wZ^7>&(2SJoRt5{+gHVJ|YK`(h!G}^zjuegi!9NpA2K z8Qhgn8Otua3eKqcyqhVG<(fc)n9Z$$fDMo0Sfeq7IvNuEbUp}km2MPeIv;^!Ij`$^ z%)#chl10BVl`jng_t3b`S_}6E(_FS4h+B=hKCPJ9ZU?`C_=Q&6m%+CXzckzSDENiQ zI_;`F+kWr?!YlG^&w;;@c(=nwx5%h^(v~}IzXQ+HtSYqqJ9z2^X*I>Rx54+3{_1ku zr{D*PudA@-z?ZKR|2dEC3dwJ5uq~7PrmeOb@P|p~h8}H3rF)I$+xg@=Kx~w=4-tDO zpE-X@>>ZpvMC@K(|EG!lHfJqJ()_)w5|Hfl9@|9FpCgC#8<}kmcxvACFJS&A#zDGY zo=GfkSrePz0LW5DWma5U;yf#oM;_>ytFr!I>D$8_x0t%sYxqdH0xj{nCey5z5f%6L zh?J!8DXdnTSNI&J@fT^8eu5l#~lGb~>gmR}>wtxEc{Fz5r$cR#xbyie{{*rFl6 zw4hcAU;iJ}u3x2kzFb-N0eSRQ8yetprT)XXkfArx)}gQQC5bmJXYorO)K5C_$@sOH z0)m>r2m4Nqr|lfeq>*brD%_w`&&v9fh*#dRyamVkT<{Lnisf3|iQvKuBn9>NDT!yuRmQIDr6Md=Ty*1ESxlvN zad#F|p{L$M=#{%+G-K*N5MM#7?$oD=b@K&StJoEZisdZn)4Zjup# z>{G3_e*{kpr`?fldkQ@57wyx>*q#I5PWm%)wX!`7!2HF;mII5APJqY8M-H2j5(W9Z zPm>bg=}3?QRS$(dg8meqQ8*&#QVe7YPf~9|kT<0@De*1utZoo`{Kq>G9TajxQ{!sT zO)1EmeDaQI8+E`@!ViVMyhkVX)?VfU3J_P-SPQ9+LhDsOoivy^wg=(2|bU2Id3 zw}&(-@qG(gi$b5E4+tu6e`&FaZwvaZ) zPNH%6sfGin9eJ36hPZZ~;%?+&JR7wHE{IbOG;i}`D%S$joz3#X89Fa<8DM#Qi!K=D z!3|m_Mme@bQ?8bB{4@)_m#I74RSdD2zg;7bYEXTgyQn5;Fwvn08ZG?L3qRsOW7=?f zg2wixM=WUAi6aj*hA%poMH3VqB%%$AJWNFM3Y~AjK@!@_=?)3emMDH`i}p?O2o5b! z`h!Naza%?xq={ytOV5PS)`Fwt09`onBQSKez|RlS&Ln=GhBjUD*bZ%|^apcjzPk9B z2~8jIVrpl~XlIu=A4Ds;?i3Gg-Q?jSx_XqSP-xpH&koV_lsFtk+u`_eC)&xy5Bkt_ z70;WOh!z=smWSr__!%9#gft%7qo!Yg#8TLFq@pOF;I@N;02m*|c+`-NP4V+Ip(6Z* zAAYb;{C+Kt9frb1_#s>0qB8NTwr%+RzUER_$-?qyCTRMKckp%@qZ%=R0^h!#OY1yk#uax zwUuTi{L*EMzZE=2I-w!3=mdaKwQ-g9fCdBOtY(;C8^c;Wbr)&GF&aSO7K{Xmw%|Mx zc;u#yT@8=($Qs*|6nSUFG)Ym_^c3GXH7muHtE!fVRGY=R0nB4+mZcYTw`CvhOP*3* zCB3=h-&QC8P%S{Lx?5E&DKl~rx#w1EgPN9ZO1D0wT6@*8zgO)W)HKU(HOKNJbqeV& zQL|Wz*=#w7N3AJlW*YmdI?mK(eMZghRc91NKWk8(Xt7=|u*X3MVQ4K;bK6wgRupIn zGBX7lOu5#EeQLI81yZtG^VC$!Ll$d9%{7f{P&0_X9);V{0Q-kjW-;|ERu^g~r7(Au zI>xJx;}tPTWm)o!nl`9TwzMJN8q|9P3bjJvAt+dCNXzO_(~hY5sGAw4)q`s8dez>e zW>}|lhcYeJVzmgBl4dc@LlI1yQ%V&z?-6ie7|!0TPIyzzDOMd&xy*%P-iG|BdT&(o ztdFSUE$}T5xtJ{0&(!ghouvVJZMfR3qJ(WH%nHgfakrYcodbRXa!iwVtCJ(B-xKPX z-Bc%CHK@|vD1m9*5w*y)4K0pj$fX*zV!d^Ynr0e@+OsY|^*^a*6(ftVI@^V2x=oz` zRn&W}Iu(p+*`wM_IlGst0MtEdZi+KkwOMmj`_Ir8t!R5UQhegpn#XnhFw0_kiNP1s zcFOLFTb@brYzH_x(=Cl34L`C zC2CoXI?eKrFx!p#T?O{bbRK9KM%e5cHNOTO z(KKUFwZDzdmF_|#1{=`*#-_BQMv=kD zY*NQ>P-oJ(KLJM`R+a7QT;8aW6Dc+C|<=+UjxTfHj zXkQw0u~H61GhX_=7}ee>R`4)G{_ehvr=6jKw~>o!H>nH!8l8<}6$ZQtItqUAHm^db z40a7Z#aYZ4hBJ9)4UFz@S+(U<~L7>dt2Z>f#++{?9T%Kg22ZK{G7lC1wKjO zNSvqteSzc26Ax(uKVV1)_m;W-F@rws!^zJN3^@Hd1mWp3wRD$?^ydqFrNA+X@UTeW zUV*0w{Az)(7Pz?a!aM?R7r062Un}qqfn%@8!}S6`I9-!vy)_B^xWFOuaD%}AC~%o? zP~e{m96#>J!;J#JOw@xc7d;}N{-D46LI!jUnD8y4q3AIQg$D$#e|ASWe~iE;vg`_y zs4r=OexIHCVR?ln%l`A6NJoDsi8Kxi{X&C2UF1_b#R8Y!x0VK4A8@PC)^pUf%Ha|^@N!|{iO{yszGca!M< zqtHL?;N~o<5k^lxC|~_eV~RQm+<|_=DukjS^dun%|Rcy z{|BKjuaxK+GKDvj=+h&c(dy^VN$?Mo;0ikGXz8RS!N(@S=}yCF>C8=n)5D$7^yx9v zX!x=ucy$uIE(v~J5`0q33A zX1sT(`)3%eIT#LwBltxqdJSxO{WWWR-s-jWJ|CpYi=>+6x+wf745^p+_ZYr#rw{+~ zp$o4k$$yBUgK(!R;A`s$G~or&7W`CF$k!O@;kPi2>-M! zN=qdpQ^uhuElN_EflOAWx;Sw!Sl$%ocZtP~T#dMNw{S1M%PQ{-8}9|{Zq15TtXf^k z8*#-NcWsqVO4NvwPc#Spa+TH`XrxfUx3#gWr33HMX+1#dQ=1FEa>HtOZS`_!(0@=t zLGW8u^51vpA^!I0O7Atx++N=`D^{$ns`u5qmwBtGchRjqUvnseH%8_EUBOGaxZ{Wy zE93v)MW@F9R|~&IPH(cw+;F#+-+=P*cWS}@&3^}j;w5Zxb{|)oE{}+sWj6nDA z`Fo^vqyNADzcByj{|)AU^?$%5`^PWL)qHFmax32jB`U;<9kot<0`2KeiuF8-ZZH~xp*k71|agY7C0<${q^UKfgJt}A(> zbW9JA7UK4Fx`%?K4lX6WuLN<$y$;Uc9i^OwJeBI!n9Aj>WDmvRc?@vFB1 zX)k|2MN+!YLqY0GdktmmC>Z)}5^2vXgCfO+{ubg9q`lOW^jnCd-#U@<7HKDFH@8ld zQKlncc1WZx?d9*&NNP<|ep&zXg#9v+;4wT^q#)@QY8(X07l&XkX}?9lC<{Cc<3avk zGf8DWB063VQZ50bx3A0AV&YU5Oc;o0?De!j$KCbz`^2VEQY$Iwg0z#+r(pEhdvnXXA65-%$}K=wxqu`*cGPNWpQ=aPNUP&ki7#m{5o#{Iy}TcDMA*ywGE(1Y zABC~ro>d6VVvUN+VQC)sUjlT{KyNSa3+D-YsVAv1f4Xf&n5;jeeW5fI)M!ulZz+Fc zb~683gFWd|khD+mMti>iY;t1ZRtY<4zZQ(-g$gGjnwSt?qe)8_jJhNnF;cIo)QqZR z#c1Lsy3YU~&HnC{ngJbo(Fdb$0Fg$F^mFSqtNn&ZgUIN$Nh3y<2i9r!>4r#y$gW9Z z|EudXdpdEg4+as(;6~5?NP}i|Ic-jK!D#>Qh%=~Y+FLYY-2WKouboIx&V8DN_U#b% vb?KU1076|t_FuU!eIE9PCguj+H)wYI3>g~zZ%bmIO%01c2b1){AhQ1i$*Len literal 0 HcmV?d00001 diff --git a/src/main/resources/mkbootimg b/src/main/resources/mkbootimg new file mode 100755 index 0000000000000000000000000000000000000000..d9e4f2c7438acf4666744fb722e7e59b9600b2d1 GIT binary patch literal 30808 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 E2hCF55C8xG literal 0 HcmV?d00001 diff --git a/src/mkbootfs/c/fs_config.c b/src/mkbootfs/c/fs_config.c new file mode 100644 index 0000000..16eca31 --- /dev/null +++ b/src/mkbootfs/c/fs_config.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2007 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. + */ + +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#define LOG_TAG "fs_config" + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define ALOGE printf +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + __typeof__(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) + + +/* The following structure is stored little endian */ +struct fs_path_config_from_file { + uint16_t len; + uint16_t mode; + uint16_t uid; + uint16_t gid; + uint64_t capabilities; + char prefix[]; +} __attribute__((__aligned__(sizeof(uint64_t)))); + +/* My kingdom for */ +static inline uint16_t get2LE(const uint8_t* src) +{ + return src[0] | (src[1] << 8); +} + +static inline uint64_t get8LE(const uint8_t* src) +{ + uint32_t low, high; + + low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); + return ((uint64_t) high << 32) | (uint64_t) low; +} + +#define ALIGN(x, alignment) ( ((x) + ((alignment) - 1)) & ~((alignment) - 1) ) + +/* Rules for directories. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. +*/ + +static const struct fs_path_config android_dirs[] = { + { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" }, + { 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, + { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, + { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, + { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor" }, + { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" }, + { 00755, AID_ROOT, AID_ROOT, 0, 0 }, +}; + +/* Rules for files. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. Prefixes ending in * denotes wildcard +** and will allow partial matches. +*/ +static const char conf_dir[] = "/system/etc/fs_config_dirs"; +static const char conf_file[] = "/system/etc/fs_config_files"; + +static const struct fs_path_config android_files[] = { + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" }, + { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" }, + { 00444, AID_ROOT, AID_ROOT, 0, conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, conf_file + 1 }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" }, + { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, + { 00644, AID_APP, AID_APP, 0, "data/data/*" }, + + /* the following five files are INTENTIONALLY set-uid, but they + * are NOT included on user builds. */ + { 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, + { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" }, + + /* the following files have enhanced capabilities and ARE included in user builds. */ + { 00750, AID_ROOT, AID_SHELL, (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" }, + { 00700, AID_SYSTEM, AID_SHELL, (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" }, + + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, + { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, + { 00644, AID_ROOT, AID_ROOT, 0, 0 }, +}; + +static int fs_config_open(int dir, const char *target_out_path) +{ + int fd = -1; + + if (target_out_path && *target_out_path) { + /* target_out_path is the path to the directory holding content of system partition + but as we cannot guaranty it ends with '/system' we need this below skip_len logic */ + char *name = NULL; + int target_out_path_len = strlen(target_out_path); + int skip_len = strlen("/system"); + + if (target_out_path[target_out_path_len] == '/') { + skip_len++; + } + asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len); + if (name) { + fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY)); + free(name); + } + } + if (fd < 0) { + fd = TEMP_FAILURE_RETRY(open(dir ? conf_dir : conf_file, O_RDONLY | O_BINARY)); + } + return fd; +} + +static bool fs_config_cmp(bool dir, const char *prefix, size_t len, + const char *path, size_t plen) +{ + if (dir) { + if (plen < len) { + return false; + } + } else { + /* If name ends in * then allow partial matches. */ + if (prefix[len - 1] == '*') { + return !strncmp(prefix, path, len - 1); + } + if (plen != len) { + return false; + } + } + return !strncmp(prefix, path, len); +} + +void fs_config(const char *path, int dir, const char *target_out_path, + unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) +{ + const struct fs_path_config *pc; + int fd, plen; + + if (path[0] == '/') { + path++; + } + + plen = strlen(path); + + fd = fs_config_open(dir, target_out_path); + if (fd >= 0) { + struct fs_path_config_from_file header; + + while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) { + char *prefix; + uint16_t host_len = get2LE((const uint8_t *)&header.len); + ssize_t len, remainder = host_len - sizeof(header); + if (remainder <= 0) { + ALOGE("%s len is corrupted", dir ? conf_dir : conf_file); + break; + } + prefix = calloc(1, remainder); + if (!prefix) { + ALOGE("%s out of memory", dir ? conf_dir : conf_file); + break; + } + if (TEMP_FAILURE_RETRY(read(fd, prefix, remainder)) != remainder) { + free(prefix); + ALOGE("%s prefix is truncated", dir ? conf_dir : conf_file); + break; + } + len = strnlen(prefix, remainder); + if (len >= remainder) { /* missing a terminating null */ + free(prefix); + ALOGE("%s is corrupted", dir ? conf_dir : conf_file); + break; + } + if (fs_config_cmp(dir, prefix, len, path, plen)) { + free(prefix); + close(fd); + *uid = get2LE((const uint8_t *)&(header.uid)); + *gid = get2LE((const uint8_t *)&(header.gid)); + *mode = (*mode & (~07777)) | get2LE((const uint8_t *)&(header.mode)); + *capabilities = get8LE((const uint8_t *)&(header.capabilities)); + return; + } + free(prefix); + } + close(fd); + } + + pc = dir ? android_dirs : android_files; + for(; pc->prefix; pc++){ + if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) { + break; + } + } + *uid = pc->uid; + *gid = pc->gid; + *mode = (*mode & (~07777)) | pc->mode; + *capabilities = pc->capabilities; +} + +ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc) +{ + struct fs_path_config_from_file *p = (struct fs_path_config_from_file *)buffer; + size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t)); + + if ((length < len) || (len > UINT16_MAX)) { + return -ENOSPC; + } + memset(p, 0, len); + uint16_t host_len = len; + p->len = get2LE((const uint8_t *)&host_len); + p->mode = get2LE((const uint8_t *)&(pc->mode)); + p->uid = get2LE((const uint8_t *)&(pc->uid)); + p->gid = get2LE((const uint8_t *)&(pc->gid)); + p->capabilities = get8LE((const uint8_t *)&(pc->capabilities)); + strcpy(p->prefix, pc->prefix); + return len; +} diff --git a/src/mkbootfs/c/mkbootfs.c b/src/mkbootfs/c/mkbootfs.c new file mode 100644 index 0000000..0e35323 --- /dev/null +++ b/src/mkbootfs/c/mkbootfs.c @@ -0,0 +1,363 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/* NOTES +** +** - see buffer-format.txt from the linux kernel docs for +** an explanation of this file format +** - dotfiles are ignored +** - directories named 'root' are ignored +** - device notes, pipes, etc are not supported (error) +*/ + +void die(const char *why, ...) +{ + va_list ap; + + va_start(ap, why); + fprintf(stderr,"error: "); + vfprintf(stderr, why, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} + +struct fs_config_entry { + char* name; + int uid, gid, mode; +}; + +static struct fs_config_entry* canned_config = NULL; +static char *target_out_path = NULL; + +/* Each line in the canned file should be a path plus three ints (uid, + * gid, mode). */ +#ifdef PATH_MAX +#define CANNED_LINE_LENGTH (PATH_MAX+100) +#else +#define CANNED_LINE_LENGTH (1024) +#endif + +static int verbose = 0; +static int total_size = 0; + +static void fix_stat(const char *path, struct stat *s) +{ + uint64_t capabilities; + if (canned_config) { + // Use the list of file uid/gid/modes loaded from the file + // given with -f. + + struct fs_config_entry* empty_path_config = NULL; + struct fs_config_entry* p; + for (p = canned_config; p->name; ++p) { + if (!p->name[0]) { + empty_path_config = p; + } + if (strcmp(p->name, path) == 0) { + s->st_uid = p->uid; + s->st_gid = p->gid; + s->st_mode = p->mode | (s->st_mode & ~07777); + return; + } + } + s->st_uid = empty_path_config->uid; + s->st_gid = empty_path_config->gid; + s->st_mode = empty_path_config->mode | (s->st_mode & ~07777); + } else { + // Use the compiled-in fs_config() function. + unsigned st_mode = s->st_mode; + fs_config(path, S_ISDIR(s->st_mode), target_out_path, + &s->st_uid, &s->st_gid, &st_mode, &capabilities); + s->st_mode = (typeof(s->st_mode)) st_mode; + } +} + +static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize) +{ + // Nothing is special about this value, just picked something in the + // approximate range that was being used already, and avoiding small + // values which may be special. + static unsigned next_inode = 300000; + + while(total_size & 3) { + total_size++; + putchar(0); + } + + fix_stat(out, s); +// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode); + + printf("%06x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x%08x%08x%08x%s%c", + 0x070701, + next_inode++, // s.st_ino, + s->st_mode, + 0, // s.st_uid, + 0, // s.st_gid, + 1, // s.st_nlink, + 0, // s.st_mtime, + datasize, + 0, // volmajor + 0, // volminor + 0, // devmajor + 0, // devminor, + olen + 1, + 0, + out, + 0 + ); + + total_size += 6 + 8*13 + olen + 1; + + if(strlen(out) != (unsigned int)olen) die("ACK!"); + + while(total_size & 3) { + total_size++; + putchar(0); + } + + if(datasize) { + fwrite(data, datasize, 1, stdout); + total_size += datasize; + } +} + +static void _eject_trailer() +{ + struct stat s; + memset(&s, 0, sizeof(s)); + _eject(&s, "TRAILER!!!", 10, 0, 0); + + while(total_size & 0xff) { + total_size++; + putchar(0); + } +} + +static void _archive(char *in, char *out, int ilen, int olen); + +static int compare(const void* a, const void* b) { + return strcmp(*(const char**)a, *(const char**)b); +} + +static void _archive_dir(char *in, char *out, int ilen, int olen) +{ + int i, t; + DIR *d; + struct dirent *de; + + if(verbose) { + fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + d = opendir(in); + if(d == 0) die("cannot open directory '%s'", in); + + int size = 32; + int entries = 0; + char** names = malloc(size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to allocate dir names array (size %d)\n", size); + exit(1); + } + + while((de = readdir(d)) != 0){ + /* xxx: feature? maybe some dotfiles are okay */ + if(de->d_name[0] == '.') continue; + + /* xxx: hack. use a real exclude list */ + if(!strcmp(de->d_name, "root")) continue; + + if (entries >= size) { + size *= 2; + names = realloc(names, size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to reallocate dir names array (size %d)\n", + size); + exit(1); + } + } + names[entries] = strdup(de->d_name); + if (names[entries] == NULL) { + fprintf(stderr, "failed to strdup name \"%s\"\n", + de->d_name); + exit(1); + } + ++entries; + } + + qsort(names, entries, sizeof(char*), compare); + + for (i = 0; i < entries; ++i) { + t = strlen(names[i]); + in[ilen] = '/'; + memcpy(in + ilen + 1, names[i], t + 1); + + if(olen > 0) { + out[olen] = '/'; + memcpy(out + olen + 1, names[i], t + 1); + _archive(in, out, ilen + t + 1, olen + t + 1); + } else { + memcpy(out, names[i], t + 1); + _archive(in, out, ilen + t + 1, t); + } + + in[ilen] = 0; + out[olen] = 0; + + free(names[i]); + } + free(names); + + closedir(d); +} + +static void _archive(char *in, char *out, int ilen, int olen) +{ + struct stat s; + + if(verbose) { + fprintf(stderr,"_archive('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + if(lstat(in, &s)) die("could not stat '%s'\n", in); + + if(S_ISREG(s.st_mode)){ + char *tmp; + int fd; + + fd = open(in, O_RDONLY); + if(fd < 0) die("cannot open '%s' for read", in); + + tmp = (char*) malloc(s.st_size); + if(tmp == 0) die("cannot allocate %d bytes", s.st_size); + + if(read(fd, tmp, s.st_size) != s.st_size) { + die("cannot read %d bytes", s.st_size); + } + + _eject(&s, out, olen, tmp, s.st_size); + + free(tmp); + close(fd); + } else if(S_ISDIR(s.st_mode)) { + _eject(&s, out, olen, 0, 0); + _archive_dir(in, out, ilen, olen); + } else if(S_ISLNK(s.st_mode)) { + char buf[1024]; + int size; + size = readlink(in, buf, 1024); + if(size < 0) die("cannot read symlink '%s'", in); + _eject(&s, out, olen, buf, size); + } else { + die("Unknown '%s' (mode %d)?\n", in, s.st_mode); + } +} + +void archive(const char *start, const char *prefix) +{ + char in[8192]; + char out[8192]; + + strcpy(in, start); + strcpy(out, prefix); + + _archive_dir(in, out, strlen(in), strlen(out)); +} + +static void read_canned_config(char* filename) +{ + int allocated = 8; + int used = 0; + + canned_config = + (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry)); + + char line[CANNED_LINE_LENGTH]; + FILE* f = fopen(filename, "r"); + if (f == NULL) die("failed to open canned file"); + + while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) { + if (!line[0]) break; + if (used >= allocated) { + allocated *= 2; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + } + + struct fs_config_entry* cc = canned_config + used; + + if (isspace(line[0])) { + cc->name = strdup(""); + cc->uid = atoi(strtok(line, " \n")); + } else { + cc->name = strdup(strtok(line, " \n")); + cc->uid = atoi(strtok(NULL, " \n")); + } + cc->gid = atoi(strtok(NULL, " \n")); + cc->mode = strtol(strtok(NULL, " \n"), NULL, 8); + ++used; + } + if (used >= allocated) { + ++allocated; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + } + canned_config[used].name = NULL; + + fclose(f); +} + + +int main(int argc, char *argv[]) +{ + argc--; + argv++; + + if (argc > 1 && strcmp(argv[0], "-d") == 0) { + target_out_path = argv[1]; + argc -= 2; + argv += 2; + } + + if (argc > 1 && strcmp(argv[0], "-f") == 0) { + read_canned_config(argv[1]); + argc -= 2; + argv += 2; + } + + if(argc == 0) die("no directories to process?!"); + + while(argc-- > 0){ + char *x = strchr(*argv, '='); + if(x != 0) { + *x++ = 0; + } else { + x = ""; + } + + archive(*argv, x); + + argv++; + } + + _eject_trailer(); + + return 0; +} diff --git a/src/mkbootfs/headers/private/android_filesystem_capability.h b/src/mkbootfs/headers/private/android_filesystem_capability.h new file mode 100644 index 0000000..b92d3db --- /dev/null +++ b/src/mkbootfs/headers/private/android_filesystem_capability.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Taken from linux/capability.h, with minor modifications + */ + +#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H +#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H + +#include + +#define __user +#define __u32 uint32_t +#define __le32 uint32_t + +#define _LINUX_CAPABILITY_VERSION_1 0x19980330 +#define _LINUX_CAPABILITY_U32S_1 1 +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 +#define _LINUX_CAPABILITY_U32S_2 2 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 + +typedef struct __user_cap_header_struct { + __u32 version; + int pid; +} __user *cap_user_header_t; + +typedef struct __user_cap_data_struct { + __u32 effective; + __u32 permitted; + __u32 inheritable; +} __user *cap_user_data_t; + +#define VFS_CAP_REVISION_MASK 0xFF000000 +#define VFS_CAP_REVISION_SHIFT 24 +#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK +#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 +#define VFS_CAP_REVISION_1 0x01000000 +#define VFS_CAP_U32_1 1 +#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1)) +#define VFS_CAP_REVISION_2 0x02000000 +#define VFS_CAP_U32_2 2 +#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2)) +#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2 +#define VFS_CAP_U32 VFS_CAP_U32_2 +#define VFS_CAP_REVISION VFS_CAP_REVISION_2 + +struct vfs_cap_data { + __le32 magic_etc; + struct { + __le32 permitted; + __le32 inheritable; + } data[VFS_CAP_U32]; +}; + +#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1 +#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1 +#define CAP_CHOWN 0 +#define CAP_DAC_OVERRIDE 1 +#define CAP_DAC_READ_SEARCH 2 +#define CAP_FOWNER 3 +#define CAP_FSETID 4 +#define CAP_KILL 5 +#define CAP_SETGID 6 +#define CAP_SETUID 7 +#define CAP_SETPCAP 8 +#define CAP_LINUX_IMMUTABLE 9 +#define CAP_NET_BIND_SERVICE 10 +#define CAP_NET_BROADCAST 11 +#define CAP_NET_ADMIN 12 +#define CAP_NET_RAW 13 +#define CAP_IPC_LOCK 14 +#define CAP_IPC_OWNER 15 +#define CAP_SYS_MODULE 16 +#define CAP_SYS_RAWIO 17 +#define CAP_SYS_CHROOT 18 +#define CAP_SYS_PTRACE 19 +#define CAP_SYS_PACCT 20 +#define CAP_SYS_ADMIN 21 +#define CAP_SYS_BOOT 22 +#define CAP_SYS_NICE 23 +#define CAP_SYS_RESOURCE 24 +#define CAP_SYS_TIME 25 +#define CAP_SYS_TTY_CONFIG 26 +#define CAP_MKNOD 27 +#define CAP_LEASE 28 +#define CAP_AUDIT_WRITE 29 +#define CAP_AUDIT_CONTROL 30 +#define CAP_SETFCAP 31 +#define CAP_MAC_OVERRIDE 32 +#define CAP_MAC_ADMIN 33 +#define CAP_SYSLOG 34 +#define CAP_WAKE_ALARM 35 +#define CAP_BLOCK_SUSPEND 36 +#define CAP_AUDIT_READ 37 +#define CAP_LAST_CAP CAP_AUDIT_READ +#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) +#define CAP_TO_INDEX(x) ((x) >> 5) +#define CAP_TO_MASK(x) (1 << ((x) & 31)) + +#undef __user +#undef __u32 +#undef __le32 + +#endif diff --git a/src/mkbootfs/headers/private/android_filesystem_config.h b/src/mkbootfs/headers/private/android_filesystem_config.h new file mode 100644 index 0000000..2ed27dc --- /dev/null +++ b/src/mkbootfs/headers/private/android_filesystem_config.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007 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. + */ + +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#ifndef _ANDROID_FILESYSTEM_CONFIG_H_ +#define _ANDROID_FILESYSTEM_CONFIG_H_ + +#include +#include +#include + +#ifdef HAVE_ANDROID_OS +#include +#else +#include "android_filesystem_capability.h" +#endif + +/* This is the master Users and Groups config for the platform. + * DO NOT EVER RENUMBER + */ + +#define AID_ROOT 0 /* traditional unix root user */ + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_RADIO 1001 /* telephony subsystem, RIL */ +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ +#define AID_GRAPHICS 1003 /* graphics devices */ +#define AID_INPUT 1004 /* input devices */ +#define AID_AUDIO 1005 /* audio devices */ +#define AID_CAMERA 1006 /* camera devices */ +#define AID_LOG 1007 /* log devices */ +#define AID_COMPASS 1008 /* compass device */ +#define AID_MOUNT 1009 /* mountd socket */ +#define AID_WIFI 1010 /* wifi subsystem */ +#define AID_ADB 1011 /* android debug bridge (adbd) */ +#define AID_INSTALL 1012 /* group for installing packages */ +#define AID_MEDIA 1013 /* mediaserver process */ +#define AID_DHCP 1014 /* dhcp client */ +#define AID_SDCARD_RW 1015 /* external storage write access */ +#define AID_VPN 1016 /* vpn system */ +#define AID_KEYSTORE 1017 /* keystore subsystem */ +#define AID_USB 1018 /* USB devices */ +#define AID_DRM 1019 /* DRM server */ +#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ +#define AID_GPS 1021 /* GPS daemon */ +#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ +#define AID_MTP 1024 /* MTP USB driver access */ +#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ +#define AID_DRMRPC 1026 /* group for drm rpc */ +#define AID_NFC 1027 /* nfc subsystem */ +#define AID_SDCARD_R 1028 /* external storage read access */ +#define AID_CLAT 1029 /* clat part of nat464 */ +#define AID_LOOP_RADIO 1030 /* loop radio devices */ +#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ +#define AID_PACKAGE_INFO 1032 /* access to installed package details */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_LOGD 1036 /* log daemon */ +#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */ + +#define AID_SHELL 2000 /* adb and debug shell user */ +#define AID_CACHE 2001 /* cache access */ +#define AID_DIAG 2002 /* access to diagnostic resources */ + +/* The range 2900-2999 is reserved for OEM, and must never be + * used here */ +#define AID_OEM_RESERVED_START 2900 +#define AID_OEM_RESERVED_END 2999 + +/* The 3000 series are intended for use as supplemental group id's only. + * They indicate special Android capabilities that the kernel is aware of. */ +#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ +#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ +#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ +#define AID_NET_RAW 3004 /* can create raw INET sockets */ +#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ +#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ + +#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */ +#define AID_MISC 9998 /* access to misc storage */ +#define AID_NOBODY 9999 + +#define AID_APP 10000 /* first app user */ + +#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ +#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ + +#define AID_USER 100000 /* offset for uid ranges for each user */ + +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ +#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */ + +#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) +/* + * Used in: + * bionic/libc/bionic/stubs.cpp + * external/libselinux/src/android.c + * system/core/logd/LogStatistics.cpp + * system/core/init/ueventd.cpp + * system/core/init/util.cpp + */ +struct android_id_info { + const char *name; + unsigned aid; +}; + +static const struct android_id_info android_ids[] = { + { "root", AID_ROOT, }, + + { "system", AID_SYSTEM, }, + + { "radio", AID_RADIO, }, + { "bluetooth", AID_BLUETOOTH, }, + { "graphics", AID_GRAPHICS, }, + { "input", AID_INPUT, }, + { "audio", AID_AUDIO, }, + { "camera", AID_CAMERA, }, + { "log", AID_LOG, }, + { "compass", AID_COMPASS, }, + { "mount", AID_MOUNT, }, + { "wifi", AID_WIFI, }, + { "adb", AID_ADB, }, + { "install", AID_INSTALL, }, + { "media", AID_MEDIA, }, + { "dhcp", AID_DHCP, }, + { "sdcard_rw", AID_SDCARD_RW, }, + { "vpn", AID_VPN, }, + { "keystore", AID_KEYSTORE, }, + { "usb", AID_USB, }, + { "drm", AID_DRM, }, + { "mdnsr", AID_MDNSR, }, + { "gps", AID_GPS, }, + // AID_UNUSED1 + { "media_rw", AID_MEDIA_RW, }, + { "mtp", AID_MTP, }, + // AID_UNUSED2 + { "drmrpc", AID_DRMRPC, }, + { "nfc", AID_NFC, }, + { "sdcard_r", AID_SDCARD_R, }, + { "clat", AID_CLAT, }, + { "loop_radio", AID_LOOP_RADIO, }, + { "mediadrm", AID_MEDIA_DRM, }, + { "package_info", AID_PACKAGE_INFO, }, + { "sdcard_pics", AID_SDCARD_PICS, }, + { "sdcard_av", AID_SDCARD_AV, }, + { "sdcard_all", AID_SDCARD_ALL, }, + { "logd", AID_LOGD, }, + { "shared_relro", AID_SHARED_RELRO, }, + + { "shell", AID_SHELL, }, + { "cache", AID_CACHE, }, + { "diag", AID_DIAG, }, + + { "net_bt_admin", AID_NET_BT_ADMIN, }, + { "net_bt", AID_NET_BT, }, + { "inet", AID_INET, }, + { "net_raw", AID_NET_RAW, }, + { "net_admin", AID_NET_ADMIN, }, + { "net_bw_stats", AID_NET_BW_STATS, }, + { "net_bw_acct", AID_NET_BW_ACCT, }, + { "net_bt_stack", AID_NET_BT_STACK, }, + + { "everybody", AID_EVERYBODY, }, + { "misc", AID_MISC, }, + { "nobody", AID_NOBODY, }, +}; + +#define android_id_count \ + (sizeof(android_ids) / sizeof(android_ids[0])) + +struct fs_path_config { + unsigned mode; + unsigned uid; + unsigned gid; + uint64_t capabilities; + const char *prefix; +}; + +/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */ + +__BEGIN_DECLS + +/* + * Used in: + * build/tools/fs_config/fs_config.c + * build/tools/fs_get_stats/fs_get_stats.c + * system/extras/ext4_utils/make_ext4fs_main.c + * external/squashfs-tools/squashfs-tools/android.c + * system/core/cpio/mkbootfs.c + * system/core/adb/file_sync_service.cpp + * system/extras/ext4_utils/canned_fs_config.c + */ +void fs_config(const char *path, int dir, const char *target_out_path, + unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); + +ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc); + +__END_DECLS + +#endif +#endif