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