11da177e4SLinus Torvalds #include <linux/init.h> 21da177e4SLinus Torvalds #include <linux/fs.h> 31da177e4SLinus Torvalds #include <linux/slab.h> 41da177e4SLinus Torvalds #include <linux/types.h> 51da177e4SLinus Torvalds #include <linux/fcntl.h> 61da177e4SLinus Torvalds #include <linux/delay.h> 71da177e4SLinus Torvalds #include <linux/string.h> 81da177e4SLinus Torvalds #include <linux/syscalls.h> 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds static __initdata char *message; 111da177e4SLinus Torvalds static void __init error(char *x) 121da177e4SLinus Torvalds { 131da177e4SLinus Torvalds if (!message) 141da177e4SLinus Torvalds message = x; 151da177e4SLinus Torvalds } 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds static void __init *malloc(size_t size) 181da177e4SLinus Torvalds { 191da177e4SLinus Torvalds return kmalloc(size, GFP_KERNEL); 201da177e4SLinus Torvalds } 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds static void __init free(void *where) 231da177e4SLinus Torvalds { 241da177e4SLinus Torvalds kfree(where); 251da177e4SLinus Torvalds } 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds /* link hash */ 281da177e4SLinus Torvalds 296a050da4SMark Huang #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) 306a050da4SMark Huang 311da177e4SLinus Torvalds static __initdata struct hash { 321da177e4SLinus Torvalds int ino, minor, major; 332139a7fbSH. Peter Anvin mode_t mode; 341da177e4SLinus Torvalds struct hash *next; 356a050da4SMark Huang char name[N_ALIGN(PATH_MAX)]; 361da177e4SLinus Torvalds } *head[32]; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static inline int hash(int major, int minor, int ino) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds unsigned long tmp = ino + minor + (major << 3); 411da177e4SLinus Torvalds tmp += tmp >> 5; 421da177e4SLinus Torvalds return tmp & 31; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds 452139a7fbSH. Peter Anvin static char __init *find_link(int major, int minor, int ino, 462139a7fbSH. Peter Anvin mode_t mode, char *name) 471da177e4SLinus Torvalds { 481da177e4SLinus Torvalds struct hash **p, *q; 491da177e4SLinus Torvalds for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { 501da177e4SLinus Torvalds if ((*p)->ino != ino) 511da177e4SLinus Torvalds continue; 521da177e4SLinus Torvalds if ((*p)->minor != minor) 531da177e4SLinus Torvalds continue; 541da177e4SLinus Torvalds if ((*p)->major != major) 551da177e4SLinus Torvalds continue; 562139a7fbSH. Peter Anvin if (((*p)->mode ^ mode) & S_IFMT) 572139a7fbSH. Peter Anvin continue; 581da177e4SLinus Torvalds return (*p)->name; 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds q = (struct hash *)malloc(sizeof(struct hash)); 611da177e4SLinus Torvalds if (!q) 621da177e4SLinus Torvalds panic("can't allocate link hash entry"); 631da177e4SLinus Torvalds q->major = major; 642139a7fbSH. Peter Anvin q->minor = minor; 652139a7fbSH. Peter Anvin q->ino = ino; 662139a7fbSH. Peter Anvin q->mode = mode; 676a050da4SMark Huang strcpy(q->name, name); 681da177e4SLinus Torvalds q->next = NULL; 691da177e4SLinus Torvalds *p = q; 701da177e4SLinus Torvalds return NULL; 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static void __init free_hash(void) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds struct hash **p, *q; 761da177e4SLinus Torvalds for (p = head; p < head + 32; p++) { 771da177e4SLinus Torvalds while (*p) { 781da177e4SLinus Torvalds q = *p; 791da177e4SLinus Torvalds *p = q->next; 801da177e4SLinus Torvalds free(q); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* cpio header parsing */ 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static __initdata unsigned long ino, major, minor, nlink; 881da177e4SLinus Torvalds static __initdata mode_t mode; 891da177e4SLinus Torvalds static __initdata unsigned long body_len, name_len; 901da177e4SLinus Torvalds static __initdata uid_t uid; 911da177e4SLinus Torvalds static __initdata gid_t gid; 921da177e4SLinus Torvalds static __initdata unsigned rdev; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds static void __init parse_header(char *s) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds unsigned long parsed[12]; 971da177e4SLinus Torvalds char buf[9]; 981da177e4SLinus Torvalds int i; 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds buf[8] = '\0'; 1011da177e4SLinus Torvalds for (i = 0, s += 6; i < 12; i++, s += 8) { 1021da177e4SLinus Torvalds memcpy(buf, s, 8); 1031da177e4SLinus Torvalds parsed[i] = simple_strtoul(buf, NULL, 16); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds ino = parsed[0]; 1061da177e4SLinus Torvalds mode = parsed[1]; 1071da177e4SLinus Torvalds uid = parsed[2]; 1081da177e4SLinus Torvalds gid = parsed[3]; 1091da177e4SLinus Torvalds nlink = parsed[4]; 1101da177e4SLinus Torvalds body_len = parsed[6]; 1111da177e4SLinus Torvalds major = parsed[7]; 1121da177e4SLinus Torvalds minor = parsed[8]; 1131da177e4SLinus Torvalds rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); 1141da177e4SLinus Torvalds name_len = parsed[11]; 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds /* FSM */ 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds static __initdata enum state { 1201da177e4SLinus Torvalds Start, 1211da177e4SLinus Torvalds Collect, 1221da177e4SLinus Torvalds GotHeader, 1231da177e4SLinus Torvalds SkipIt, 1241da177e4SLinus Torvalds GotName, 1251da177e4SLinus Torvalds CopyFile, 1261da177e4SLinus Torvalds GotSymlink, 1271da177e4SLinus Torvalds Reset 1281da177e4SLinus Torvalds } state, next_state; 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds static __initdata char *victim; 1311da177e4SLinus Torvalds static __initdata unsigned count; 1321da177e4SLinus Torvalds static __initdata loff_t this_header, next_header; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds static __initdata int dry_run; 1351da177e4SLinus Torvalds 136*b0a5ab93SAl Viro static inline void __init eat(unsigned n) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds victim += n; 1391da177e4SLinus Torvalds this_header += n; 1401da177e4SLinus Torvalds count -= n; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds static __initdata char *collected; 1441da177e4SLinus Torvalds static __initdata int remains; 1451da177e4SLinus Torvalds static __initdata char *collect; 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static void __init read_into(char *buf, unsigned size, enum state next) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds if (count >= size) { 1501da177e4SLinus Torvalds collected = victim; 1511da177e4SLinus Torvalds eat(size); 1521da177e4SLinus Torvalds state = next; 1531da177e4SLinus Torvalds } else { 1541da177e4SLinus Torvalds collect = collected = buf; 1551da177e4SLinus Torvalds remains = size; 1561da177e4SLinus Torvalds next_state = next; 1571da177e4SLinus Torvalds state = Collect; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static __initdata char *header_buf, *symlink_buf, *name_buf; 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds static int __init do_start(void) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds read_into(header_buf, 110, GotHeader); 1661da177e4SLinus Torvalds return 0; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds static int __init do_collect(void) 1701da177e4SLinus Torvalds { 1711da177e4SLinus Torvalds unsigned n = remains; 1721da177e4SLinus Torvalds if (count < n) 1731da177e4SLinus Torvalds n = count; 1741da177e4SLinus Torvalds memcpy(collect, victim, n); 1751da177e4SLinus Torvalds eat(n); 1761da177e4SLinus Torvalds collect += n; 1771da177e4SLinus Torvalds if ((remains -= n) != 0) 1781da177e4SLinus Torvalds return 1; 1791da177e4SLinus Torvalds state = next_state; 1801da177e4SLinus Torvalds return 0; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds static int __init do_header(void) 1841da177e4SLinus Torvalds { 1852e591bbcSArjan van de Ven if (memcmp(collected, "070707", 6)==0) { 1862e591bbcSArjan van de Ven error("incorrect cpio method used: use -H newc option"); 1872e591bbcSArjan van de Ven return 1; 1882e591bbcSArjan van de Ven } 1891da177e4SLinus Torvalds if (memcmp(collected, "070701", 6)) { 1901da177e4SLinus Torvalds error("no cpio magic"); 1911da177e4SLinus Torvalds return 1; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds parse_header(collected); 1941da177e4SLinus Torvalds next_header = this_header + N_ALIGN(name_len) + body_len; 1951da177e4SLinus Torvalds next_header = (next_header + 3) & ~3; 1961da177e4SLinus Torvalds if (dry_run) { 1971da177e4SLinus Torvalds read_into(name_buf, N_ALIGN(name_len), GotName); 1981da177e4SLinus Torvalds return 0; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds state = SkipIt; 2011da177e4SLinus Torvalds if (name_len <= 0 || name_len > PATH_MAX) 2021da177e4SLinus Torvalds return 0; 2031da177e4SLinus Torvalds if (S_ISLNK(mode)) { 2041da177e4SLinus Torvalds if (body_len > PATH_MAX) 2051da177e4SLinus Torvalds return 0; 2061da177e4SLinus Torvalds collect = collected = symlink_buf; 2071da177e4SLinus Torvalds remains = N_ALIGN(name_len) + body_len; 2081da177e4SLinus Torvalds next_state = GotSymlink; 2091da177e4SLinus Torvalds state = Collect; 2101da177e4SLinus Torvalds return 0; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds if (S_ISREG(mode) || !body_len) 2131da177e4SLinus Torvalds read_into(name_buf, N_ALIGN(name_len), GotName); 2141da177e4SLinus Torvalds return 0; 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds static int __init do_skip(void) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds if (this_header + count < next_header) { 2201da177e4SLinus Torvalds eat(count); 2211da177e4SLinus Torvalds return 1; 2221da177e4SLinus Torvalds } else { 2231da177e4SLinus Torvalds eat(next_header - this_header); 2241da177e4SLinus Torvalds state = next_state; 2251da177e4SLinus Torvalds return 0; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds static int __init do_reset(void) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds while(count && *victim == '\0') 2321da177e4SLinus Torvalds eat(1); 2331da177e4SLinus Torvalds if (count && (this_header & 3)) 2341da177e4SLinus Torvalds error("broken padding"); 2351da177e4SLinus Torvalds return 1; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds static int __init maybe_link(void) 2391da177e4SLinus Torvalds { 2401da177e4SLinus Torvalds if (nlink >= 2) { 2412139a7fbSH. Peter Anvin char *old = find_link(major, minor, ino, mode, collected); 2421da177e4SLinus Torvalds if (old) 2431da177e4SLinus Torvalds return (sys_link(old, collected) < 0) ? -1 : 1; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds return 0; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 2482139a7fbSH. Peter Anvin static void __init clean_path(char *path, mode_t mode) 2492139a7fbSH. Peter Anvin { 2502139a7fbSH. Peter Anvin struct stat st; 2512139a7fbSH. Peter Anvin 2522139a7fbSH. Peter Anvin if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) { 2532139a7fbSH. Peter Anvin if (S_ISDIR(st.st_mode)) 2542139a7fbSH. Peter Anvin sys_rmdir(path); 2552139a7fbSH. Peter Anvin else 2562139a7fbSH. Peter Anvin sys_unlink(path); 2572139a7fbSH. Peter Anvin } 2582139a7fbSH. Peter Anvin } 2592139a7fbSH. Peter Anvin 2601da177e4SLinus Torvalds static __initdata int wfd; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds static int __init do_name(void) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds state = SkipIt; 2651da177e4SLinus Torvalds next_state = Reset; 2661da177e4SLinus Torvalds if (strcmp(collected, "TRAILER!!!") == 0) { 2671da177e4SLinus Torvalds free_hash(); 2681da177e4SLinus Torvalds return 0; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds if (dry_run) 2711da177e4SLinus Torvalds return 0; 2722139a7fbSH. Peter Anvin clean_path(collected, mode); 2731da177e4SLinus Torvalds if (S_ISREG(mode)) { 2742139a7fbSH. Peter Anvin int ml = maybe_link(); 2752139a7fbSH. Peter Anvin if (ml >= 0) { 2762139a7fbSH. Peter Anvin int openflags = O_WRONLY|O_CREAT; 2772139a7fbSH. Peter Anvin if (ml != 1) 2782139a7fbSH. Peter Anvin openflags |= O_TRUNC; 2792139a7fbSH. Peter Anvin wfd = sys_open(collected, openflags, mode); 2802139a7fbSH. Peter Anvin 2811da177e4SLinus Torvalds if (wfd >= 0) { 2821da177e4SLinus Torvalds sys_fchown(wfd, uid, gid); 2831da177e4SLinus Torvalds sys_fchmod(wfd, mode); 2841da177e4SLinus Torvalds state = CopyFile; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds } else if (S_ISDIR(mode)) { 2881da177e4SLinus Torvalds sys_mkdir(collected, mode); 2891da177e4SLinus Torvalds sys_chown(collected, uid, gid); 2901da177e4SLinus Torvalds sys_chmod(collected, mode); 2911da177e4SLinus Torvalds } else if (S_ISBLK(mode) || S_ISCHR(mode) || 2921da177e4SLinus Torvalds S_ISFIFO(mode) || S_ISSOCK(mode)) { 2931da177e4SLinus Torvalds if (maybe_link() == 0) { 2941da177e4SLinus Torvalds sys_mknod(collected, mode, rdev); 2951da177e4SLinus Torvalds sys_chown(collected, uid, gid); 2961da177e4SLinus Torvalds sys_chmod(collected, mode); 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds static int __init do_copy(void) 3031da177e4SLinus Torvalds { 3041da177e4SLinus Torvalds if (count >= body_len) { 3051da177e4SLinus Torvalds sys_write(wfd, victim, body_len); 3061da177e4SLinus Torvalds sys_close(wfd); 3071da177e4SLinus Torvalds eat(body_len); 3081da177e4SLinus Torvalds state = SkipIt; 3091da177e4SLinus Torvalds return 0; 3101da177e4SLinus Torvalds } else { 3111da177e4SLinus Torvalds sys_write(wfd, victim, count); 3121da177e4SLinus Torvalds body_len -= count; 3131da177e4SLinus Torvalds eat(count); 3141da177e4SLinus Torvalds return 1; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds static int __init do_symlink(void) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds collected[N_ALIGN(name_len) + body_len] = '\0'; 3212139a7fbSH. Peter Anvin clean_path(collected, 0); 3221da177e4SLinus Torvalds sys_symlink(collected + N_ALIGN(name_len), collected); 3231da177e4SLinus Torvalds sys_lchown(collected, uid, gid); 3241da177e4SLinus Torvalds state = SkipIt; 3251da177e4SLinus Torvalds next_state = Reset; 3261da177e4SLinus Torvalds return 0; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds static __initdata int (*actions[])(void) = { 3301da177e4SLinus Torvalds [Start] = do_start, 3311da177e4SLinus Torvalds [Collect] = do_collect, 3321da177e4SLinus Torvalds [GotHeader] = do_header, 3331da177e4SLinus Torvalds [SkipIt] = do_skip, 3341da177e4SLinus Torvalds [GotName] = do_name, 3351da177e4SLinus Torvalds [CopyFile] = do_copy, 3361da177e4SLinus Torvalds [GotSymlink] = do_symlink, 3371da177e4SLinus Torvalds [Reset] = do_reset, 3381da177e4SLinus Torvalds }; 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static int __init write_buffer(char *buf, unsigned len) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds count = len; 3431da177e4SLinus Torvalds victim = buf; 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds while (!actions[state]()) 3461da177e4SLinus Torvalds ; 3471da177e4SLinus Torvalds return len - count; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds static void __init flush_buffer(char *buf, unsigned len) 3511da177e4SLinus Torvalds { 3521da177e4SLinus Torvalds int written; 3531da177e4SLinus Torvalds if (message) 3541da177e4SLinus Torvalds return; 3551da177e4SLinus Torvalds while ((written = write_buffer(buf, len)) < len && !message) { 3561da177e4SLinus Torvalds char c = buf[written]; 3571da177e4SLinus Torvalds if (c == '0') { 3581da177e4SLinus Torvalds buf += written; 3591da177e4SLinus Torvalds len -= written; 3601da177e4SLinus Torvalds state = Start; 3611da177e4SLinus Torvalds } else if (c == 0) { 3621da177e4SLinus Torvalds buf += written; 3631da177e4SLinus Torvalds len -= written; 3641da177e4SLinus Torvalds state = Reset; 3651da177e4SLinus Torvalds } else 3661da177e4SLinus Torvalds error("junk in compressed archive"); 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds /* 3711da177e4SLinus Torvalds * gzip declarations 3721da177e4SLinus Torvalds */ 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds #define OF(args) args 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds #ifndef memzero 3771da177e4SLinus Torvalds #define memzero(s, n) memset ((s), 0, (n)) 3781da177e4SLinus Torvalds #endif 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds typedef unsigned char uch; 3811da177e4SLinus Torvalds typedef unsigned short ush; 3821da177e4SLinus Torvalds typedef unsigned long ulg; 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds #define WSIZE 0x8000 /* window size--must be a power of two, and */ 3851da177e4SLinus Torvalds /* at least 32K for zip's deflate method */ 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds static uch *inbuf; 3881da177e4SLinus Torvalds static uch *window; 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds static unsigned insize; /* valid bytes in inbuf */ 3911da177e4SLinus Torvalds static unsigned inptr; /* index of next byte to be processed in inbuf */ 3921da177e4SLinus Torvalds static unsigned outcnt; /* bytes in output buffer */ 3931da177e4SLinus Torvalds static long bytes_out; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds #define get_byte() (inptr < insize ? inbuf[inptr++] : -1) 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds /* Diagnostic functions (stubbed out) */ 3981da177e4SLinus Torvalds #define Assert(cond,msg) 3991da177e4SLinus Torvalds #define Trace(x) 4001da177e4SLinus Torvalds #define Tracev(x) 4011da177e4SLinus Torvalds #define Tracevv(x) 4021da177e4SLinus Torvalds #define Tracec(c,x) 4031da177e4SLinus Torvalds #define Tracecv(c,x) 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds #define STATIC static 4061da177e4SLinus Torvalds #define INIT __init 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds static void __init flush_window(void); 4091da177e4SLinus Torvalds static void __init error(char *m); 4101da177e4SLinus Torvalds static void __init gzip_mark(void **); 4111da177e4SLinus Torvalds static void __init gzip_release(void **); 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds #include "../lib/inflate.c" 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds static void __init gzip_mark(void **ptr) 4161da177e4SLinus Torvalds { 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds static void __init gzip_release(void **ptr) 4201da177e4SLinus Torvalds { 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds /* =========================================================================== 4241da177e4SLinus Torvalds * Write the output window window[0..outcnt-1] and update crc and bytes_out. 4251da177e4SLinus Torvalds * (Used for the decompressed data only.) 4261da177e4SLinus Torvalds */ 4271da177e4SLinus Torvalds static void __init flush_window(void) 4281da177e4SLinus Torvalds { 4291da177e4SLinus Torvalds ulg c = crc; /* temporary variable */ 4301da177e4SLinus Torvalds unsigned n; 4311da177e4SLinus Torvalds uch *in, ch; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds flush_buffer(window, outcnt); 4341da177e4SLinus Torvalds in = window; 4351da177e4SLinus Torvalds for (n = 0; n < outcnt; n++) { 4361da177e4SLinus Torvalds ch = *in++; 4371da177e4SLinus Torvalds c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds crc = c; 4401da177e4SLinus Torvalds bytes_out += (ulg)outcnt; 4411da177e4SLinus Torvalds outcnt = 0; 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only) 4451da177e4SLinus Torvalds { 4461da177e4SLinus Torvalds int written; 4471da177e4SLinus Torvalds dry_run = check_only; 4481da177e4SLinus Torvalds header_buf = malloc(110); 4491da177e4SLinus Torvalds symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1); 4501da177e4SLinus Torvalds name_buf = malloc(N_ALIGN(PATH_MAX)); 4511da177e4SLinus Torvalds window = malloc(WSIZE); 4521da177e4SLinus Torvalds if (!window || !header_buf || !symlink_buf || !name_buf) 4531da177e4SLinus Torvalds panic("can't allocate buffers"); 4541da177e4SLinus Torvalds state = Start; 4551da177e4SLinus Torvalds this_header = 0; 4561da177e4SLinus Torvalds message = NULL; 4571da177e4SLinus Torvalds while (!message && len) { 4581da177e4SLinus Torvalds loff_t saved_offset = this_header; 4591da177e4SLinus Torvalds if (*buf == '0' && !(this_header & 3)) { 4601da177e4SLinus Torvalds state = Start; 4611da177e4SLinus Torvalds written = write_buffer(buf, len); 4621da177e4SLinus Torvalds buf += written; 4631da177e4SLinus Torvalds len -= written; 4641da177e4SLinus Torvalds continue; 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds if (!*buf) { 4671da177e4SLinus Torvalds buf++; 4681da177e4SLinus Torvalds len--; 4691da177e4SLinus Torvalds this_header++; 4701da177e4SLinus Torvalds continue; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds this_header = 0; 4731da177e4SLinus Torvalds insize = len; 4741da177e4SLinus Torvalds inbuf = buf; 4751da177e4SLinus Torvalds inptr = 0; 4761da177e4SLinus Torvalds outcnt = 0; /* bytes in output buffer */ 4771da177e4SLinus Torvalds bytes_out = 0; 4781da177e4SLinus Torvalds crc = (ulg)0xffffffffL; /* shift register contents */ 4791da177e4SLinus Torvalds makecrc(); 4801da177e4SLinus Torvalds gunzip(); 4811da177e4SLinus Torvalds if (state != Reset) 4821da177e4SLinus Torvalds error("junk in gzipped archive"); 4831da177e4SLinus Torvalds this_header = saved_offset + inptr; 4841da177e4SLinus Torvalds buf += inptr; 4851da177e4SLinus Torvalds len -= inptr; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds free(window); 4881da177e4SLinus Torvalds free(name_buf); 4891da177e4SLinus Torvalds free(symlink_buf); 4901da177e4SLinus Torvalds free(header_buf); 4911da177e4SLinus Torvalds return message; 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds 4940a7b35cbSMichael Neuling static int __initdata do_retain_initrd; 4950a7b35cbSMichael Neuling 4960a7b35cbSMichael Neuling static int __init retain_initrd_param(char *str) 4970a7b35cbSMichael Neuling { 4980a7b35cbSMichael Neuling if (*str) 4990a7b35cbSMichael Neuling return 0; 5000a7b35cbSMichael Neuling do_retain_initrd = 1; 5010a7b35cbSMichael Neuling return 1; 5020a7b35cbSMichael Neuling } 5030a7b35cbSMichael Neuling __setup("retain_initrd", retain_initrd_param); 5040a7b35cbSMichael Neuling 5051da177e4SLinus Torvalds extern char __initramfs_start[], __initramfs_end[]; 5061da177e4SLinus Torvalds #ifdef CONFIG_BLK_DEV_INITRD 5071da177e4SLinus Torvalds #include <linux/initrd.h> 5089c15e852SHaren Myneni #include <linux/kexec.h> 5090f3d2bd5SJan Beulich 5100f3d2bd5SJan Beulich static void __init free_initrd(void) 5110f3d2bd5SJan Beulich { 5129c15e852SHaren Myneni #ifdef CONFIG_KEXEC 5139c15e852SHaren Myneni unsigned long crashk_start = (unsigned long)__va(crashk_res.start); 5149c15e852SHaren Myneni unsigned long crashk_end = (unsigned long)__va(crashk_res.end); 5150a7b35cbSMichael Neuling #endif 5160a7b35cbSMichael Neuling if (do_retain_initrd) 5170a7b35cbSMichael Neuling goto skip; 5189c15e852SHaren Myneni 5190a7b35cbSMichael Neuling #ifdef CONFIG_KEXEC 5209c15e852SHaren Myneni /* 5219c15e852SHaren Myneni * If the initrd region is overlapped with crashkernel reserved region, 5229c15e852SHaren Myneni * free only memory that is not part of crashkernel region. 5239c15e852SHaren Myneni */ 5249c15e852SHaren Myneni if (initrd_start < crashk_end && initrd_end > crashk_start) { 5259c15e852SHaren Myneni /* 5269c15e852SHaren Myneni * Initialize initrd memory region since the kexec boot does 5279c15e852SHaren Myneni * not do. 5289c15e852SHaren Myneni */ 5299c15e852SHaren Myneni memset((void *)initrd_start, 0, initrd_end - initrd_start); 5309c15e852SHaren Myneni if (initrd_start < crashk_start) 5319c15e852SHaren Myneni free_initrd_mem(initrd_start, crashk_start); 5329c15e852SHaren Myneni if (initrd_end > crashk_end) 5339c15e852SHaren Myneni free_initrd_mem(crashk_end, initrd_end); 5349c15e852SHaren Myneni } else 5359c15e852SHaren Myneni #endif 5360f3d2bd5SJan Beulich free_initrd_mem(initrd_start, initrd_end); 5370a7b35cbSMichael Neuling skip: 5380f3d2bd5SJan Beulich initrd_start = 0; 5390f3d2bd5SJan Beulich initrd_end = 0; 5400f3d2bd5SJan Beulich } 5410f3d2bd5SJan Beulich 5421da177e4SLinus Torvalds #endif 5431da177e4SLinus Torvalds 5448d610dd5SLinus Torvalds static int __init populate_rootfs(void) 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds char *err = unpack_to_rootfs(__initramfs_start, 5471da177e4SLinus Torvalds __initramfs_end - __initramfs_start, 0); 5481da177e4SLinus Torvalds if (err) 5491da177e4SLinus Torvalds panic(err); 5501da177e4SLinus Torvalds #ifdef CONFIG_BLK_DEV_INITRD 5511da177e4SLinus Torvalds if (initrd_start) { 552340e48e6SZdenek Pavlas #ifdef CONFIG_BLK_DEV_RAM 5531da177e4SLinus Torvalds int fd; 5541da177e4SLinus Torvalds printk(KERN_INFO "checking if image is initramfs..."); 5551da177e4SLinus Torvalds err = unpack_to_rootfs((char *)initrd_start, 5561da177e4SLinus Torvalds initrd_end - initrd_start, 1); 5571da177e4SLinus Torvalds if (!err) { 5581da177e4SLinus Torvalds printk(" it is\n"); 5591da177e4SLinus Torvalds unpack_to_rootfs((char *)initrd_start, 5601da177e4SLinus Torvalds initrd_end - initrd_start, 0); 5610f3d2bd5SJan Beulich free_initrd(); 5628d610dd5SLinus Torvalds return 0; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds printk("it isn't (%s); looks like an initrd\n", err); 56533644c5eSJason Gunthorpe fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700); 5661da177e4SLinus Torvalds if (fd >= 0) { 5671da177e4SLinus Torvalds sys_write(fd, (char *)initrd_start, 5681da177e4SLinus Torvalds initrd_end - initrd_start); 5691da177e4SLinus Torvalds sys_close(fd); 5700f3d2bd5SJan Beulich free_initrd(); 5711da177e4SLinus Torvalds } 572340e48e6SZdenek Pavlas #else 573340e48e6SZdenek Pavlas printk(KERN_INFO "Unpacking initramfs..."); 574340e48e6SZdenek Pavlas err = unpack_to_rootfs((char *)initrd_start, 575340e48e6SZdenek Pavlas initrd_end - initrd_start, 0); 576340e48e6SZdenek Pavlas if (err) 577340e48e6SZdenek Pavlas panic(err); 578340e48e6SZdenek Pavlas printk(" done\n"); 579340e48e6SZdenek Pavlas free_initrd(); 580340e48e6SZdenek Pavlas #endif 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds #endif 5838d610dd5SLinus Torvalds return 0; 5841da177e4SLinus Torvalds } 5858d610dd5SLinus Torvalds rootfs_initcall(populate_rootfs); 586