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