Home | History | Annotate | Download | only in fs_config
      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