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