1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <ctype.h> 18 #include <stdbool.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include <private/android_filesystem_config.h> 25 26 /* 27 * This program expects android_device_dirs and android_device_files 28 * to be defined in the supplied android_filesystem_config.h file in 29 * the device/<vendor>/<product> $(TARGET_DEVICE_DIR). Then generates 30 * the binary format used in the /system/etc/fs_config_dirs and 31 * the /system/etc/fs_config_files to be used by the runtimes. 32 */ 33 #ifdef ANDROID_FILESYSTEM_CONFIG 34 #include ANDROID_FILESYSTEM_CONFIG 35 #else 36 #include "android_filesystem_config.h" 37 #endif 38 39 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS 40 static const struct fs_path_config android_device_dirs[] = { }; 41 #endif 42 43 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES 44 static const struct fs_path_config android_device_files[] = { 45 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS 46 {0000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs"}, 47 {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_dirs"}, 48 {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_dirs"}, 49 {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_dirs"}, 50 #endif 51 {0000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files"}, 52 {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_files"}, 53 {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_files"}, 54 {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_files"}, 55 }; 56 #endif 57 58 static void usage() { 59 fprintf(stderr, 60 "Generate binary content for fs_config_dirs (-D) and fs_config_files (-F)\n" 61 "from device-specific android_filesystem_config.h override. Filter based\n" 62 "on a comma separated partition list (-P) whitelist or prefixed by a\n" 63 "minus blacklist. Partitions are identified as path references to\n" 64 "<partition>/ or system/<partition>/\n\n" 65 "Usage: fs_config_generate -D|-F [-P list] [-o output-file]\n"); 66 } 67 68 /* If tool switches to C++, use android-base/macros.h array_size() */ 69 #ifndef ARRAY_SIZE /* popular macro */ 70 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 71 #endif 72 73 int main(int argc, char** argv) { 74 const struct fs_path_config* pc; 75 const struct fs_path_config* end; 76 bool dir = false, file = false; 77 const char* partitions = NULL; 78 FILE* fp = stdout; 79 int opt; 80 static const char optstring[] = "DFP:ho:"; 81 82 while ((opt = getopt(argc, argv, optstring)) != -1) { 83 switch (opt) { 84 case 'D': 85 if (file) { 86 fprintf(stderr, "Must specify only -D or -F\n"); 87 usage(); 88 exit(EXIT_FAILURE); 89 } 90 dir = true; 91 break; 92 case 'F': 93 if (dir) { 94 fprintf(stderr, "Must specify only -F or -D\n"); 95 usage(); 96 exit(EXIT_FAILURE); 97 } 98 file = true; 99 break; 100 case 'P': 101 if (partitions) { 102 fprintf(stderr, "Specify only one partition list\n"); 103 usage(); 104 exit(EXIT_FAILURE); 105 } 106 while (*optarg && isspace(*optarg)) ++optarg; 107 if (!optarg[0]) { 108 fprintf(stderr, "Partition list empty\n"); 109 usage(); 110 exit(EXIT_FAILURE); 111 } 112 if (!optarg[1]) { 113 fprintf(stderr, "Partition list too short \"%s\"\n", optarg); 114 usage(); 115 exit(EXIT_FAILURE); 116 } 117 if ((optarg[0] == '-') && strchr(optstring, optarg[1]) && !optarg[2]) { 118 fprintf(stderr, "Partition list is a flag \"%s\"\n", optarg); 119 usage(); 120 exit(EXIT_FAILURE); 121 } 122 partitions = optarg; 123 break; 124 case 'o': 125 if (fp != stdout) { 126 fprintf(stderr, "Specify only one output file\n"); 127 usage(); 128 exit(EXIT_FAILURE); 129 } 130 fp = fopen(optarg, "wb"); 131 if (fp == NULL) { 132 fprintf(stderr, "Can not open \"%s\"\n", optarg); 133 exit(EXIT_FAILURE); 134 } 135 break; 136 case 'h': 137 usage(); 138 exit(EXIT_SUCCESS); 139 default: 140 usage(); 141 exit(EXIT_FAILURE); 142 } 143 } 144 145 if (optind < argc) { 146 fprintf(stderr, "Unknown non-argument \"%s\"\n", argv[optind]); 147 usage(); 148 exit(EXIT_FAILURE); 149 } 150 151 if (!file && !dir) { 152 fprintf(stderr, "Must specify either -F or -D\n"); 153 usage(); 154 exit(EXIT_FAILURE); 155 } 156 157 if (dir) { 158 pc = android_device_dirs; 159 end = &android_device_dirs[ARRAY_SIZE(android_device_dirs)]; 160 } else { 161 pc = android_device_files; 162 end = &android_device_files[ARRAY_SIZE(android_device_files)]; 163 } 164 for (; (pc < end) && pc->prefix; pc++) { 165 bool submit; 166 char buffer[512]; 167 ssize_t len = fs_config_generate(buffer, sizeof(buffer), pc); 168 if (len < 0) { 169 fprintf(stderr, "Entry too large\n"); 170 exit(EXIT_FAILURE); 171 } 172 submit = true; 173 if (partitions) { 174 char* partitions_copy = strdup(partitions); 175 char* arg = partitions_copy; 176 char* sv = NULL; /* Do not leave uninitialized, NULL is known safe. */ 177 /* Deal with case all iterated partitions are blacklists with no match */ 178 bool all_blacklist_but_no_match = true; 179 submit = false; 180 181 if (!partitions_copy) { 182 fprintf(stderr, "Failed to allocate a copy of %s\n", partitions); 183 exit(EXIT_FAILURE); 184 } 185 /* iterate through (officially) comma separated list of partitions */ 186 while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) { 187 static const char system[] = "system/"; 188 size_t plen; 189 bool blacklist = false; 190 if (*arg == '-') { 191 blacklist = true; 192 ++arg; 193 } else { 194 all_blacklist_but_no_match = false; 195 } 196 plen = strlen(arg); 197 /* deal with evil callers */ 198 while (arg[plen - 1] == '/') { 199 --plen; 200 } 201 /* check if we have <partition>/ or /system/<partition>/ */ 202 if ((!strncmp(pc->prefix, arg, plen) && (pc->prefix[plen] == '/')) || 203 (!strncmp(pc->prefix, system, strlen(system)) && 204 !strncmp(pc->prefix + strlen(system), arg, plen) && 205 (pc->prefix[strlen(system) + plen] == '/'))) { 206 all_blacklist_but_no_match = false; 207 /* we have a match !!! */ 208 if (!blacklist) submit = true; 209 break; 210 } 211 arg = NULL; 212 } 213 free(partitions_copy); 214 if (all_blacklist_but_no_match) submit = true; 215 } 216 if (submit && (fwrite(buffer, 1, len, fp) != (size_t)len)) { 217 fprintf(stderr, "Write failure\n"); 218 exit(EXIT_FAILURE); 219 } 220 } 221 fclose(fp); 222 223 return 0; 224 } 225