1 /* febootstrap-supermin-helper reimplementation in C.
2 * Copyright (C) 2009-2011 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33 #include "xvasprintf.h"
36 #include "ext2internal.h"
40 /* The ext2 image that we build always has a fixed size, and we 'hope'
41 * that the files fit in (otherwise we'll get an error). Note that
42 * the file is sparsely allocated.
44 * The downside of allocating a very large initial disk is that the
45 * fixed overhead of ext2 is larger (since ext2 calculates it based on
46 * the size of the disk). For a 4GB disk the overhead is
49 * In future, make this configurable, or determine it from the input
52 #define APPLIANCE_SIZE ((off_t)4*1024*1024*1024)
55 ext2_start (const char *hostcpu, const char *appliance,
56 const char *modpath, const char *initrd)
58 initialize_ext2_error_table ();
60 /* Make the initrd. */
61 ext2_make_initrd (modpath, initrd);
63 /* Make the appliance sparse image. */
64 int fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
66 error (EXIT_FAILURE, errno, "open: %s", appliance);
68 if (lseek (fd, APPLIANCE_SIZE - 1, SEEK_SET) == (off_t) -1)
69 error (EXIT_FAILURE, errno, "lseek");
72 if (write (fd, &c, 1) != 1)
73 error (EXIT_FAILURE, errno, "write");
76 error (EXIT_FAILURE, errno, "close");
78 /* Run mke2fs on the file.
79 * XXX Quoting, but this string doesn't come from an untrusted source.
81 char *cmd = xasprintf ("%s -t ext2 -F%s '%s'",
83 verbose >= 2 ? "" : "q",
86 if (r == -1 || WEXITSTATUS (r) != 0)
87 error (EXIT_FAILURE, 0, "%s: failed", cmd);
91 print_timestamped_message ("finished mke2fs");
93 /* Open the filesystem. */
95 ext2fs_open (appliance, EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs);
97 error (EXIT_FAILURE, 0, "ext2fs_open: %s", error_message (err));
99 /* Bitmaps are not loaded by default, so load them. ext2fs_close will
100 * write out any changes.
102 err = ext2fs_read_bitmaps (fs);
104 error (EXIT_FAILURE, 0, "ext2fs_read_bitmaps: %s", error_message (err));
111 print_timestamped_message ("closing ext2 filesystem");
113 /* Write out changes and close. */
115 #ifdef HAVE_EXT2FS_CLOSE2
116 err = ext2fs_close2 (fs, EXT2_FLAG_FLUSH_NO_SYNC);
118 err = ext2fs_close (fs);
121 error (EXIT_FAILURE, 0, "ext2fs_close: %s", error_message (err));
125 ext2_mkdir (ext2_ino_t dir_ino, const char *dirname, const char *basename,
126 mode_t mode, uid_t uid, gid_t gid,
127 time_t ctime, time_t atime, time_t mtime)
131 mode = LINUX_S_IFDIR | (mode & 0777);
133 /* Does the directory exist? This is legitimate: we just skip
137 err = ext2fs_namei (fs, EXT2_ROOT_INO, dir_ino, basename, &ino);
141 /* Otherwise, create it. */
142 err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
144 error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
147 err = ext2fs_mkdir (fs, dir_ino, ino, basename);
149 /* See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=217892 */
150 if (err == EXT2_ET_DIR_NO_SPACE) {
151 err = ext2fs_expand_dir (fs, dir_ino);
153 error (EXIT_FAILURE, 0, "ext2fs_expand_dir: %s/%s: %s",
154 dirname, basename, error_message (err));
157 error (EXIT_FAILURE, 0, "ext2fs_mkdir: %s/%s: %s",
158 dirname, basename, error_message (err));
161 /* Copy the final permissions, UID etc. to the inode. */
162 struct ext2_inode inode;
163 err = ext2fs_read_inode (fs, ino, &inode);
165 error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
169 inode.i_ctime = ctime;
170 inode.i_atime = atime;
171 inode.i_mtime = mtime;
172 err = ext2fs_write_inode (fs, ino, &inode);
174 error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
178 ext2_empty_inode (ext2_ino_t dir_ino, const char *dirname, const char *basename,
179 mode_t mode, uid_t uid, gid_t gid,
180 time_t ctime, time_t atime, time_t mtime,
181 int major, int minor, int dir_ft, ext2_ino_t *ino_ret)
184 struct ext2_inode inode;
187 err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
189 error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
191 memset (&inode, 0, sizeof inode);
196 inode.i_links_count = 1;
197 inode.i_ctime = ctime;
198 inode.i_atime = atime;
199 inode.i_mtime = mtime;
201 inode.i_block[0] = (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
203 err = ext2fs_write_new_inode (fs, ino, &inode);
205 error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
207 ext2_link (dir_ino, basename, ino, dir_ft);
209 ext2fs_inode_alloc_stats2 (fs, ino, 1, 0);
215 /* You must create the file first with ext2_empty_inode. */
217 ext2_write_file (ext2_ino_t ino, const char *buf, size_t size,
218 const char *orig_filename)
222 err = ext2fs_file_open2 (fs, ino, NULL, EXT2_FILE_WRITE, &file);
224 error (EXIT_FAILURE, 0, "ext2fs_file_open2: %s: %s",
225 orig_filename, error_message (err));
227 /* ext2fs_file_write cannot deal with partial writes. You have
228 * to write the entire file in a single call.
230 unsigned int written;
231 err = ext2fs_file_write (file, buf, size, &written);
233 error (EXIT_FAILURE, 0, "ext2fs_file_write: %s: %s\n"
234 "Block allocation failures can happen here for several reasons:\n"
235 " - /lib/modules contains modules with debug that makes the modules very large\n"
236 " - a file listed in 'hostfiles' is unexpectedly very large\n"
237 " - too many packages added to the supermin appliance",
238 orig_filename, error_message (err));
239 if ((size_t) written != size)
240 error (EXIT_FAILURE, 0,
241 "ext2fs_file_write: %s: size = %zu != written = %u\n",
242 orig_filename, size, written);
244 err = ext2fs_file_flush (file);
246 error (EXIT_FAILURE, 0, "ext2fs_file_flush: %s: %s",
247 orig_filename, error_message (err));
248 err = ext2fs_file_close (file);
250 error (EXIT_FAILURE, 0, "ext2fs_file_close: %s: %s",
251 orig_filename, error_message (err));
253 /* Update the true size in the inode. */
254 struct ext2_inode inode;
255 err = ext2fs_read_inode (fs, ino, &inode);
257 error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s: %s",
258 orig_filename, error_message (err));
260 err = ext2fs_write_inode (fs, ino, &inode);
262 error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s: %s",
263 orig_filename, error_message (err));
266 /* This is just a wrapper around ext2fs_link which calls
267 * ext2fs_expand_dir as necessary if the directory fills up. See
268 * definition of expand_dir in the sources of debugfs.
271 ext2_link (ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft)
276 err = ext2fs_link (fs, dir_ino, basename, ino, dir_ft);
278 if (err == EXT2_ET_DIR_NO_SPACE) {
279 err = ext2fs_expand_dir (fs, dir_ino);
281 error (EXIT_FAILURE, 0, "ext2_link: ext2fs_expand_dir: %s: %s",
282 basename, error_message (err));
287 error (EXIT_FAILURE, 0, "ext2fs_link: %s: %s",
288 basename, error_message (err));
292 release_block (ext2_filsys fs, blk_t *blocknr,
293 int blockcnt, void *private)
298 ext2fs_block_alloc_stats (fs, block, -1);
302 /* unlink or rmdir path, if it exists. */
304 ext2_clean_path (ext2_ino_t dir_ino,
305 const char *dirname, const char *basename,
311 err = ext2fs_lookup (fs, dir_ino, basename, strlen (basename),
313 if (err == EXT2_ET_FILE_NOT_FOUND)
317 struct ext2_inode inode;
318 err = ext2fs_read_inode (fs, ino, &inode);
320 error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
321 inode.i_links_count--;
322 err = ext2fs_write_inode (fs, ino, &inode);
324 error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
326 err = ext2fs_unlink (fs, dir_ino, basename, 0, 0);
328 error (EXIT_FAILURE, 0, "ext2fs_unlink_inode: %s", error_message (err));
330 if (inode.i_links_count == 0) {
331 inode.i_dtime = time (NULL);
332 err = ext2fs_write_inode (fs, ino, &inode);
334 error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
336 if (ext2fs_inode_has_valid_blocks (&inode)) {
338 /* From the docs: "BLOCK_FLAG_READ_ONLY is a promise by the
339 * caller that it will not modify returned block number."
340 * RHEL 5 does not have this flag, so just omit it if it is
343 #ifdef BLOCK_FLAG_READ_ONLY
344 flags |= BLOCK_FLAG_READ_ONLY;
346 ext2fs_block_iterate (fs, ino, flags, NULL,
347 release_block, NULL);
350 ext2fs_inode_alloc_stats2 (fs, ino, -1, isdir);
353 /* else it's a directory, what to do? XXX */
356 /* Read in the whole file into memory. Check the size is still 'size'. */
358 read_whole_file (const char *filename, size_t size)
360 char *buf = malloc (size);
362 error (EXIT_FAILURE, errno, "malloc");
364 int fd = open (filename, O_RDONLY);
366 error (EXIT_FAILURE, errno, "open: %s", filename);
372 ssize_t r = read (fd, p, size - n);
374 error (EXIT_FAILURE, errno, "read: %s", filename);
376 error (EXIT_FAILURE, 0,
377 "error: file has changed size unexpectedly: %s", filename);
382 if (close (fd) == -1)
383 error (EXIT_FAILURE, errno, "close: %s", filename);
388 /* Add a file (or directory etc) from the host. */
390 ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
395 fprintf (stderr, "ext2_file_stat %s 0%o\n",
396 orig_filename, statbuf->st_mode);
398 /* Sanity check the path. These rules are always true for the paths
399 * passed to us here from the appliance layer. The assertions just
400 * verify that the rules haven't changed.
402 size_t n = strlen (orig_filename);
403 assert (n <= PATH_MAX);
405 assert (orig_filename[0] == '/'); /* always absolute path */
406 assert (n == 1 || orig_filename[n-1] != '/'); /* no trailing slash */
408 /* Don't make the root directory, it always exists. This simplifies
409 * the code that follows.
413 const char *dirname, *basename;
414 const char *p = strrchr (orig_filename, '/');
416 if (orig_filename == p) { /* "/foo" */
418 basename = orig_filename+1;
419 dir_ino = EXT2_ROOT_INO;
420 } else { /* "/foo/bar" */
421 dirname = strndup (orig_filename, p-orig_filename);
424 /* If the parent directory is a symlink to another directory, then
425 * we want to look up the target directory. (RHBZ#698089).
427 struct stat stat1, stat2;
428 if (lstat (dirname, &stat1) == 0 && S_ISLNK (stat1.st_mode) &&
429 stat (dirname, &stat2) == 0 && S_ISDIR (stat2.st_mode)) {
430 char *new_dirname = malloc (PATH_MAX+1);
431 ssize_t r = readlink (dirname, new_dirname, PATH_MAX+1);
433 error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
434 new_dirname[r] = '\0';
435 dirname = new_dirname;
438 /* Look up the parent directory. */
439 err = ext2fs_namei (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, dirname, &dir_ino);
441 error (EXIT_FAILURE, 0, "ext2: parent directory not found: %s: %s",
442 dirname, error_message (err));
445 ext2_clean_path (dir_ino, dirname, basename, S_ISDIR (statbuf->st_mode));
449 /* Create regular file. */
450 if (S_ISREG (statbuf->st_mode)) {
451 /* XXX Hard links get duplicated here. */
453 ext2_empty_inode (dir_ino, dirname, basename,
454 statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
455 statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
456 0, 0, EXT2_FT_REG_FILE, &ino);
458 if (statbuf->st_size > 0) {
459 char *buf = read_whole_file (orig_filename, statbuf->st_size);
460 ext2_write_file (ino, buf, statbuf->st_size, orig_filename);
464 /* Create a symlink. */
465 else if (S_ISLNK (statbuf->st_mode)) {
467 ext2_empty_inode (dir_ino, dirname, basename,
468 statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
469 statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
470 0, 0, EXT2_FT_SYMLINK, &ino);
472 char buf[PATH_MAX+1];
473 ssize_t r = readlink (orig_filename, buf, sizeof buf);
475 error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
476 ext2_write_file (ino, buf, r, orig_filename);
478 /* Create directory. */
479 else if (S_ISDIR (statbuf->st_mode))
480 ext2_mkdir (dir_ino, dirname, basename,
481 statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
482 statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime);
483 /* Create a special file. */
484 else if (S_ISBLK (statbuf->st_mode)) {
485 dir_ft = EXT2_FT_BLKDEV;
488 else if (S_ISCHR (statbuf->st_mode)) {
489 dir_ft = EXT2_FT_CHRDEV;
491 } else if (S_ISFIFO (statbuf->st_mode)) {
492 dir_ft = EXT2_FT_FIFO;
494 } else if (S_ISSOCK (statbuf->st_mode)) {
495 dir_ft = EXT2_FT_SOCK;
497 ext2_empty_inode (dir_ino, dirname, basename,
498 statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
499 statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
500 major (statbuf->st_rdev), minor (statbuf->st_rdev),
506 ext2_file (const char *filename)
510 if (lstat (filename, &statbuf) == -1)
511 error (EXIT_FAILURE, errno, "lstat: %s", filename);
512 ext2_file_stat (filename, &statbuf);
515 /* In theory this could be optimized to avoid a namei lookup, but
516 * it probably wouldn't make much difference.
519 ext2_fts_entry (FTSENT *entry)
521 if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
522 ext2_file (entry->fts_path);
524 ext2_file_stat (entry->fts_path, entry->fts_statp);
527 struct writer ext2_writer = {
528 .wr_start = ext2_start,
530 .wr_file = ext2_file,
531 .wr_file_stat = ext2_file_stat,
532 .wr_fts_entry = ext2_fts_entry,
533 .wr_cpio_file = ext2_cpio_file,