linux/init/initramfs.c
<<
>>
Prefs
   1/*
   2 * Many of the syscalls used in this file expect some of the arguments
   3 * to be __user pointers not __kernel pointers.  To limit the sparse
   4 * noise, turn off sparse checking for this file.
   5 */
   6#ifdef __CHECKER__
   7#undef __CHECKER__
   8#warning "Sparse checking disabled for this file"
   9#endif
  10
  11#include <linux/init.h>
  12#include <linux/fs.h>
  13#include <linux/slab.h>
  14#include <linux/types.h>
  15#include <linux/fcntl.h>
  16#include <linux/delay.h>
  17#include <linux/string.h>
  18#include <linux/dirent.h>
  19#include <linux/syscalls.h>
  20#include <linux/utime.h>
  21
  22static __initdata char *message;
  23static void __init error(char *x)
  24{
  25        if (!message)
  26                message = x;
  27}
  28
  29/* link hash */
  30
  31#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
  32
  33static __initdata struct hash {
  34        int ino, minor, major;
  35        umode_t mode;
  36        struct hash *next;
  37        char name[N_ALIGN(PATH_MAX)];
  38} *head[32];
  39
  40static inline int hash(int major, int minor, int ino)
  41{
  42        unsigned long tmp = ino + minor + (major << 3);
  43        tmp += tmp >> 5;
  44        return tmp & 31;
  45}
  46
  47static char __init *find_link(int major, int minor, int ino,
  48                              umode_t mode, char *name)
  49{
  50        struct hash **p, *q;
  51        for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
  52                if ((*p)->ino != ino)
  53                        continue;
  54                if ((*p)->minor != minor)
  55                        continue;
  56                if ((*p)->major != major)
  57                        continue;
  58                if (((*p)->mode ^ mode) & S_IFMT)
  59                        continue;
  60                return (*p)->name;
  61        }
  62        q = kmalloc(sizeof(struct hash), GFP_KERNEL);
  63        if (!q)
  64                panic("can't allocate link hash entry");
  65        q->major = major;
  66        q->minor = minor;
  67        q->ino = ino;
  68        q->mode = mode;
  69        strcpy(q->name, name);
  70        q->next = NULL;
  71        *p = q;
  72        return NULL;
  73}
  74
  75static void __init free_hash(void)
  76{
  77        struct hash **p, *q;
  78        for (p = head; p < head + 32; p++) {
  79                while (*p) {
  80                        q = *p;
  81                        *p = q->next;
  82                        kfree(q);
  83                }
  84        }
  85}
  86
  87static long __init do_utime(char *filename, time_t mtime)
  88{
  89        struct timespec t[2];
  90
  91        t[0].tv_sec = mtime;
  92        t[0].tv_nsec = 0;
  93        t[1].tv_sec = mtime;
  94        t[1].tv_nsec = 0;
  95
  96        return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
  97}
  98
  99static __initdata LIST_HEAD(dir_list);
 100struct dir_entry {
 101        struct list_head list;
 102        char *name;
 103        time_t mtime;
 104};
 105
 106static void __init dir_add(const char *name, time_t mtime)
 107{
 108        struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
 109        if (!de)
 110                panic("can't allocate dir_entry buffer");
 111        INIT_LIST_HEAD(&de->list);
 112        de->name = kstrdup(name, GFP_KERNEL);
 113        de->mtime = mtime;
 114        list_add(&de->list, &dir_list);
 115}
 116
 117static void __init dir_utime(void)
 118{
 119        struct dir_entry *de, *tmp;
 120        list_for_each_entry_safe(de, tmp, &dir_list, list) {
 121                list_del(&de->list);
 122                do_utime(de->name, de->mtime);
 123                kfree(de->name);
 124                kfree(de);
 125        }
 126}
 127
 128static __initdata time_t mtime;
 129
 130/* cpio header parsing */
 131
 132static __initdata unsigned long ino, major, minor, nlink;
 133static __initdata umode_t mode;
 134static __initdata unsigned long body_len, name_len;
 135static __initdata uid_t uid;
 136static __initdata gid_t gid;
 137static __initdata unsigned rdev;
 138
 139static void __init parse_header(char *s)
 140{
 141        unsigned long parsed[12];
 142        char buf[9];
 143        int i;
 144
 145        buf[8] = '\0';
 146        for (i = 0, s += 6; i < 12; i++, s += 8) {
 147                memcpy(buf, s, 8);
 148                parsed[i] = simple_strtoul(buf, NULL, 16);
 149        }
 150        ino = parsed[0];
 151        mode = parsed[1];
 152        uid = parsed[2];
 153        gid = parsed[3];
 154        nlink = parsed[4];
 155        mtime = parsed[5];
 156        body_len = parsed[6];
 157        major = parsed[7];
 158        minor = parsed[8];
 159        rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
 160        name_len = parsed[11];
 161}
 162
 163/* FSM */
 164
 165static __initdata enum state {
 166        Start,
 167        Collect,
 168        GotHeader,
 169        SkipIt,
 170        GotName,
 171        CopyFile,
 172        GotSymlink,
 173        Reset
 174} state, next_state;
 175
 176static __initdata char *victim;
 177static __initdata unsigned count;
 178static __initdata loff_t this_header, next_header;
 179
 180static inline void __init eat(unsigned n)
 181{
 182        victim += n;
 183        this_header += n;
 184        count -= n;
 185}
 186
 187static __initdata char *vcollected;
 188static __initdata char *collected;
 189static __initdata int remains;
 190static __initdata char *collect;
 191
 192static void __init read_into(char *buf, unsigned size, enum state next)
 193{
 194        if (count >= size) {
 195                collected = victim;
 196                eat(size);
 197                state = next;
 198        } else {
 199                collect = collected = buf;
 200                remains = size;
 201                next_state = next;
 202                state = Collect;
 203        }
 204}
 205
 206static __initdata char *header_buf, *symlink_buf, *name_buf;
 207
 208static int __init do_start(void)
 209{
 210        read_into(header_buf, 110, GotHeader);
 211        return 0;
 212}
 213
 214static int __init do_collect(void)
 215{
 216        unsigned n = remains;
 217        if (count < n)
 218                n = count;
 219        memcpy(collect, victim, n);
 220        eat(n);
 221        collect += n;
 222        if ((remains -= n) != 0)
 223                return 1;
 224        state = next_state;
 225        return 0;
 226}
 227
 228static int __init do_header(void)
 229{
 230        if (memcmp(collected, "070707", 6)==0) {
 231                error("incorrect cpio method used: use -H newc option");
 232                return 1;
 233        }
 234        if (memcmp(collected, "070701", 6)) {
 235                error("no cpio magic");
 236                return 1;
 237        }
 238        parse_header(collected);
 239        next_header = this_header + N_ALIGN(name_len) + body_len;
 240        next_header = (next_header + 3) & ~3;
 241        state = SkipIt;
 242        if (name_len <= 0 || name_len > PATH_MAX)
 243                return 0;
 244        if (S_ISLNK(mode)) {
 245                if (body_len > PATH_MAX)
 246                        return 0;
 247                collect = collected = symlink_buf;
 248                remains = N_ALIGN(name_len) + body_len;
 249                next_state = GotSymlink;
 250                state = Collect;
 251                return 0;
 252        }
 253        if (S_ISREG(mode) || !body_len)
 254                read_into(name_buf, N_ALIGN(name_len), GotName);
 255        return 0;
 256}
 257
 258static int __init do_skip(void)
 259{
 260        if (this_header + count < next_header) {
 261                eat(count);
 262                return 1;
 263        } else {
 264                eat(next_header - this_header);
 265                state = next_state;
 266                return 0;
 267        }
 268}
 269
 270static int __init do_reset(void)
 271{
 272        while(count && *victim == '\0')
 273                eat(1);
 274        if (count && (this_header & 3))
 275                error("broken padding");
 276        return 1;
 277}
 278
 279static int __init maybe_link(void)
 280{
 281        if (nlink >= 2) {
 282                char *old = find_link(major, minor, ino, mode, collected);
 283                if (old)
 284                        return (sys_link(old, collected) < 0) ? -1 : 1;
 285        }
 286        return 0;
 287}
 288
 289static void __init clean_path(char *path, umode_t mode)
 290{
 291        struct stat st;
 292
 293        if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) {
 294                if (S_ISDIR(st.st_mode))
 295                        sys_rmdir(path);
 296                else
 297                        sys_unlink(path);
 298        }
 299}
 300
 301static __initdata int wfd;
 302
 303static int __init do_name(void)
 304{
 305        state = SkipIt;
 306        next_state = Reset;
 307        if (strcmp(collected, "TRAILER!!!") == 0) {
 308                free_hash();
 309                return 0;
 310        }
 311        clean_path(collected, mode);
 312        if (S_ISREG(mode)) {
 313                int ml = maybe_link();
 314                if (ml >= 0) {
 315                        int openflags = O_WRONLY|O_CREAT;
 316                        if (ml != 1)
 317                                openflags |= O_TRUNC;
 318                        wfd = sys_open(collected, openflags, mode);
 319
 320                        if (wfd >= 0) {
 321                                sys_fchown(wfd, uid, gid);
 322                                sys_fchmod(wfd, mode);
 323                                if (body_len)
 324                                        sys_ftruncate(wfd, body_len);
 325                                vcollected = kstrdup(collected, GFP_KERNEL);
 326                                state = CopyFile;
 327                        }
 328                }
 329        } else if (S_ISDIR(mode)) {
 330                sys_mkdir(collected, mode);
 331                sys_chown(collected, uid, gid);
 332                sys_chmod(collected, mode);
 333                dir_add(collected, mtime);
 334        } else if (S_ISBLK(mode) || S_ISCHR(mode) ||
 335                   S_ISFIFO(mode) || S_ISSOCK(mode)) {
 336                if (maybe_link() == 0) {
 337                        sys_mknod(collected, mode, rdev);
 338                        sys_chown(collected, uid, gid);
 339                        sys_chmod(collected, mode);
 340                        do_utime(collected, mtime);
 341                }
 342        }
 343        return 0;
 344}
 345
 346static int __init do_copy(void)
 347{
 348        if (count >= body_len) {
 349                sys_write(wfd, victim, body_len);
 350                sys_close(wfd);
 351                do_utime(vcollected, mtime);
 352                kfree(vcollected);
 353                eat(body_len);
 354                state = SkipIt;
 355                return 0;
 356        } else {
 357                sys_write(wfd, victim, count);
 358                body_len -= count;
 359                eat(count);
 360                return 1;
 361        }
 362}
 363
 364static int __init do_symlink(void)
 365{
 366        collected[N_ALIGN(name_len) + body_len] = '\0';
 367        clean_path(collected, 0);
 368        sys_symlink(collected + N_ALIGN(name_len), collected);
 369        sys_lchown(collected, uid, gid);
 370        do_utime(collected, mtime);
 371        state = SkipIt;
 372        next_state = Reset;
 373        return 0;
 374}
 375
 376static __initdata int (*actions[])(void) = {
 377        [Start]         = do_start,
 378        [Collect]       = do_collect,
 379        [GotHeader]     = do_header,
 380        [SkipIt]        = do_skip,
 381        [GotName]       = do_name,
 382        [CopyFile]      = do_copy,
 383        [GotSymlink]    = do_symlink,
 384        [Reset]         = do_reset,
 385};
 386
 387static int __init write_buffer(char *buf, unsigned len)
 388{
 389        count = len;
 390        victim = buf;
 391
 392        while (!actions[state]())
 393                ;
 394        return len - count;
 395}
 396
 397static int __init flush_buffer(void *bufv, unsigned len)
 398{
 399        char *buf = (char *) bufv;
 400        int written;
 401        int origLen = len;
 402        if (message)
 403                return -1;
 404        while ((written = write_buffer(buf, len)) < len && !message) {
 405                char c = buf[written];
 406                if (c == '0') {
 407                        buf += written;
 408                        len -= written;
 409                        state = Start;
 410                } else if (c == 0) {
 411                        buf += written;
 412                        len -= written;
 413                        state = Reset;
 414                } else
 415                        error("junk in compressed archive");
 416        }
 417        return origLen;
 418}
 419
 420static unsigned my_inptr;   /* index of next byte to be processed in inbuf */
 421
 422#include <linux/decompress/generic.h>
 423
 424static char * __init unpack_to_rootfs(char *buf, unsigned len)
 425{
 426        int written, res;
 427        decompress_fn decompress;
 428        const char *compress_name;
 429        static __initdata char msg_buf[64];
 430
 431        header_buf = kmalloc(110, GFP_KERNEL);
 432        symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
 433        name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
 434
 435        if (!header_buf || !symlink_buf || !name_buf)
 436                panic("can't allocate buffers");
 437
 438        state = Start;
 439        this_header = 0;
 440        message = NULL;
 441        while (!message && len) {
 442                loff_t saved_offset = this_header;
 443                if (*buf == '0' && !(this_header & 3)) {
 444                        state = Start;
 445                        written = write_buffer(buf, len);
 446                        buf += written;
 447                        len -= written;
 448                        continue;
 449                }
 450                if (!*buf) {
 451                        buf++;
 452                        len--;
 453                        this_header++;
 454                        continue;
 455                }
 456                this_header = 0;
 457                decompress = decompress_method(buf, len, &compress_name);
 458                if (decompress) {
 459                        res = decompress(buf, len, NULL, flush_buffer, NULL,
 460                                   &my_inptr, error);
 461                        if (res)
 462                                error("decompressor failed");
 463                } else if (compress_name) {
 464                        if (!message) {
 465                                snprintf(msg_buf, sizeof msg_buf,
 466                                         "compression method %s not configured",
 467                                         compress_name);
 468                                message = msg_buf;
 469                        }
 470                } else
 471                        error("junk in compressed archive");
 472                if (state != Reset)
 473                        error("junk in compressed archive");
 474                this_header = saved_offset + my_inptr;
 475                buf += my_inptr;
 476                len -= my_inptr;
 477        }
 478        dir_utime();
 479        kfree(name_buf);
 480        kfree(symlink_buf);
 481        kfree(header_buf);
 482        return message;
 483}
 484
 485static int __initdata do_retain_initrd;
 486
 487static int __init retain_initrd_param(char *str)
 488{
 489        if (*str)
 490                return 0;
 491        do_retain_initrd = 1;
 492        return 1;
 493}
 494__setup("retain_initrd", retain_initrd_param);
 495
 496extern char __initramfs_start[];
 497extern unsigned long __initramfs_size;
 498#include <linux/initrd.h>
 499#include <linux/kexec.h>
 500
 501static void __init free_initrd(void)
 502{
 503#ifdef CONFIG_KEXEC
 504        unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
 505        unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
 506#endif
 507        if (do_retain_initrd)
 508                goto skip;
 509
 510#ifdef CONFIG_KEXEC
 511        /*
 512         * If the initrd region is overlapped with crashkernel reserved region,
 513         * free only memory that is not part of crashkernel region.
 514         */
 515        if (initrd_start < crashk_end && initrd_end > crashk_start) {
 516                /*
 517                 * Initialize initrd memory region since the kexec boot does
 518                 * not do.
 519                 */
 520                memset((void *)initrd_start, 0, initrd_end - initrd_start);
 521                if (initrd_start < crashk_start)
 522                        free_initrd_mem(initrd_start, crashk_start);
 523                if (initrd_end > crashk_end)
 524                        free_initrd_mem(crashk_end, initrd_end);
 525        } else
 526#endif
 527                free_initrd_mem(initrd_start, initrd_end);
 528skip:
 529        initrd_start = 0;
 530        initrd_end = 0;
 531}
 532
 533#ifdef CONFIG_BLK_DEV_RAM
 534#define BUF_SIZE 1024
 535static void __init clean_rootfs(void)
 536{
 537        int fd;
 538        void *buf;
 539        struct linux_dirent64 *dirp;
 540        int num;
 541
 542        fd = sys_open("/", O_RDONLY, 0);
 543        WARN_ON(fd < 0);
 544        if (fd < 0)
 545                return;
 546        buf = kzalloc(BUF_SIZE, GFP_KERNEL);
 547        WARN_ON(!buf);
 548        if (!buf) {
 549                sys_close(fd);
 550                return;
 551        }
 552
 553        dirp = buf;
 554        num = sys_getdents64(fd, dirp, BUF_SIZE);
 555        while (num > 0) {
 556                while (num > 0) {
 557                        struct stat st;
 558                        int ret;
 559
 560                        ret = sys_newlstat(dirp->d_name, &st);
 561                        WARN_ON_ONCE(ret);
 562                        if (!ret) {
 563                                if (S_ISDIR(st.st_mode))
 564                                        sys_rmdir(dirp->d_name);
 565                                else
 566                                        sys_unlink(dirp->d_name);
 567                        }
 568
 569                        num -= dirp->d_reclen;
 570                        dirp = (void *)dirp + dirp->d_reclen;
 571                }
 572                dirp = buf;
 573                memset(buf, 0, BUF_SIZE);
 574                num = sys_getdents64(fd, dirp, BUF_SIZE);
 575        }
 576
 577        sys_close(fd);
 578        kfree(buf);
 579}
 580#endif
 581
 582static int __init populate_rootfs(void)
 583{
 584        char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
 585        if (err)
 586                panic(err);     /* Failed to decompress INTERNAL initramfs */
 587        if (initrd_start) {
 588#ifdef CONFIG_BLK_DEV_RAM
 589                int fd;
 590                printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
 591                err = unpack_to_rootfs((char *)initrd_start,
 592                        initrd_end - initrd_start);
 593                if (!err) {
 594                        free_initrd();
 595                        return 0;
 596                } else {
 597                        clean_rootfs();
 598                        unpack_to_rootfs(__initramfs_start, __initramfs_size);
 599                }
 600                printk(KERN_INFO "rootfs image is not initramfs (%s)"
 601                                "; looks like an initrd\n", err);
 602                fd = sys_open("/initrd.image",
 603                              O_WRONLY|O_CREAT, 0700);
 604                if (fd >= 0) {
 605                        sys_write(fd, (char *)initrd_start,
 606                                        initrd_end - initrd_start);
 607                        sys_close(fd);
 608                        free_initrd();
 609                }
 610#else
 611                printk(KERN_INFO "Unpacking initramfs...\n");
 612                err = unpack_to_rootfs((char *)initrd_start,
 613                        initrd_end - initrd_start);
 614                if (err)
 615                        printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
 616                free_initrd();
 617#endif
 618        }
 619        return 0;
 620}
 621rootfs_initcall(populate_rootfs);
 622
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.