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