nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | static int verbose = 0; |
||
55 | static int total_size = 0; |
||
56 | |||
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 | |||
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 | |||
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 | |||
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 | |||
153 | static int compare(const void* a, const void* b) { |
||
154 | return strcmp(*(const char**)a, *(const char**)b); |
||
155 | } |
||
156 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | } |