Home | History | Annotate | Download | only in cpio
      1 
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <unistd.h>
      5 #include <string.h>
      6 #include <ctype.h>
      7 
      8 #include <sys/types.h>
      9 #include <sys/stat.h>
     10 #include <dirent.h>
     11 
     12 #include <stdarg.h>
     13 #include <fcntl.h>
     14 
     15 #include <private/android_filesystem_config.h>
     16 
     17 /* NOTES
     18 **
     19 ** - see buffer-format.txt from the linux kernel docs for
     20 **   an explanation of this file format
     21 ** - dotfiles are ignored
     22 ** - directories named 'root' are ignored
     23 ** - device notes, pipes, etc are not supported (error)
     24 */
     25 
     26 void die(const char *why, ...)
     27 {
     28     va_list ap;
     29 
     30     va_start(ap, why);
     31     fprintf(stderr,"error: ");
     32     vfprintf(stderr, why, ap);
     33     fprintf(stderr,"\n");
     34     va_end(ap);
     35     exit(1);
     36 }
     37 
     38 struct fs_config_entry {
     39     char* name;
     40     int uid, gid, mode;
     41 };
     42 
     43 static struct fs_config_entry* canned_config = NULL;
     44 static char *target_out_path = NULL;
     45 
     46 /* Each line in the canned file should be a path plus three ints (uid,
     47  * gid, mode). */
     48 #ifdef PATH_MAX
     49 #define CANNED_LINE_LENGTH  (PATH_MAX+100)
     50 #else
     51 #define CANNED_LINE_LENGTH  (1024)
     52 #endif
     53 
     54 #define TRAILER "TRAILER!!!"
     55 
     56 static int verbose = 0;
     57 static int total_size = 0;
     58 
     59 static void fix_stat(const char *path, struct stat *s)
     60 {
     61     uint64_t capabilities;
     62     if (canned_config) {
     63         // Use the list of file uid/gid/modes loaded from the file
     64         // given with -f.
     65 
     66         struct fs_config_entry* empty_path_config = NULL;
     67         struct fs_config_entry* p;
     68         for (p = canned_config; p->name; ++p) {
     69             if (!p->name[0]) {
     70                 empty_path_config = p;
     71             }
     72             if (strcmp(p->name, path) == 0) {
     73                 s->st_uid = p->uid;
     74                 s->st_gid = p->gid;
     75                 s->st_mode = p->mode | (s->st_mode & ~07777);
     76                 return;
     77             }
     78         }
     79         s->st_uid = empty_path_config->uid;
     80         s->st_gid = empty_path_config->gid;
     81         s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);
     82     } else {
     83         // Use the compiled-in fs_config() function.
     84         unsigned st_mode = s->st_mode;
     85         int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0;
     86         fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);
     87         s->st_mode = (typeof(s->st_mode)) st_mode;
     88     }
     89 }
     90 
     91 static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
     92 {
     93     // Nothing is special about this value, just picked something in the
     94     // approximate range that was being used already, and avoiding small
     95     // values which may be special.
     96     static unsigned next_inode = 300000;
     97 
     98     while(total_size & 3) {
     99         total_size++;
    100         putchar(0);
    101     }
    102 
    103     fix_stat(out, s);
    104 //    fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
    105 
    106     printf("%06x%08x%08x%08x%08x%08x%08x"
    107            "%08x%08x%08x%08x%08x%08x%08x%s%c",
    108            0x070701,
    109            next_inode++,  //  s.st_ino,
    110            s->st_mode,
    111            0, // s.st_uid,
    112            0, // s.st_gid,
    113            1, // s.st_nlink,
    114            0, // s.st_mtime,
    115            datasize,
    116            0, // volmajor
    117            0, // volminor
    118            0, // devmajor
    119            0, // devminor,
    120            olen + 1,
    121            0,
    122            out,
    123            0
    124            );
    125 
    126     total_size += 6 + 8*13 + olen + 1;
    127 
    128     if(strlen(out) != (unsigned int)olen) die("ACK!");
    129 
    130     while(total_size & 3) {
    131         total_size++;
    132         putchar(0);
    133     }
    134 
    135     if(datasize) {
    136         fwrite(data, datasize, 1, stdout);
    137         total_size += datasize;
    138     }
    139 }
    140 
    141 static void _eject_trailer()
    142 {
    143     struct stat s;
    144     memset(&s, 0, sizeof(s));
    145     _eject(&s, TRAILER, 10, 0, 0);
    146 
    147     while(total_size & 0xff) {
    148         total_size++;
    149         putchar(0);
    150     }
    151 }
    152 
    153 static void _archive(char *in, char *out, int ilen, int olen);
    154 
    155 static int compare(const void* a, const void* b) {
    156   return strcmp(*(const char**)a, *(const char**)b);
    157 }
    158 
    159 static void _archive_dir(char *in, char *out, int ilen, int olen)
    160 {
    161     int i, t;
    162     DIR *d;
    163     struct dirent *de;
    164 
    165     if(verbose) {
    166         fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
    167                 in, out, ilen, olen);
    168     }
    169 
    170     d = opendir(in);
    171     if(d == 0) die("cannot open directory '%s'", in);
    172 
    173     int size = 32;
    174     int entries = 0;
    175     char** names = malloc(size * sizeof(char*));
    176     if (names == NULL) {
    177       fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
    178       exit(1);
    179     }
    180 
    181     while((de = readdir(d)) != 0){
    182             /* xxx: feature? maybe some dotfiles are okay */
    183         if(de->d_name[0] == '.') continue;
    184 
    185             /* xxx: hack. use a real exclude list */
    186         if(!strcmp(de->d_name, "root")) continue;
    187 
    188         if (entries >= size) {
    189           size *= 2;
    190           names = realloc(names, size * sizeof(char*));
    191           if (names == NULL) {
    192             fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
    193                     size);
    194             exit(1);
    195           }
    196         }
    197         names[entries] = strdup(de->d_name);
    198         if (names[entries] == NULL) {
    199           fprintf(stderr, "failed to strdup name \"%s\"\n",
    200                   de->d_name);
    201           exit(1);
    202         }
    203         ++entries;
    204     }
    205 
    206     qsort(names, entries, sizeof(char*), compare);
    207 
    208     for (i = 0; i < entries; ++i) {
    209         t = strlen(names[i]);
    210         in[ilen] = '/';
    211         memcpy(in + ilen + 1, names[i], t + 1);
    212 
    213         if(olen > 0) {
    214             out[olen] = '/';
    215             memcpy(out + olen + 1, names[i], t + 1);
    216             _archive(in, out, ilen + t + 1, olen + t + 1);
    217         } else {
    218             memcpy(out, names[i], t + 1);
    219             _archive(in, out, ilen + t + 1, t);
    220         }
    221 
    222         in[ilen] = 0;
    223         out[olen] = 0;
    224 
    225         free(names[i]);
    226     }
    227     free(names);
    228 
    229     closedir(d);
    230 }
    231 
    232 static void _archive(char *in, char *out, int ilen, int olen)
    233 {
    234     struct stat s;
    235 
    236     if(verbose) {
    237         fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
    238                 in, out, ilen, olen);
    239     }
    240 
    241     if(lstat(in, &s)) die("could not stat '%s'\n", in);
    242 
    243     if(S_ISREG(s.st_mode)){
    244         char *tmp;
    245         int fd;
    246 
    247         fd = open(in, O_RDONLY);
    248         if(fd < 0) die("cannot open '%s' for read", in);
    249 
    250         tmp = (char*) malloc(s.st_size);
    251         if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
    252 
    253         if(read(fd, tmp, s.st_size) != s.st_size) {
    254             die("cannot read %d bytes", s.st_size);
    255         }
    256 
    257         _eject(&s, out, olen, tmp, s.st_size);
    258 
    259         free(tmp);
    260         close(fd);
    261     } else if(S_ISDIR(s.st_mode)) {
    262         _eject(&s, out, olen, 0, 0);
    263         _archive_dir(in, out, ilen, olen);
    264     } else if(S_ISLNK(s.st_mode)) {
    265         char buf[1024];
    266         int size;
    267         size = readlink(in, buf, 1024);
    268         if(size < 0) die("cannot read symlink '%s'", in);
    269         _eject(&s, out, olen, buf, size);
    270     } else {
    271         die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
    272     }
    273 }
    274 
    275 void archive(const char *start, const char *prefix)
    276 {
    277     char in[8192];
    278     char out[8192];
    279 
    280     strcpy(in, start);
    281     strcpy(out, prefix);
    282 
    283     _archive_dir(in, out, strlen(in), strlen(out));
    284 }
    285 
    286 static void read_canned_config(char* filename)
    287 {
    288     int allocated = 8;
    289     int used = 0;
    290 
    291     canned_config =
    292         (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
    293 
    294     char line[CANNED_LINE_LENGTH];
    295     FILE* f = fopen(filename, "r");
    296     if (f == NULL) die("failed to open canned file");
    297 
    298     while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
    299         if (!line[0]) break;
    300         if (used >= allocated) {
    301             allocated *= 2;
    302             canned_config = (struct fs_config_entry*)realloc(
    303                 canned_config, allocated * sizeof(struct fs_config_entry));
    304             if (canned_config == NULL) die("failed to reallocate memory");
    305         }
    306 
    307         struct fs_config_entry* cc = canned_config + used;
    308 
    309         if (isspace(line[0])) {
    310             cc->name = strdup("");
    311             cc->uid = atoi(strtok(line, " \n"));
    312         } else {
    313             cc->name = strdup(strtok(line, " \n"));
    314             cc->uid = atoi(strtok(NULL, " \n"));
    315         }
    316         cc->gid = atoi(strtok(NULL, " \n"));
    317         cc->mode = strtol(strtok(NULL, " \n"), NULL, 8);
    318         ++used;
    319     }
    320     if (used >= allocated) {
    321         ++allocated;
    322         canned_config = (struct fs_config_entry*)realloc(
    323             canned_config, allocated * sizeof(struct fs_config_entry));
    324         if (canned_config == NULL) die("failed to reallocate memory");
    325     }
    326     canned_config[used].name = NULL;
    327 
    328     fclose(f);
    329 }
    330 
    331 
    332 int main(int argc, char *argv[])
    333 {
    334     argc--;
    335     argv++;
    336 
    337     if (argc > 1 && strcmp(argv[0], "-d") == 0) {
    338         target_out_path = argv[1];
    339         argc -= 2;
    340         argv += 2;
    341     }
    342 
    343     if (argc > 1 && strcmp(argv[0], "-f") == 0) {
    344         read_canned_config(argv[1]);
    345         argc -= 2;
    346         argv += 2;
    347     }
    348 
    349     if(argc == 0) die("no directories to process?!");
    350 
    351     while(argc-- > 0){
    352         char *x = strchr(*argv, '=');
    353         if(x != 0) {
    354             *x++ = 0;
    355         } else {
    356             x = "";
    357         }
    358 
    359         archive(*argv, x);
    360 
    361         argv++;
    362     }
    363 
    364     _eject_trailer();
    365 
    366     return 0;
    367 }
    368