The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/init/initramfs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   22 static __initdata char *message;
   23 static 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 
   33 static __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 
   40 static 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 
   47 static 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 
   75 static 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 
   87 static 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 
   99 static __initdata LIST_HEAD(dir_list);
  100 struct dir_entry {
  101         struct list_head list;
  102         char *name;
  103         time_t mtime;
  104 };
  105 
  106 static 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 
  117 static 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 
  128 static __initdata time_t mtime;
  129 
  130 /* cpio header parsing */
  131 
  132 static __initdata unsigned long ino, major, minor, nlink;
  133 static __initdata umode_t mode;
  134 static __initdata unsigned long body_len, name_len;
  135 static __initdata uid_t uid;
  136 static __initdata gid_t gid;
  137 static __initdata unsigned rdev;
  138 
  139 static 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 
  165 static __initdata enum state {
  166         Start,
  167         Collect,
  168         GotHeader,
  169         SkipIt,
  170         GotName,
  171         CopyFile,
  172         GotSymlink,
  173         Reset
  174 } state, next_state;
  175 
  176 static __initdata char *victim;
  177 static __initdata unsigned count;
  178 static __initdata loff_t this_header, next_header;
  179 
  180 static inline void __init eat(unsigned n)
  181 {
  182         victim += n;
  183         this_header += n;
  184         count -= n;
  185 }
  186 
  187 static __initdata char *vcollected;
  188 static __initdata char *collected;
  189 static __initdata int remains;
  190 static __initdata char *collect;
  191 
  192 static 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 
  206 static __initdata char *header_buf, *symlink_buf, *name_buf;
  207 
  208 static int __init do_start(void)
  209 {
  210         read_into(header_buf, 110, GotHeader);
  211         return 0;
  212 }
  213 
  214 static 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 
  228 static 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 
  258 static 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 
  270 static 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 
  279 static 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 
  289 static 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 
  301 static __initdata int wfd;
  302 
  303 static 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 
  346 static 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 
  364 static 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 
  376 static __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 
  387 static 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 
  397 static 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 == '') {
  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 
  420 static unsigned my_inptr;   /* index of next byte to be processed in inbuf */
  421 
  422 #include <linux/decompress/generic.h>
  423 
  424 static 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 == '' && !(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 
  485 static int __initdata do_retain_initrd;
  486 
  487 static 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 
  496 extern char __initramfs_start[];
  497 extern unsigned long __initramfs_size;
  498 #include <linux/initrd.h>
  499 #include <linux/kexec.h>
  500 
  501 static 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);
  528 skip:
  529         initrd_start = 0;
  530         initrd_end = 0;
  531 }
  532 
  533 #ifdef CONFIG_BLK_DEV_RAM
  534 #define BUF_SIZE 1024
  535 static 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 
  582 static 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 }
  621 rootfs_initcall(populate_rootfs);

Cache object: 126ae84a9c4817236963e83786700257


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.