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