Magellan Linux

Annotation of /trunk/mkinitrd-magellan/klibc/usr/kinit/initrd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1122 - (hide annotations) (download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 4903 byte(s)
-updated to klibc-1.5.19
1 niro 532 /*
2     * Handle initrd, thus putting the backwards into backwards compatible
3     */
4    
5     #include <stdio.h>
6     #include <string.h>
7     #include <unistd.h>
8     #include <fcntl.h>
9     #include <errno.h>
10     #include <sys/stat.h>
11     #include <sys/mount.h>
12     #include <sys/ioctl.h>
13     #include <sys/wait.h>
14     #include "do_mounts.h"
15     #include "kinit.h"
16     #include "xpio.h"
17    
18     #define BUF_SIZE 65536 /* Should be a power of 2 */
19    
20     /*
21     * Copy the initrd to /dev/ram0, copy from the end to the beginning
22     * to avoid taking 2x the memory.
23     */
24     static int rd_copy_uncompressed(int ffd, int dfd)
25     {
26     char buffer[BUF_SIZE];
27     off_t bytes;
28     struct stat st;
29    
30 niro 1122 dprintf("kinit: uncompressed initrd\n");
31 niro 532
32     if (ffd < 0 || fstat(ffd, &st) || !S_ISREG(st.st_mode) ||
33     (bytes = st.st_size) == 0)
34     return -1;
35    
36     while (bytes) {
37     ssize_t blocksize = ((bytes - 1) & (BUF_SIZE - 1)) + 1;
38     off_t offset = bytes - blocksize;
39    
40 niro 1122 dprintf("kinit: copying %zd bytes at offset %llu\n",
41     blocksize, offset);
42 niro 532
43     if (xpread(ffd, buffer, blocksize, offset) != blocksize ||
44     xpwrite(dfd, buffer, blocksize, offset) != blocksize)
45     return -1;
46    
47     ftruncate(ffd, offset); /* Free up memory */
48     bytes = offset;
49     }
50     return 0;
51     }
52    
53     static int rd_copy_image(const char *path)
54     {
55     int ffd = open(path, O_RDONLY);
56     int rv = -1;
57     unsigned char gzip_magic[2];
58    
59     if (ffd < 0)
60     goto barf;
61    
62     if (xpread(ffd, gzip_magic, 2, 0) == 2 &&
63     gzip_magic[0] == 037 && gzip_magic[1] == 0213) {
64     FILE *wfd = fopen("/dev/ram0", "w");
65     if (!wfd)
66     goto barf;
67     rv = load_ramdisk_compressed(path, wfd, 0);
68     fclose(wfd);
69     } else {
70     int dfd = open("/dev/ram0", O_WRONLY);
71     if (dfd < 0)
72     goto barf;
73     rv = rd_copy_uncompressed(ffd, dfd);
74     close(dfd);
75     }
76    
77     barf:
78     if (ffd >= 0)
79     close(ffd);
80     return rv;
81     }
82    
83     /*
84     * Run /linuxrc, for emulation of old-style initrd
85     */
86     static int run_linuxrc(int argc, char *argv[], dev_t root_dev)
87     {
88     int root_fd, old_fd;
89     pid_t pid;
90     long realroot = Root_RAM0;
91     const char *ramdisk_name = "/dev/ram0";
92     FILE *fp;
93    
94 niro 1122 dprintf("kinit: mounting initrd\n");
95 niro 532 mkdir("/root", 0700);
96     if (!mount_block(ramdisk_name, "/root", NULL, MS_VERBOSE, NULL))
97     return -errno;
98    
99     /* Write the current "real root device" out to procfs */
100 niro 1122 dprintf("kinit: real_root_dev = %#x\n", root_dev);
101 niro 532 fp = fopen("/proc/sys/kernel/real-root-dev", "w");
102     fprintf(fp, "%u", root_dev);
103     fclose(fp);
104    
105     mkdir("/old", 0700);
106     root_fd = open_cloexec("/", O_RDONLY | O_DIRECTORY, 0);
107     old_fd = open_cloexec("/old", O_RDONLY | O_DIRECTORY, 0);
108    
109     if (root_fd < 0 || old_fd < 0)
110     return -errno;
111    
112     if (chdir("/root") ||
113     mount(".", "/", NULL, MS_MOVE, NULL) || chroot("."))
114     return -errno;
115    
116     pid = vfork();
117     if (pid == 0) {
118     setsid();
119     /* Looks like linuxrc doesn't get the init environment
120     or parameters. Weird, but so is the whole linuxrc bit. */
121     execl("/linuxrc", "linuxrc", NULL);
122     _exit(255);
123     } else if (pid > 0) {
124 niro 1122 dprintf("kinit: Waiting for linuxrc to complete...\n");
125 niro 532 while (waitpid(pid, NULL, 0) != pid) ;
126 niro 1122 dprintf("kinit: linuxrc done\n");
127 niro 532 } else {
128     return -errno;
129     }
130    
131     if (fchdir(old_fd) ||
132     mount("/", ".", NULL, MS_MOVE, NULL) ||
133     fchdir(root_fd) || chroot("."))
134     return -errno;
135    
136     close(root_fd);
137     close(old_fd);
138    
139     getintfile("/proc/sys/kernel/real-root-dev", &realroot);
140    
141     /* If realroot is Root_RAM0, then the initrd did any necessary work */
142     if (realroot == Root_RAM0) {
143     if (mount("/old", "/root", NULL, MS_MOVE, NULL))
144     return -errno;
145     } else {
146     mount_root(argc, argv, (dev_t) realroot, NULL);
147    
148     /* If /root/initrd exists, move the initrd there, otherwise discard */
149     if (!mount("/old", "/root/initrd", NULL, MS_MOVE, NULL)) {
150     /* We're good */
151     } else {
152     int olddev = open(ramdisk_name, O_RDWR);
153     umount2("/old", MNT_DETACH);
154     if (olddev < 0 ||
155     ioctl(olddev, BLKFLSBUF, (long)0) ||
156     close(olddev)) {
157     fprintf(stderr,
158     "%s: Cannot flush initrd contents\n",
159     progname);
160     }
161     }
162     }
163    
164     rmdir("/old");
165     return 0;
166     }
167    
168     int initrd_load(int argc, char *argv[], dev_t root_dev)
169     {
170     if (access("/initrd.image", R_OK))
171     return 0; /* No initrd */
172    
173 niro 1122 dprintf("kinit: initrd found\n");
174 niro 532
175     create_dev("/dev/ram0", Root_RAM0);
176    
177     if (rd_copy_image("/initrd.image") || unlink("/initrd.image")) {
178     fprintf(stderr, "%s: initrd installation failed (too big?)\n",
179     progname);
180     return 0; /* Failed to copy initrd */
181     }
182    
183 niro 1122 dprintf("kinit: initrd copied\n");
184 niro 532
185 niro 1122 if (root_dev == Root_MULTI) {
186     dprintf("kinit: skipping linuxrc: incompatible with multiple roots\n");
187     /* Mounting initrd as ordinary root */
188     return 0;
189     }
190    
191 niro 532 if (root_dev != Root_RAM0) {
192     int err;
193 niro 1122 dprintf("kinit: running linuxrc\n");
194 niro 532 err = run_linuxrc(argc, argv, root_dev);
195     if (err)
196     fprintf(stderr, "%s: running linuxrc: %s\n", progname,
197     strerror(-err));
198     return 1; /* initrd is root, or run_linuxrc took care of it */
199     } else {
200 niro 1122 dprintf("kinit: permament (or pivoting) initrd, not running linuxrc\n");
201 niro 532 return 0; /* Mounting initrd as ordinary root */
202     }
203     }