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