1 #include <linux/init.h> 2 #include <linux/fs.h> 3 #include <linux/slab.h> 4 #include <linux/types.h> 5 #include <linux/fcntl.h> 6 #include <linux/delay.h> 7 #include <linux/string.h> 8 #include <linux/syscalls.h> 9 10 static __initdata char *message; 11 static void __init error(char *x) 12 { 13 if (!message) 14 message = x; 15 } 16 17 static void __init *malloc(size_t size) 18 { 19 return kmalloc(size, GFP_KERNEL); 20 } 21 22 static void __init free(void *where) 23 { 24 kfree(where); 25 } 26 27 /* link hash */ 28 29 static __initdata struct hash { 30 int ino, minor, major; 31 struct hash *next; 32 char *name; 33 } *head[32]; 34 35 static inline int hash(int major, int minor, int ino) 36 { 37 unsigned long tmp = ino + minor + (major << 3); 38 tmp += tmp >> 5; 39 return tmp & 31; 40 } 41 42 static char __init *find_link(int major, int minor, int ino, char *name) 43 { 44 struct hash **p, *q; 45 for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { 46 if ((*p)->ino != ino) 47 continue; 48 if ((*p)->minor != minor) 49 continue; 50 if ((*p)->major != major) 51 continue; 52 return (*p)->name; 53 } 54 q = (struct hash *)malloc(sizeof(struct hash)); 55 if (!q) 56 panic("can't allocate link hash entry"); 57 q->ino = ino; 58 q->minor = minor; 59 q->major = major; 60 q->name = name; 61 q->next = NULL; 62 *p = q; 63 return NULL; 64 } 65 66 static void __init free_hash(void) 67 { 68 struct hash **p, *q; 69 for (p = head; p < head + 32; p++) { 70 while (*p) { 71 q = *p; 72 *p = q->next; 73 free(q); 74 } 75 } 76 } 77 78 /* cpio header parsing */ 79 80 static __initdata unsigned long ino, major, minor, nlink; 81 static __initdata mode_t mode; 82 static __initdata unsigned long body_len, name_len; 83 static __initdata uid_t uid; 84 static __initdata gid_t gid; 85 static __initdata unsigned rdev; 86 87 static void __init parse_header(char *s) 88 { 89 unsigned long parsed[12]; 90 char buf[9]; 91 int i; 92 93 buf[8] = '\0'; 94 for (i = 0, s += 6; i < 12; i++, s += 8) { 95 memcpy(buf, s, 8); 96 parsed[i] = simple_strtoul(buf, NULL, 16); 97 } 98 ino = parsed[0]; 99 mode = parsed[1]; 100 uid = parsed[2]; 101 gid = parsed[3]; 102 nlink = parsed[4]; 103 body_len = parsed[6]; 104 major = parsed[7]; 105 minor = parsed[8]; 106 rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); 107 name_len = parsed[11]; 108 } 109 110 /* FSM */ 111 112 static __initdata enum state { 113 Start, 114 Collect, 115 GotHeader, 116 SkipIt, 117 GotName, 118 CopyFile, 119 GotSymlink, 120 Reset 121 } state, next_state; 122 123 static __initdata char *victim; 124 static __initdata unsigned count; 125 static __initdata loff_t this_header, next_header; 126 127 static __initdata int dry_run; 128 129 static inline void eat(unsigned n) 130 { 131 victim += n; 132 this_header += n; 133 count -= n; 134 } 135 136 #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) 137 138 static __initdata char *collected; 139 static __initdata int remains; 140 static __initdata char *collect; 141 142 static void __init read_into(char *buf, unsigned size, enum state next) 143 { 144 if (count >= size) { 145 collected = victim; 146 eat(size); 147 state = next; 148 } else { 149 collect = collected = buf; 150 remains = size; 151 next_state = next; 152 state = Collect; 153 } 154 } 155 156 static __initdata char *header_buf, *symlink_buf, *name_buf; 157 158 static int __init do_start(void) 159 { 160 read_into(header_buf, 110, GotHeader); 161 return 0; 162 } 163 164 static int __init do_collect(void) 165 { 166 unsigned n = remains; 167 if (count < n) 168 n = count; 169 memcpy(collect, victim, n); 170 eat(n); 171 collect += n; 172 if ((remains -= n) != 0) 173 return 1; 174 state = next_state; 175 return 0; 176 } 177 178 static int __init do_header(void) 179 { 180 if (memcmp(collected, "070701", 6)) { 181 error("no cpio magic"); 182 return 1; 183 } 184 parse_header(collected); 185 next_header = this_header + N_ALIGN(name_len) + body_len; 186 next_header = (next_header + 3) & ~3; 187 if (dry_run) { 188 read_into(name_buf, N_ALIGN(name_len), GotName); 189 return 0; 190 } 191 state = SkipIt; 192 if (name_len <= 0 || name_len > PATH_MAX) 193 return 0; 194 if (S_ISLNK(mode)) { 195 if (body_len > PATH_MAX) 196 return 0; 197 collect = collected = symlink_buf; 198 remains = N_ALIGN(name_len) + body_len; 199 next_state = GotSymlink; 200 state = Collect; 201 return 0; 202 } 203 if (S_ISREG(mode) || !body_len) 204 read_into(name_buf, N_ALIGN(name_len), GotName); 205 return 0; 206 } 207 208 static int __init do_skip(void) 209 { 210 if (this_header + count < next_header) { 211 eat(count); 212 return 1; 213 } else { 214 eat(next_header - this_header); 215 state = next_state; 216 return 0; 217 } 218 } 219 220 static int __init do_reset(void) 221 { 222 while(count && *victim == '\0') 223 eat(1); 224 if (count && (this_header & 3)) 225 error("broken padding"); 226 return 1; 227 } 228 229 static int __init maybe_link(void) 230 { 231 if (nlink >= 2) { 232 char *old = find_link(major, minor, ino, collected); 233 if (old) 234 return (sys_link(old, collected) < 0) ? -1 : 1; 235 } 236 return 0; 237 } 238 239 static __initdata int wfd; 240 241 static int __init do_name(void) 242 { 243 state = SkipIt; 244 next_state = Reset; 245 if (strcmp(collected, "TRAILER!!!") == 0) { 246 free_hash(); 247 return 0; 248 } 249 if (dry_run) 250 return 0; 251 if (S_ISREG(mode)) { 252 if (maybe_link() >= 0) { 253 wfd = sys_open(collected, O_WRONLY|O_CREAT, mode); 254 if (wfd >= 0) { 255 sys_fchown(wfd, uid, gid); 256 sys_fchmod(wfd, mode); 257 state = CopyFile; 258 } 259 } 260 } else if (S_ISDIR(mode)) { 261 sys_mkdir(collected, mode); 262 sys_chown(collected, uid, gid); 263 sys_chmod(collected, mode); 264 } else if (S_ISBLK(mode) || S_ISCHR(mode) || 265 S_ISFIFO(mode) || S_ISSOCK(mode)) { 266 if (maybe_link() == 0) { 267 sys_mknod(collected, mode, rdev); 268 sys_chown(collected, uid, gid); 269 sys_chmod(collected, mode); 270 } 271 } 272 return 0; 273 } 274 275 static int __init do_copy(void) 276 { 277 if (count >= body_len) { 278 sys_write(wfd, victim, body_len); 279 sys_close(wfd); 280 eat(body_len); 281 state = SkipIt; 282 return 0; 283 } else { 284 sys_write(wfd, victim, count); 285 body_len -= count; 286 eat(count); 287 return 1; 288 } 289 } 290 291 static int __init do_symlink(void) 292 { 293 collected[N_ALIGN(name_len) + body_len] = '\0'; 294 sys_symlink(collected + N_ALIGN(name_len), collected); 295 sys_lchown(collected, uid, gid); 296 state = SkipIt; 297 next_state = Reset; 298 return 0; 299 } 300 301 static __initdata int (*actions[])(void) = { 302 [Start] = do_start, 303 [Collect] = do_collect, 304 [GotHeader] = do_header, 305 [SkipIt] = do_skip, 306 [GotName] = do_name, 307 [CopyFile] = do_copy, 308 [GotSymlink] = do_symlink, 309 [Reset] = do_reset, 310 }; 311 312 static int __init write_buffer(char *buf, unsigned len) 313 { 314 count = len; 315 victim = buf; 316 317 while (!actions[state]()) 318 ; 319 return len - count; 320 } 321 322 static void __init flush_buffer(char *buf, unsigned len) 323 { 324 int written; 325 if (message) 326 return; 327 while ((written = write_buffer(buf, len)) < len && !message) { 328 char c = buf[written]; 329 if (c == '0') { 330 buf += written; 331 len -= written; 332 state = Start; 333 } else if (c == 0) { 334 buf += written; 335 len -= written; 336 state = Reset; 337 } else 338 error("junk in compressed archive"); 339 } 340 } 341 342 /* 343 * gzip declarations 344 */ 345 346 #define OF(args) args 347 348 #ifndef memzero 349 #define memzero(s, n) memset ((s), 0, (n)) 350 #endif 351 352 typedef unsigned char uch; 353 typedef unsigned short ush; 354 typedef unsigned long ulg; 355 356 #define WSIZE 0x8000 /* window size--must be a power of two, and */ 357 /* at least 32K for zip's deflate method */ 358 359 static uch *inbuf; 360 static uch *window; 361 362 static unsigned insize; /* valid bytes in inbuf */ 363 static unsigned inptr; /* index of next byte to be processed in inbuf */ 364 static unsigned outcnt; /* bytes in output buffer */ 365 static long bytes_out; 366 367 #define get_byte() (inptr < insize ? inbuf[inptr++] : -1) 368 369 /* Diagnostic functions (stubbed out) */ 370 #define Assert(cond,msg) 371 #define Trace(x) 372 #define Tracev(x) 373 #define Tracevv(x) 374 #define Tracec(c,x) 375 #define Tracecv(c,x) 376 377 #define STATIC static 378 #define INIT __init 379 380 static void __init flush_window(void); 381 static void __init error(char *m); 382 static void __init gzip_mark(void **); 383 static void __init gzip_release(void **); 384 385 #include "../lib/inflate.c" 386 387 static void __init gzip_mark(void **ptr) 388 { 389 } 390 391 static void __init gzip_release(void **ptr) 392 { 393 } 394 395 /* =========================================================================== 396 * Write the output window window[0..outcnt-1] and update crc and bytes_out. 397 * (Used for the decompressed data only.) 398 */ 399 static void __init flush_window(void) 400 { 401 ulg c = crc; /* temporary variable */ 402 unsigned n; 403 uch *in, ch; 404 405 flush_buffer(window, outcnt); 406 in = window; 407 for (n = 0; n < outcnt; n++) { 408 ch = *in++; 409 c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); 410 } 411 crc = c; 412 bytes_out += (ulg)outcnt; 413 outcnt = 0; 414 } 415 416 static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only) 417 { 418 int written; 419 dry_run = check_only; 420 header_buf = malloc(110); 421 symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1); 422 name_buf = malloc(N_ALIGN(PATH_MAX)); 423 window = malloc(WSIZE); 424 if (!window || !header_buf || !symlink_buf || !name_buf) 425 panic("can't allocate buffers"); 426 state = Start; 427 this_header = 0; 428 message = NULL; 429 while (!message && len) { 430 loff_t saved_offset = this_header; 431 if (*buf == '0' && !(this_header & 3)) { 432 state = Start; 433 written = write_buffer(buf, len); 434 buf += written; 435 len -= written; 436 continue; 437 } 438 if (!*buf) { 439 buf++; 440 len--; 441 this_header++; 442 continue; 443 } 444 this_header = 0; 445 insize = len; 446 inbuf = buf; 447 inptr = 0; 448 outcnt = 0; /* bytes in output buffer */ 449 bytes_out = 0; 450 crc = (ulg)0xffffffffL; /* shift register contents */ 451 makecrc(); 452 gunzip(); 453 if (state != Reset) 454 error("junk in gzipped archive"); 455 this_header = saved_offset + inptr; 456 buf += inptr; 457 len -= inptr; 458 } 459 free(window); 460 free(name_buf); 461 free(symlink_buf); 462 free(header_buf); 463 return message; 464 } 465 466 extern char __initramfs_start[], __initramfs_end[]; 467 #ifdef CONFIG_BLK_DEV_INITRD 468 #include <linux/initrd.h> 469 470 static void __init free_initrd(void) 471 { 472 free_initrd_mem(initrd_start, initrd_end); 473 initrd_start = 0; 474 initrd_end = 0; 475 } 476 477 #endif 478 479 void __init populate_rootfs(void) 480 { 481 char *err = unpack_to_rootfs(__initramfs_start, 482 __initramfs_end - __initramfs_start, 0); 483 if (err) 484 panic(err); 485 #ifdef CONFIG_BLK_DEV_INITRD 486 if (initrd_start) { 487 int fd; 488 printk(KERN_INFO "checking if image is initramfs..."); 489 err = unpack_to_rootfs((char *)initrd_start, 490 initrd_end - initrd_start, 1); 491 if (!err) { 492 printk(" it is\n"); 493 unpack_to_rootfs((char *)initrd_start, 494 initrd_end - initrd_start, 0); 495 free_initrd(); 496 return; 497 } 498 printk("it isn't (%s); looks like an initrd\n", err); 499 fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700); 500 if (fd >= 0) { 501 sys_write(fd, (char *)initrd_start, 502 initrd_end - initrd_start); 503 sys_close(fd); 504 free_initrd(); 505 } 506 } 507 #endif 508 } 509