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