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 
die(const char * why,...)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 
fix_stat(const char * path,struct stat * s)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 
_eject(struct stat * s,char * out,int olen,char * data,unsigned datasize)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 
_eject_trailer()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 
compare(const void * a,const void * b)155 static int compare(const void* a, const void* b) {
156   return strcmp(*(const char**)a, *(const char**)b);
157 }
158 
_archive_dir(char * in,char * out,int ilen,int olen)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 
_archive(char * in,char * out,int ilen,int olen)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 
archive(const char * start,const char * prefix)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 
read_canned_config(char * filename)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 
main(int argc,char * argv[])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