Fix some printf format warnings when -Wall is enabled.
[febootstrap.git] / helper / ext2.c
index 9d60da9..2395871 100644 (file)
@@ -1,5 +1,5 @@
 /* febootstrap-supermin-helper reimplementation in C.
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -43,16 +43,16 @@ ext2_filsys fs;
  *
  * The downside of allocating a very large initial disk is that the
  * fixed overhead of ext2 is larger (since ext2 calculates it based on
- * the size of the disk).  For a 1GB disk the overhead is
- * approximately 16MB.
+ * the size of the disk).  For a 4GB disk the overhead is
+ * approximately 66MB.
  *
  * In future, make this configurable, or determine it from the input
  * files (XXX).
  */
-#define APPLIANCE_SIZE (1024*1024*1024)
+#define APPLIANCE_SIZE ((off_t)4*1024*1024*1024)
 
 static void
-ext2_start (const char *appliance,
+ext2_start (const char *hostcpu, const char *appliance,
             const char *modpath, const char *initrd)
 {
   initialize_ext2_error_table ();
@@ -65,7 +65,7 @@ ext2_start (const char *appliance,
   if (fd == -1)
     error (EXIT_FAILURE, errno, "open: %s", appliance);
 
-  if (lseek (fd, APPLIANCE_SIZE - 1, SEEK_SET) == -1)
+  if (lseek (fd, APPLIANCE_SIZE - 1, SEEK_SET) == (off_t) -1)
     error (EXIT_FAILURE, errno, "lseek");
 
   char c = 0;
@@ -135,10 +135,20 @@ ext2_mkdir (ext2_ino_t dir_ino, const char *dirname, const char *basename,
   if (err != 0)
     error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
 
+ try_again:
   err = ext2fs_mkdir (fs, dir_ino, ino, basename);
-  if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_mkdir: %s/%s: %s",
-           dirname, basename, error_message (err));
+  if (err != 0) {
+    /* See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=217892 */
+    if (err == EXT2_ET_DIR_NO_SPACE) {
+      err = ext2fs_expand_dir (fs, dir_ino);
+      if (err)
+        error (EXIT_FAILURE, 0, "ext2fs_expand_dir: %s/%s: %s",
+               dirname, basename, error_message (err));
+      goto try_again;
+    } else
+      error (EXIT_FAILURE, 0, "ext2fs_mkdir: %s/%s: %s",
+             dirname, basename, error_message (err));
+  }
 
   /* Copy the final permissions, UID etc. to the inode. */
   struct ext2_inode inode;
@@ -196,13 +206,15 @@ ext2_empty_inode (ext2_ino_t dir_ino, const char *dirname, const char *basename,
 
 /* You must create the file first with ext2_empty_inode. */
 void
-ext2_write_file (ext2_ino_t ino, const char *buf, size_t size)
+ext2_write_file (ext2_ino_t ino, const char *buf, size_t size,
+                 const char *orig_filename)
 {
   errcode_t err;
   ext2_file_t file;
   err = ext2fs_file_open2 (fs, ino, NULL, EXT2_FILE_WRITE, &file);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_file_open2: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_file_open2: %s: %s",
+           orig_filename, error_message (err));
 
   /* ext2fs_file_write cannot deal with partial writes.  You have
    * to write the entire file in a single call.
@@ -210,28 +222,37 @@ ext2_write_file (ext2_ino_t ino, const char *buf, size_t size)
   unsigned int written;
   err = ext2fs_file_write (file, buf, size, &written);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_file_write: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_file_write: %s: %s\n"
+           "Block allocation failures can happen here for several reasons:\n"
+           " - /lib/modules contains modules with debug that makes the modules very large\n"
+           " - a file listed in 'hostfiles' is unexpectedly very large\n"
+           " - too many packages added to the supermin appliance",
+           orig_filename, error_message (err));
   if ((size_t) written != size)
     error (EXIT_FAILURE, 0,
-           "ext2fs_file_write: size = %zu != written = %u\n",
-           size, written);
+           "ext2fs_file_write: %s: size = %zu != written = %u\n",
+           orig_filename, size, written);
 
   err = ext2fs_file_flush (file);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_file_flush: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_file_flush: %s: %s",
+           orig_filename, error_message (err));
   err = ext2fs_file_close (file);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_file_close: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_file_close: %s: %s",
+           orig_filename, error_message (err));
 
   /* Update the true size in the inode. */
   struct ext2_inode inode;
   err = ext2fs_read_inode (fs, ino, &inode);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s: %s",
+           orig_filename, error_message (err));
   inode.i_size = size;
   err = ext2fs_write_inode (fs, ino, &inode);
   if (err != 0)
-    error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+    error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s: %s",
+           orig_filename, error_message (err));
 }
 
 /* This is just a wrapper around ext2fs_link which calls
@@ -304,9 +325,19 @@ ext2_clean_path (ext2_ino_t dir_ino,
       if (err != 0)
         error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
 
-      if (ext2fs_inode_has_valid_blocks (&inode))
-        ext2fs_block_iterate (fs, ino, BLOCK_FLAG_READ_ONLY, NULL,
+      if (ext2fs_inode_has_valid_blocks (&inode)) {
+       int flags = 0;
+       /* From the docs: "BLOCK_FLAG_READ_ONLY is a promise by the
+        * caller that it will not modify returned block number."
+        * RHEL 5 does not have this flag, so just omit it if it is
+        * not defined.
+        */
+#ifdef BLOCK_FLAG_READ_ONLY
+       flags |= BLOCK_FLAG_READ_ONLY;
+#endif
+        ext2fs_block_iterate (fs, ino, flags, NULL,
                               release_block, NULL);
+      }
 
       ext2fs_inode_alloc_stats2 (fs, ino, -1, isdir);
     }
@@ -382,6 +413,20 @@ ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
     dirname = strndup (orig_filename, p-orig_filename);
     basename = p+1;
 
+    /* If the parent directory is a symlink to another directory, then
+     * we want to look up the target directory. (RHBZ#698089).
+     */
+    struct stat stat1, stat2;
+    if (lstat (dirname, &stat1) == 0 && S_ISLNK (stat1.st_mode) &&
+        stat (dirname, &stat2) == 0 && S_ISDIR (stat2.st_mode)) {
+      char *new_dirname = malloc (PATH_MAX+1);
+      ssize_t r = readlink (dirname, new_dirname, PATH_MAX+1);
+      if (r == -1)
+        error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
+      new_dirname[r] = '\0';
+      dirname = new_dirname;
+    }
+
     /* Look up the parent directory. */
     err = ext2fs_namei (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, dirname, &dir_ino);
     if (err != 0)
@@ -404,7 +449,7 @@ ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
 
     if (statbuf->st_size > 0) {
       char *buf = read_whole_file (orig_filename, statbuf->st_size);
-      ext2_write_file (ino, buf, statbuf->st_size);
+      ext2_write_file (ino, buf, statbuf->st_size, orig_filename);
       free (buf);
     }
   }
@@ -420,7 +465,7 @@ ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
     ssize_t r = readlink (orig_filename, buf, sizeof buf);
     if (r == -1)
       error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
-    ext2_write_file (ino, buf, r);
+    ext2_write_file (ino, buf, r, orig_filename);
   }
   /* Create directory. */
   else if (S_ISDIR (statbuf->st_mode))