Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Supermin 5 rewrite.

  • Loading branch information...
commit 30de2cb603cdde33524a66d5466f6a9b986ce8a6 1 parent dbfb603
@rwmjones rwmjones authored
Showing with 3,919 additions and 6,494 deletions.
  1. +4 −3 .gitignore
  2. +0 −3  .gitmodules
  3. +41 −0 HACKING
  4. +2 −4 Makefile.am
  5. +5 −2 README
  6. +11 −47 TODO
  7. +0 −65 autobuild.sh
  8. +0 −12 autogen.sh
  9. +11 −35 configure.ac
  10. +11 −0 examples/README
  11. +50 −13 examples/build-basic-vm.sh
  12. +0 −1  gnulib
  13. +0 −88 helper/Makefile.am
  14. +0 −10 helper/README
  15. +0 −330 helper/appliance.c
  16. +0 −119 helper/checksum.c
  17. +0 −279 helper/cpio.c
  18. +0 −538 helper/ext2.c
  19. +0 −418 helper/ext2cpio.c
  20. +0 −281 helper/ext2initrd.c
  21. +0 −45 helper/ext2internal.h
  22. +0 −85 helper/helper.h
  23. +0 −454 helper/kernel.c
  24. +0 −475 helper/main.c
  25. +0 −351 helper/supermin-helper.pod
  26. +0 −314 helper/utils.c
  27. +0 −175 lib/.gitignore
  28. +0 −114 m4/.gitignore
  29. +0 −55 m4/gnulib-cache.m4
  30. +88 −52 src/Makefile.am
  31. 0  {helper → src}/bin2s.pl
  32. +391 −0 src/build.ml
  33. +77 −0 src/chroot.ml
  34. +4 −6 src/config.ml.in
  35. +190 −0 src/dpkg.ml
  36. +80 −0 src/ext2.ml
  37. +156 −0 src/ext2_initrd.ml
  38. +655 −0 src/ext2fs-c.c
  39. +26 −0 src/ext2fs.ml
  40. +33 −0 src/ext2fs.mli
  41. +44 −0 src/ext2init-c.c
  42. +19 −0 src/ext2init.ml
  43. +19 −0 src/ext2init.mli
  44. +64 −0 src/fnmatch-c.c
  45. +28 −0 src/fnmatch.ml
  46. +27 −0 src/fnmatch.mli
  47. +84 −0 src/glob-c.c
  48. +28 −0 src/glob.ml
  49. +27 −0 src/glob.mli
  50. +1 −1  {helper → src}/init.c
  51. +273 −0 src/kernel.ml
  52. +130 −0 src/package_handler.ml
  53. +167 −0 src/package_handler.mli
  54. +163 −0 src/prepare.ml
  55. +44 −0 src/realpath-c.c
  56. +19 −0 src/realpath.ml
  57. +19 −0 src/realpath.mli
  58. +233 −0 src/rpm.ml
  59. +24 −0 src/supermin-link.sh.in
  60. +223 −402 src/supermin.ml
  61. +332 −164 src/supermin.pod
  62. +0 −120 src/supermin_cmdline.ml
  63. +0 −60 src/supermin_cmdline.mli
  64. +0 −231 src/supermin_debian.ml
  65. +0 −81 src/supermin_package_handlers.ml
  66. +0 −73 src/supermin_package_handlers.mli
  67. +0 −145 src/supermin_pacman.ml
  68. +0 −149 src/supermin_pacman_g2.ml
  69. +0 −132 src/supermin_urpmi_rpm.ml
  70. +0 −262 src/supermin_yum_rpm.ml
  71. +0 −269 src/supermin_zypp_rpm.ml
  72. +19 −0 src/types.ml
  73. +72 −23 src/{supermin_utils.ml → utils.ml}
  74. +25 −8 src/{supermin_utils.mli → utils.mli}
View
7 .gitignore
@@ -27,9 +27,7 @@ pod2htm?.tmp
/configure
/cscope.out
/depcomp
-/examples/basic-kernel
-/examples/basic-initrd
-/examples/basic-root
+/examples/basic-full-appliance
/examples/basic-supermin.d
/helper/ext2init.S
/helper/supermin-helper
@@ -57,8 +55,11 @@ pod2htm?.tmp
/snippet/
/src/.depend
/src/config.ml
+/src/ext2init-bin.S
+/src/init
/src/supermin
/src/supermin.1
+/src/supermin-link.sh
/stamp-h1
/supermin-*.tar.gz
/test-driver
View
3  .gitmodules
@@ -1,3 +0,0 @@
-[submodule "gnulib"]
- path = gnulib
- url = git://git.sv.gnu.org/gnulib.git
View
41 HACKING
@@ -0,0 +1,41 @@
+Send patches to the libguestfs mailing list.
+
+Overview of the source files:
+
+ supermin.ml main program, argument parsing and coordination
+
+ |
+ +- prepare.ml Prepare mode (--prepare option)
+ |
+ +- build.ml Build mode (--build option)
+ |
+ +- chroot.ml Build a chroot (--build -f chroot)
+ |
+ +- ext2.ml Build an ext2 fs (--build -f ext2)
+ |
+ +- kernel.ml Find the right kernel to use
+ |
+ +- ext2_initrd.ml Build a minimal initrd
+
+Libraries used by both modes:
+
+ |
+ +- package_hander.ml Package manager interface, for resolving
+ | | package dependencies, list of files, etc.
+ | |
+ | +- rpm.ml Package manager implementation for RPM distros
+ | |
+ | +- dpkg.ml Package manager implementation for dpkg distros
+ | |
+ | +- etc.
+ |
+ +- config.ml Configuration (from autoconf)
+ |
+ +- types.ml Some global type declarations
+ |
+ +- utils.ml Some utility functions
+ |
+ +- fnmatch.ml Interface to fnmatch(3)
+ | |
+ | +- fnmatch-c.c Binding to fnmatch(3)
+ +- etc. And other C bindings ...
View
6 Makefile.am
@@ -19,20 +19,18 @@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = lib src helper examples tests
+SUBDIRS = src examples tests
EXTRA_DIST = \
.gitignore \
.gitmodules \
autogen.sh \
html/pod.css \
- m4/gnulib-cache.m4 \
$(SOURCES)
# Maintainer website update.
HTMLFILES = \
- html/supermin.1.html \
- html/supermin-helper.1.html
+ html/supermin.1.html
WEBSITEDIR = $(HOME)/d/redhat/websites/libguestfs
View
7 README
@@ -10,6 +10,10 @@ second when you need to boot one of them.
A complete description is in the supermin(1) man page.
+IMPORTANT NOTE FOR USERS OF SUPERMIN 4.x: supermin 5.x is a rewrite of
+ supermin 4. It is compatible at a high level with supermin 4 /
+ febootstrap 3, but requires some command line adjustments.
+
IMPORTANT NOTE FOR USERS OF FEBOOTSTRAP 3.x:
supermin 4.x is just an evolution of febootstrap 3.x (really we just
renamed it). The previous febootstrap program is now called
@@ -49,8 +53,7 @@ Requirements
For Fedora/RHEL:
rpm
- yum
- yumdownloader
+ yumdownloader (from yum-utils)
For Debian/Ubuntu:
View
58 TODO
@@ -1,34 +1,6 @@
-Ideas for a future version of supermin.
+For supermin 5 rewrite:
-
-hostfiles
----------
-
-'hostfiles' causes lots of trouble, because it bakes path dependencies
-into the supermin appliance.
-
-We propose to replace this with a list of root packages. Lines in
-hostfiles beginning with a "+" sign are a root package, eg:
-
- +bash
- +coreutils
-
-We then query RPM or dpkg to get a list of hostfiles at appliance boot
-time. eg. For rpm we'd do the rpmlib equivalents of:
-
- rpm -ql bash # list of files in bash
- rpm -qR bash # what bash requires
- rpm -q --whatprovides <depN> # recursively look up each requires
-
-(If this is too slow, aggressively cache the results)
-
-Some files still need to be stored at build time, basically %config
-files.
-
-Unclear what to do about kernels if we make this change.
-
-'hostfiles' may still be needed for a handful of files that we really
-want to copy in. Notable ones: /etc/localtime and /etc/resolv.conf.
+ - support for other distros
Store %post scripts
@@ -38,21 +10,13 @@ Can we get the %post scripts and store them in a directory in the
appliance?
-Directories
------------
-
-Reconstruction is tied to having directories being created before they
-are used. We should try to remove this limitation as it's really
-quite unnecessary.
-
-
-Use libsolv or hawkey for dependency resolution
------------------------------------------------
-
-Using Python + yum to resolve RPM dependencies is sucky and slow. We
-should look at the combination of DNF, libsolv and hawkey to replace
-them:
+Some features of supermin 4 which were cut in supermin 5
+--------------------------------------------------------
-https://fedoraproject.org/wiki/Features/DNF
-https://github.com/openSUSE/libsolv
-https://github.com/akozumpl/hawkey
+ - write an initrd file (ie. '-f cpio')
+ - install from a set of package files
+ - exclude packages after dep solving
+ - save-temps
+ - --output-{appliance,dtb,initrd,kernel} options
+ - --user/--group options
+ - --kmods option
View
65 autobuild.sh
@@ -1,65 +0,0 @@
-#!/bin/bash -
-
-PROJECT=supermin
-MAILTO=libguestfs@redhat.com
-HOSTNAME="$(hostname -s)"
-
-#----------------------------------------------------------------------
-# Helper functions.
-
-failed ()
-{
- mail -s "$HOSTNAME $PROJECT FAILED $1 $gitsha" $MAILTO < local-log
-}
-
-ok ()
-{
- mail -s "$HOSTNAME $PROJECT success $gitsha" $MAILTO < local-log
-}
-
-#----------------------------------------------------------------------
-
-set -e
-set -x
-
-rm -f local-log
-cat > local-log <<EOF
-
-This is an automatic message generated by the builder on
-$HOSTNAME for $PROJECT. Log files from the build
-follow below.
-
-$(uname -a)
-$(date)
-
------
-
-EOF
-exec >> local-log 2>&1
-
-# Pull from the public repo so that we don't need ssh-agent.
-git pull --rebase git://github.com/libguestfs/supermin.git
-git clean -d -f
-
-# The git version we are building.
-gitsha=$(git log|head -1|awk '{print $2}')
-
-# Do the configure step.
-./autogen.sh || {
- failed "configure step"
- exit 1
-}
-
-# Do the build step.
-make || {
- failed "build step"
- exit 1
-}
-
-# Run the tests.
-make check || {
- failed "tests"
- exit 1
-}
-
-ok
View
12 autogen.sh
@@ -1,12 +0,0 @@
-#!/bin/sh -
-
-if [ -z "$(ls gnulib 2>/dev/null)" ]
-then
- git clone git://git.savannah.gnu.org/gnulib.git
-fi
-
-./gnulib/gnulib-tool --update
-
-export AUTOMAKE='automake --foreign --add-missing'
-autoreconf
-./configure "$@"
View
46 configure.ac
@@ -1,5 +1,5 @@
dnl supermin configure.ac
-dnl (C) Copyright 2009-2013 Red Hat Inc.
+dnl (C) Copyright 2009-2014 Red Hat Inc.
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
@@ -17,12 +17,11 @@ dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
dnl
dnl Written by Richard W.M. Jones <rjones@redhat.com>
-AC_INIT([supermin],[4.1.6])
+AC_INIT([supermin],[5.1.0])
AM_INIT_AUTOMAKE(foreign)
dnl Check for basic C environment.
AC_PROG_CC_STDC
-gl_EARLY
AC_PROG_INSTALL
AC_PROG_CPP
@@ -35,10 +34,11 @@ AM_PROG_CC_C_O
dnl Check support for 64 bit file offsets.
AC_SYS_LARGEFILE
-gl_INIT
+dnl Enable GNU stuff.
+AC_USE_SYSTEM_EXTENSIONS
-dnl Define a C symbol for the host CPU architecture.
-AC_DEFINE_UNQUOTED([host_cpu],["$host_cpu"],[Host architecture.])
+dnl Define the host CPU architecture (defines host_cpu).
+AC_CANONICAL_HOST
# Define $(SED).
m4_ifdef([AC_PROG_SED],[
@@ -59,10 +59,6 @@ if test "$OCAMLFIND" = "no"; then
AC_MSG_ERROR([You must install OCaml findlib (the ocamlfind command)])
fi
-dnl Optional OCaml packages.
-AC_CHECK_OCAML_PKG([inifiles])
-AM_CONDITIONAL([HAVE_OCAML_INIFILES], [test "x$OCAML_PKG_inifiles" != "xno"])
-
dnl Optional programs.
AC_CHECK_PROG(PERLDOC,[perldoc],[perldoc],[no])
if test "x$PERLDOC" = "xno" ; then
@@ -71,7 +67,6 @@ fi
AM_CONDITIONAL(HAVE_PERLDOC,[test "$perldoc" != "no"])
dnl For yum-rpm handler.
-AC_CHECK_PROG(YUM,[yum],[yum],[no])
AC_CHECK_PROG(RPM,[rpm],[rpm],[no])
AC_CHECK_PROG(YUMDOWNLOADER,[yumdownloader],[yumdownloader],[no])
@@ -82,25 +77,8 @@ dnl For URPMI handler.
AC_CHECK_PROG(URPMI,[urpmi],[urpmi],[no], [$PATH$PATH_SEPARATOR/usr/sbin])
dnl For Debian handler.
-AC_CHECK_PROG(APTITUDE,[aptitude],[aptitude],[no])
-AC_CHECK_PROG(APT_CACHE,[apt-cache],[apt-cache],[no])
AC_CHECK_PROG(DPKG,[dpkg],[dpkg],[no])
-dnl Include workaround for broken apt-cache depends --recurse (Ubuntu 10.04)?
-if test "x$APT_CACHE" != "xno"; then
- AC_MSG_CHECKING([if apt-cache depends --recurse is broken])
- if ! $APT_CACHE depends --recurse -i bash | grep -q '^libc6$'; then
- AC_MSG_RESULT([yes])
- APT_CACHE_DEPENDS_RECURSE_BROKEN=true
- else
- AC_MSG_RESULT([no])
- APT_CACHE_DEPENDS_RECURSE_BROKEN=false
- fi
-else
- APT_CACHE_DEPENDS_RECURSE_BROKEN=false
-fi
-AC_SUBST([APT_CACHE_DEPENDS_RECURSE_BROKEN])
-
dnl For FrugalWare handler.
AC_CHECK_PROG(PACMAN_G2,[pacman-g2],[pacman-g2],[no])
@@ -187,8 +165,7 @@ AC_PATH_PROG([MKE2FS],[mke2fs],[no],
if test "x$MKE2FS" = "xno" ; then
AC_MSG_FAILURE([mke2fs program not found])
fi
-AC_DEFINE_UNQUOTED([MKE2FS],["$MKE2FS"],
- [Full path to the mke2fs program.])
+AC_SUBST([MKE2FS])
dnl RHEL 5 mke2fs needed -T <fs> instead of -t <fs>. Unhelpfully
dnl the --help output doesn't mention this, so we have to test it.
@@ -204,14 +181,13 @@ else
fi
rm conftest.img
AC_MSG_RESULT([$MKE2FS_T_OPTION])
-AC_DEFINE_UNQUOTED([MKE2FS_T_OPTION],["$MKE2FS_T_OPTION"],
- [mke2fs option used to specify filesystem type (usually "-t")])
+AC_SUBST([MKE2FS_T_OPTION])
dnl ext2fs, com_err.
PKG_CHECK_MODULES([EXT2FS], [ext2fs])
PKG_CHECK_MODULES([COM_ERR], [com_err])
-dnl Optional ext2fs_close2 function.
+dnl Requires ext2fs_close2 function, added in 2011.
old_LIBS="$LIBS"
LIBS="$EXT2FS_LIBS $COM_ERR_LIBS"
AC_CHECK_FUNCS([ext2fs_close2])
@@ -233,10 +209,10 @@ AM_CONDITIONAL([NETWORK_TESTS],
[test "x$enable_network_tests" = "xyes"])
AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([src/supermin-link.sh],
+ [chmod +x,-w src/supermin-link.sh])
AC_CONFIG_FILES([Makefile
examples/Makefile
- helper/Makefile
- lib/Makefile
src/config.ml
src/Makefile
tests/Makefile])
View
11 examples/README
@@ -0,0 +1,11 @@
+The examples in this directory will give you some ideas how to run supermin.
+
+You can run them from this directory, eg:
+
+ ./build-basic-vm.sh
+
+- You will need to build supermin first.
+
+- Read the scripts first!
+
+- They do NOT need root privileges, and are safe to run.
View
63 examples/build-basic-vm.sh
@@ -7,12 +7,24 @@ set -e
# shell. Also included is coreutils so that commands such as 'ls'
# will work.
+if [ "$(id -u)" -eq "0" ]; then
+ echo "Do not run this script as root!"
+ exit 1
+fi
+
+#----------------------------------------------------------------------
+
+# Prepare mode:
+
pkgs="bash coreutils"
+echo "Building a supermin appliance containing $pkgs ..."
+echo
+
# Create a supermin appliance in basic-supermin.d/ subdirectory.
rm -rf basic-supermin.d
mkdir basic-supermin.d
-supermin -v --names $pkgs -o basic-supermin.d
+../src/supermin --prepare $pkgs -o basic-supermin.d
# Create an init script.
rm -f init
@@ -22,26 +34,51 @@ exec bash
EOF
chmod 0755 init
-# Create an init cpio file containing the init script as "/init".
-echo -e "init\n" | cpio --quiet -o -H newc > basic-supermin.d/init.img
+# Create a tar file containing the init script as "/init".
+tar zcf basic-supermin.d/init.tar.gz init
-# Normally the contents of basic-supermin.d are what you would
-# distribute to users. However for this example, I'm now going to run
-# supermin-helper to build the final appliance.
echo "Built the supermin appliance:"
ls -lh basic-supermin.d/
+echo
+
+# Clean up temporary files.
+rm init
+
+#----------------------------------------------------------------------
+
+# Build mode:
+
+# Normally the contents of basic-supermin.d are what you would
+# distribute to users. However for this example, I'm now going to run
+# supermin --build to build the final appliance.
+
+echo "If you see 'Permission denied' errors here, it could be because your"
+echo "distro has decided to engage in security-by-obscurity by making"
+echo "some host binaries unreadable by ordinary users. Normally you can"
+echo "ignore these errors."
+echo
# Build the full appliance.
-supermin-helper --copy-kernel -f ext2 basic-supermin.d "$(uname -m)" \
- basic-kernel basic-initrd basic-root
+rm -rf basic-full-appliance
+mkdir basic-full-appliance
+../src/supermin --build -f ext2 \
+ --copy-kernel --host-cpu "$(uname -m)" \
+ -o basic-full-appliance \
+ basic-supermin.d
+echo
echo "Built the full appliance:"
-ls -lh basic-kernel basic-initrd basic-root
+ls -lsh basic-full-appliance
echo
+
+#----------------------------------------------------------------------
+
echo "To run the full appliance, use a command such as:"
-echo " qemu-kvm -m 512 -kernel basic-kernel -initrd basic-initrd \\"
+echo " qemu-kvm -m 512 -kernel kernel -initrd initrd \\"
echo " -append 'vga=773 selinux=0' \\"
-echo " -drive file=basic-root,format=raw,if=virtio"
+echo " -drive file=root,format=raw,if=virtio"
+echo
-# Clean up temporary files.
-rm init
+echo "You can examine the supermin appliance in basic-supermin.d/"
+echo "You can examine the full appliance in basic-full-appliance/"
+echo
1  gnulib
@@ -1 +0,0 @@
-Subproject commit 0ac90c5a98030c998f3e1db3a0d7f19d4630b6b6
View
88 helper/Makefile.am
@@ -1,88 +0,0 @@
-# supermin Makefile.am
-# (C) Copyright 2010-2013 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
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-# Written by Richard W.M. Jones <rjones@redhat.com>
-
-bin_PROGRAMS = \
- supermin-helper
-
-supermin_helper_SOURCES = \
- helper.h \
- appliance.c \
- checksum.c \
- cpio.c \
- ext2.c \
- ext2cpio.c \
- ext2initrd.c \
- ext2internal.h \
- kernel.c \
- main.c \
- utils.c
-supermin_helper_CFLAGS = \
- -Wall $(EXT2FS_CFLAGS) $(COM_ERR_CFLAGS) -I../lib
-supermin_helper_LDADD = \
- ext2init.o $(EXT2FS_LIBS) $(COM_ERR_LIBS) $(ZLIB_LIBS) \
- $(LTLIBINTL) -L../lib -lgnu
-
-# init "script" used by ext2 initrd.
-noinst_PROGRAMS = init
-init_SOURCES = init.c
-init_CFLAGS = -static
-init_LDFLAGS = -static
-init_LDADD = $(ZLIB_STATIC_LIBS) $(LZMA_STATIC_LIBS)
-
-CLEANFILES = ext2init.S
-
-ext2init.o: ext2init.S
- $(CC) -o $@ -c $<
-
-ext2init.S: init
- strip --strip-all $<
- @file $< | grep -isq static || \
- (echo "*** error: init is not staticly linked"; exit 1)
- ./bin2s.pl $< $@
-
-man_MANS = \
- supermin-helper.1
-
-if HAVE_PERLDOC
-
-supermin-helper.1: supermin-helper.pod
- pod2man \
- -u \
- --section 1 \
- -c "Virtualization Support" \
- --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
- $< > $@
-
-noinst_DATA = \
- $(top_builddir)/html/supermin-helper.1.html
-
-$(top_builddir)/html/supermin-helper.1.html: supermin-helper.pod
- mkdir -p $(top_builddir)/html
- cd $(top_builddir) && pod2html \
- --css 'pod.css' \
- --htmldir html \
- --outfile html/supermin-helper.1.html \
- helper/supermin-helper.pod
-
-endif
-
-EXTRA_DIST = \
- supermin-helper.1 \
- supermin-helper.pod \
- bin2s.pl
View
10 helper/README
@@ -1,10 +0,0 @@
-This directory contains the supermin-helper reimplementation in C.
-
-This program builds the supermin appliance on the fly each time the
-appliance runs (or in recent versions of libguestfs, the appliance is
-cached as long as the host files don't change).
-
-*NOTE*: This program is designed to be very short-lived, and so we
-don't normally bother to free up any memory that we allocate. That's
-not completely true - we free up stuff if it's obvious and easy to
-free up, and ignore the rest.
View
330 helper/appliance.c
@@ -1,330 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fnmatch.h>
-#include <sys/stat.h>
-#include <assert.h>
-
-#include "error.h"
-#include "fts_.h"
-#include "xalloc.h"
-#include "xvasprintf.h"
-
-#include "helper.h"
-
-static void iterate_inputs (char **inputs, int nr_inputs, struct writer *);
-static void iterate_input_directory (const char *dirname, int dirfd, struct writer *);
-static void add_kernel_modules (const char *whitelist, const char *modpath, struct writer *);
-static void add_hostfiles (const char *hostfiles_file, struct writer *);
-
-/* Create the appliance.
- *
- * The initrd consists of these components concatenated together:
- *
- * (1) The base skeleton appliance that we constructed at build time.
- * format = plain cpio
- * (2) The host files which match wildcards in *.supermin.hostfiles.
- * input format = plain text, output format = plain cpio
- * (3) The modules from modpath which are on the module whitelist.
- * output format = plain cpio
- *
- * The original shell script used the external cpio program to create
- * parts (2) and (3), but we have decided it's going to be faster if
- * we just write out the data outselves. The reasons are that
- * external cpio is slow (particularly when used with SELinux because
- * it does 512 byte reads), and the format that we're writing is
- * narrow and well understood, because we only care that the Linux
- * kernel can read it.
- *
- * This version contains some improvements over the C version written
- * for libguestfs, in that we can have multiple base images (or
- * hostfiles) or use a directory to store these files.
- */
-void
-create_appliance (const char *hostcpu,
- char **inputs, int nr_inputs,
- const char *whitelist,
- const char *modpath,
- const char *initrd,
- const char *appliance,
- struct writer *writer)
-{
- writer->wr_start (hostcpu, appliance, modpath, initrd);
-
- iterate_inputs (inputs, nr_inputs, writer);
-
- writer->wr_file ("/lib/modules");
- /* Kernel modules (3). */
- add_kernel_modules (whitelist, modpath, writer);
-
- writer->wr_end ();
-}
-
-/* Iterate over the inputs to find out what they are, visiting
- * directories if specified.
- */
-static void
-iterate_inputs (char **inputs, int nr_inputs, struct writer *writer)
-{
- int i;
- for (i = 0; i < nr_inputs; ++i) {
- if (verbose)
- print_timestamped_message ("visiting %s", inputs[i]);
-
- int fd = open (inputs[i], O_RDONLY);
- if (fd == -1)
- error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
-
- struct stat statbuf;
- if (fstat (fd, &statbuf) == -1)
- error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
-
- /* Directory? */
- if (S_ISDIR (statbuf.st_mode))
- iterate_input_directory (inputs[i], fd, writer);
- else if (S_ISREG (statbuf.st_mode)) {
- /* Is it a cpio file? */
- char buf[6];
- ssize_t r = read (fd, buf, 6);
- if (r >= 6 && memcmp (buf, "070701", 6) == 0)
- /* Yes, a cpio file. This is a skeleton appliance, case (1). */
- cpio:
- writer->wr_cpio_file (inputs[i]);
- else if (r >= 2 && memcmp (buf, "\x1f\x8b", 2) == 0)
- /* A gzip-compressed file. This must be cpio; case (1). */
- goto cpio;
- else
- /* No, must be hostfiles, case (2). */
- add_hostfiles (inputs[i], writer);
- }
- else
- error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
- inputs[i]);
-
- close (fd);
- }
-}
-
-static int
-string_compare (const void *p1, const void *p2)
-{
- return strcmp (* (char * const *) p1, * (char * const *) p2);
-}
-
-static void
-iterate_input_directory (const char *dirname, int dirfd, struct writer *writer)
-{
- DIR *dir = fdopendir (dirfd);
- if (dir == NULL)
- error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
-
- char **entries = NULL;
- size_t nr_entries = 0, nr_alloc = 0;
-
- struct dirent *d;
- while ((errno = 0, d = readdir (dir)) != NULL) {
- if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
- continue;
-
- /* Ignore *~ files created by editors. */
- size_t len = strlen (d->d_name);
- if (len > 0 && d->d_name[len-1] == '~')
- continue;
-
- add_string (&entries, &nr_entries, &nr_alloc, d->d_name);
- }
-
- if (errno != 0)
- error (EXIT_FAILURE, errno, "readdir: %s", dirname);
-
- if (closedir (dir) == -1)
- error (EXIT_FAILURE, errno, "closedir: %s", dirname);
-
- add_string (&entries, &nr_entries, &nr_alloc, NULL);
-
- /* Visit directory entries in order. In febootstrap <= 2.8 we
- * didn't impose any order, but that led to some difficult
- * heisenbugs.
- */
- sort (entries, string_compare);
-
- char path[PATH_MAX];
- char *inputs[] = { path };
- size_t len = strlen (dirname);
-
- if (len + 1 >= PATH_MAX)
- error (EXIT_FAILURE, 0, "%s: directory name too long", __func__);
-
- strcpy (path, dirname);
- path[len++] = '/';
-
- size_t i;
- for (i = 0; entries[i] != NULL; ++i) {
- size_t len2 = strlen (entries[i]);
-
- if (len + 1 + len2 >= PATH_MAX)
- error (EXIT_FAILURE, 0, "%s: path name too long", __func__);
-
- strcpy (&path[len], entries[i]);
-
- iterate_inputs (inputs, 1, writer);
- }
-}
-
-/* Copy kernel modules.
- *
- * Find every file under modpath.
- *
- * Exclude all *.ko files, *except* ones which match names in
- * the whitelist (which may contain wildcards). Include all
- * other files.
- *
- * Add chosen files to the output.
- *
- * whitelist_file may be NULL, to include ALL kernel modules.
- */
-static void
-add_kernel_modules (const char *whitelist_file, const char *modpath,
- struct writer *writer)
-{
- if (verbose)
- print_timestamped_message ("adding kernel modules");
-
- char **whitelist = NULL;
- if (whitelist_file != NULL)
- whitelist = load_file (whitelist_file);
-
- char *paths[2] = { (char *) modpath, NULL };
- FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
- if (fts == NULL)
- error (EXIT_FAILURE, errno, "add_kernel_modules: fts_open: %s", modpath);
-
- for (;;) {
- errno = 0;
- FTSENT *entry = fts_read (fts);
- if (entry == NULL && errno != 0)
- error (EXIT_FAILURE, errno, "add_kernel_modules: fts_read: %s", modpath);
- if (entry == NULL)
- break;
-
- /* Ignore directories being visited in post-order. */
- if (entry->fts_info & FTS_DP)
- continue;
-
- /* Is it a *.ko file? */
- if (entry->fts_namelen >= 3 &&
- entry->fts_name[entry->fts_namelen-3] == '.' &&
- entry->fts_name[entry->fts_namelen-2] == 'k' &&
- entry->fts_name[entry->fts_namelen-1] == 'o') {
- if (whitelist) {
- /* Is it a *.ko file which is on the whitelist? */
- size_t j;
- for (j = 0; whitelist[j] != NULL; ++j) {
- int r;
- r = fnmatch (whitelist[j], entry->fts_name, 0);
- if (r == 0) {
- /* It's on the whitelist, so include it. */
- if (verbose >= 2)
- fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
- entry->fts_name, whitelist[j]);
- writer->wr_fts_entry (entry);
- break;
- } else if (r != FNM_NOMATCH)
- error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
- whitelist[j], entry->fts_name, 0, r);
- } /* for (j) */
- } else { /* whitelist == NULL, always include */
- if (verbose >= 2)
- fprintf (stderr, "including kernel module %s\n", entry->fts_name);
- writer->wr_fts_entry (entry);
- }
- } else
- /* It's some other sort of file, or a directory, always include. */
- writer->wr_fts_entry (entry);
- }
-
- if (fts_close (fts) == -1)
- error (EXIT_FAILURE, errno, "add_kernel_modules: fts_close: %s", modpath);
-}
-
-/* Copy the host files.
- *
- * Read the list of entries in hostfiles (which may contain
- * wildcards). Look them up in the filesystem, and add those files
- * that exist. Ignore any files that don't exist or are not readable.
- */
-static void
-add_hostfiles (const char *hostfiles_file, struct writer *writer)
-{
- char **hostfiles = load_file (hostfiles_file);
-
- /* Hostfiles list can contain "." before each path - ignore it.
- * It also contains each directory name before we enter it. But
- * we don't read that until we see a wildcard for that directory.
- */
- size_t i, j;
- for (i = 0; hostfiles[i] != NULL; ++i) {
- char *hostfile = hostfiles[i];
- if (hostfile[0] == '.')
- hostfile++;
-
- struct stat statbuf;
-
- /* Is it a wildcard? */
- if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
- char *dirname = xstrdup (hostfile);
- char *patt = strrchr (dirname, '/');
- if (!patt)
- error (EXIT_FAILURE, 0, "%s: line %zu: invalid pattern\n(is this file a supermin appliance hostfiles file?)",
- hostfiles_file, i+1);
- *patt++ = '\0';
-
- char **files = read_dir (dirname);
- files = filter_fnmatch (files, patt, FNM_NOESCAPE);
-
- /* Add matching files. */
- for (j = 0; files[j] != NULL; ++j) {
- char *tmp = xasprintf ("%s/%s", dirname, files[j]);
-
- if (verbose >= 2)
- fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
-
- writer->wr_file (tmp);
-
- free (tmp);
- }
- }
- /* Else does this file/directory/whatever exist? */
- else if (lstat (hostfile, &statbuf) == 0) {
- if (verbose >= 2)
- fprintf (stderr, "including host file %s (directly referenced)\n",
- hostfile);
-
- writer->wr_file_stat (hostfile, &statbuf);
- } /* Ignore files that don't exist. */
- }
-}
View
119 helper/checksum.c
@@ -1,119 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <assert.h>
-
-#include "error.h"
-
-#include "helper.h"
-
-static FILE *pp = NULL;
-
-/* This is the command we run to calculate the SHA. Note that we sort
- * the rows first so that the checksum is roughly stable, since the
- * order that we output files might not be (eg. because we rely on the
- * ordering of readdir). Uncomment the second line to see the output
- * before hashing.
- */
-static const char *shacmd = "sort | sha256sum | awk '{print $1}'";
-//static const char *shacmd = "sort | cat";
-
-static void
-checksum_start (const char *hostcpu, const char *appliance,
- const char *modpath, const char *initrd)
-{
- pp = popen (shacmd, "w");
- if (pp == NULL)
- error (EXIT_FAILURE, errno, "popen: command failed: %s", shacmd);
-
- fprintf (pp, "%s %s %s %d\n",
- PACKAGE_STRING, hostcpu, modpath, geteuid ());
-}
-
-static void
-checksum_end (void)
-{
- if (pclose (pp) == -1)
- error (EXIT_FAILURE, errno, "pclose: command failed: %s", shacmd);
- pp = NULL;
-}
-
-static void
-checksum_file_stat (const char *filename, const struct stat *statbuf)
-{
- /* Publically writable directories (ie. /tmp) and special files
- * don't have stable times. Since we only care about some
- * attributes of directories and special files, we vary the output
- * accordingly.
- */
- if (S_ISREG (statbuf->st_mode))
- fprintf (pp, "%s %ld %ld %d %d %" PRIu64 " %o\n",
- filename,
- (long) statbuf->st_ctime, (long) statbuf->st_mtime,
- statbuf->st_uid, statbuf->st_gid, (uint64_t) statbuf->st_size,
- statbuf->st_mode);
- else
- fprintf (pp, "%s %d %d %o\n",
- filename,
- statbuf->st_uid, statbuf->st_gid,
- statbuf->st_mode);
-}
-
-static void
-checksum_file (const char *filename)
-{
- struct stat statbuf;
-
- if (lstat (filename, &statbuf) == -1)
- error (EXIT_FAILURE, errno, "lstat: %s", filename);
- checksum_file_stat (filename, &statbuf);
-}
-
-static void
-checksum_fts_entry (FTSENT *entry)
-{
- if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
- checksum_file (entry->fts_path);
- else
- checksum_file_stat (entry->fts_path, entry->fts_statp);
-}
-
-static void
-checksum_cpio_file (const char *cpio_file)
-{
- checksum_file (cpio_file);
-}
-
-struct writer checksum_writer = {
- .wr_start = checksum_start,
- .wr_end = checksum_end,
- .wr_file = checksum_file,
- .wr_file_stat = checksum_file_stat,
- .wr_fts_entry = checksum_fts_entry,
- .wr_cpio_file = checksum_cpio_file,
-};
View
279 helper/cpio.c
@@ -1,279 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <assert.h>
-
-#include "error.h"
-#include "fts_.h"
-#include "full-write.h"
-#include "xalloc.h"
-#include "xvasprintf.h"
-
-#include "helper.h"
-
-/* Buffer size used in copy operations throughout. Large for
- * greatest efficiency.
- */
-#define BUFFER_SIZE 65536
-
-static int out_fd = -1;
-static off_t out_offset = 0;
-
-static void write_file_to_fd (const char *filename);
-static void write_file_len_to_fd (const char *filename, size_t len);
-static void write_padding (size_t len);
-static void cpio_append_fts_entry (FTSENT *entry);
-static void cpio_append_stat (const char *filename, const struct stat *);
-static void cpio_append (const char *filename);
-static void cpio_append_trailer (void);
-
-/* Copy contents of buffer to out_fd and keep out_offset correct. */
-static void
-write_to_fd (const void *buffer, size_t len)
-{
- if (full_write (out_fd, buffer, len) != len)
- error (EXIT_FAILURE, errno, "write");
- out_offset += len;
-}
-
-/* Copy contents of file to out_fd. */
-static void
-write_file_to_fd (const char *filename)
-{
- char buffer[BUFFER_SIZE];
- int fd2;
- ssize_t r;
-
- if (verbose >= 2)
- fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
-
- fd2 = open (filename, O_RDONLY);
- if (fd2 == -1)
- error (EXIT_FAILURE, errno, "open: %s", filename);
- for (;;) {
- r = read (fd2, buffer, sizeof buffer);
- if (r == 0)
- break;
- if (r == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- error (EXIT_FAILURE, errno, "read: %s", filename);
- }
- write_to_fd (buffer, r);
- }
-
- if (close (fd2) == -1)
- error (EXIT_FAILURE, errno, "close: %s", filename);
-}
-
-/* Copy file of given length to output, and fail if the file has
- * changed size.
- */
-static void
-write_file_len_to_fd (const char *filename, size_t len)
-{
- char buffer[BUFFER_SIZE];
- size_t count = 0;
-
- if (verbose >= 2)
- fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
-
- int fd2 = open (filename, O_RDONLY);
- if (fd2 == -1)
- error (EXIT_FAILURE, errno, "open: %s", filename);
- for (;;) {
- ssize_t r = read (fd2, buffer, sizeof buffer);
- if (r == 0)
- break;
- if (r == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- error (EXIT_FAILURE, errno, "read: %s", filename);
- }
- write_to_fd (buffer, r);
- count += r;
- if (count > len)
- error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
- }
-
- if (close (fd2) == -1)
- error (EXIT_FAILURE, errno, "close: %s", filename);
-
- if (count != len)
- error (EXIT_FAILURE, 0, "supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
-}
-
-/* Append the file pointed to by FTSENT to the cpio output. */
-static void
-cpio_append_fts_entry (FTSENT *entry)
-{
- if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
- cpio_append (entry->fts_path);
- else
- cpio_append_stat (entry->fts_path, entry->fts_statp);
-}
-
-/* Append the file named 'filename' to the cpio output. */
-static void
-cpio_append (const char *filename)
-{
- struct stat statbuf;
-
- if (lstat (filename, &statbuf) == -1)
- error (EXIT_FAILURE, errno, "lstat: %s", filename);
- cpio_append_stat (filename, &statbuf);
-}
-
-/* Append the file to the cpio output. */
-#define PADDING(len) ((((len) + 3) & ~3) - (len))
-
-#define CPIO_HEADER_LEN (6 + 13*8)
-
-static void
-cpio_append_stat (const char *filename, const struct stat *statbuf)
-{
- const char *orig_filename = filename;
-
- if (*filename == '/')
- filename++;
- if (*filename == '\0')
- filename = ".";
-
- if (verbose >= 2)
- fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
- orig_filename, statbuf->st_mode, out_fd);
-
- /* Regular files and symlinks are the only ones that have a "body"
- * in this cpio entry.
- */
- int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
-
- size_t len = strlen (filename) + 1;
-
- char header[CPIO_HEADER_LEN + 1];
- snprintf (header, sizeof header,
- "070701" /* magic */
- "%08X" /* inode */
- "%08X" /* mode */
- "%08X" "%08X" /* uid, gid */
- "%08X" /* nlink */
- "%08X" /* mtime */
- "%08X" /* file length */
- "%08X" "%08X" /* device holding file major, minor */
- "%08X" "%08X" /* for specials, device major, minor */
- "%08X" /* name length (including \0 byte) */
- "%08X", /* checksum (not used by the kernel) */
- (unsigned) statbuf->st_ino, statbuf->st_mode,
- statbuf->st_uid, statbuf->st_gid,
- (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
- has_body ? (unsigned) statbuf->st_size : 0,
- major (statbuf->st_dev), minor (statbuf->st_dev),
- major (statbuf->st_rdev), minor (statbuf->st_rdev),
- (unsigned) len, 0);
-
- /* Write the header. */
- write_to_fd (header, CPIO_HEADER_LEN);
-
- /* Follow with the filename, and pad it. */
- write_to_fd (filename, len);
- size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
- write_padding (padding_len);
-
- /* Follow with the file or symlink content, and pad it. */
- if (has_body) {
- if (S_ISREG (statbuf->st_mode))
- write_file_len_to_fd (orig_filename, statbuf->st_size);
- else if (S_ISLNK (statbuf->st_mode)) {
- char tmp[PATH_MAX];
- if (readlink (orig_filename, tmp, sizeof tmp) == -1)
- error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
- write_to_fd (tmp, statbuf->st_size);
- }
-
- padding_len = PADDING (statbuf->st_size);
- write_padding (padding_len);
- }
-}
-
-/* CPIO voodoo. */
-static void
-cpio_append_trailer (void)
-{
- struct stat statbuf;
- memset (&statbuf, 0, sizeof statbuf);
- statbuf.st_nlink = 1;
- cpio_append_stat ("TRAILER!!!", &statbuf);
-
- /* CPIO seems to pad up to the next block boundary, ie. up to
- * the next 512 bytes.
- */
- write_padding (((out_offset + 511) & ~511) - out_offset);
- assert ((out_offset & 511) == 0);
-}
-
-/* Write 'len' bytes of zeroes out. */
-static void
-write_padding (size_t len)
-{
- static const char buffer[512] = { 0 };
-
- while (len > 0) {
- size_t n = len < sizeof buffer ? len : sizeof buffer;
- write_to_fd (buffer, n);
- len -= n;
- }
-}
-
-static void
-cpio_start (const char *hostcpu, const char *appliance,
- const char *modpath, const char *initrd)
-{
- out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
- if (out_fd == -1)
- error (EXIT_FAILURE, errno, "open: %s", initrd);
- out_offset = 0;
-}
-
-static void
-cpio_end (void)
-{
- cpio_append_trailer ();
-
- /* Finish off and close output file. */
- if (close (out_fd) == -1)
- error (EXIT_FAILURE, errno, "close");
-}
-
-struct writer cpio_writer = {
- .wr_start = cpio_start,
- .wr_end = cpio_end,
- .wr_file = cpio_append,
- .wr_file_stat = cpio_append_stat,
- .wr_fts_entry = cpio_append_fts_entry,
- .wr_cpio_file = write_file_to_fd,
-};
View
538 helper/ext2.c
@@ -1,538 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <assert.h>
-
-#include "error.h"
-#include "fts_.h"
-#include "xvasprintf.h"
-
-#include "helper.h"
-#include "ext2internal.h"
-
-ext2_filsys fs;
-
-/* The ext2 image that we build always has a fixed size, and we 'hope'
- * that the files fit in (otherwise we'll get an error). Note that
- * the file is sparsely allocated.
- *
- * 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 4GB disk the overhead is
- * approximately 66MB.
- *
- * In future, make this configurable, or determine it from the input
- * files (XXX).
- */
-#define APPLIANCE_SIZE ((off_t)4*1024*1024*1024)
-
-static void
-ext2_start (const char *hostcpu, const char *appliance,
- const char *modpath, const char *initrd)
-{
- initialize_ext2_error_table ();
-
- /* Make the initrd. */
- ext2_make_initrd (modpath, initrd);
-
- /* Make the appliance sparse image. */
- int fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
- if (fd == -1)
- error (EXIT_FAILURE, errno, "open: %s", appliance);
-
- if (lseek (fd, APPLIANCE_SIZE - 1, SEEK_SET) == (off_t) -1)
- error (EXIT_FAILURE, errno, "lseek");
-
- char c = 0;
- if (write (fd, &c, 1) != 1)
- error (EXIT_FAILURE, errno, "write");
-
- if (close (fd) == -1)
- error (EXIT_FAILURE, errno, "close");
-
- /* Run mke2fs on the file.
- * XXX Quoting, but this string doesn't come from an untrusted source.
- */
- char *cmd = xasprintf ("%s %s ext2 -F%s '%s'",
- MKE2FS, MKE2FS_T_OPTION,
- verbose >= 2 ? "" : "q",
- appliance);
- int r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0)
- error (EXIT_FAILURE, 0, "%s: failed", cmd);
- free (cmd);
-
- if (verbose)
- print_timestamped_message ("finished mke2fs");
-
- /* Open the filesystem. */
- int fs_flags = EXT2_FLAG_RW;
-#ifdef EXT2_FLAG_64BITS
- fs_flags |= EXT2_FLAG_64BITS;
-#endif
- errcode_t err =
- ext2fs_open (appliance, fs_flags, 0, 0, unix_io_manager, &fs);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_open: %s", error_message (err));
-
- /* Bitmaps are not loaded by default, so load them. ext2fs_close will
- * write out any changes.
- */
- err = ext2fs_read_bitmaps (fs);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_read_bitmaps: %s", error_message (err));
-}
-
-static void
-ext2_end (void)
-{
- if (verbose)
- print_timestamped_message ("closing ext2 filesystem");
-
- /* Write out changes and close. */
- errcode_t err;
-#ifdef HAVE_EXT2FS_CLOSE2
- err = ext2fs_close2 (fs, EXT2_FLAG_FLUSH_NO_SYNC);
-#else
- err = ext2fs_close (fs);
-#endif
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_close: %s", error_message (err));
-}
-
-void
-ext2_mkdir (ext2_ino_t dir_ino, const char *dirname, const char *basename,
- mode_t mode, uid_t uid, gid_t gid,
- time_t ctime, time_t atime, time_t mtime)
-{
- errcode_t err;
-
- mode = LINUX_S_IFDIR | (mode & 03777);
-
- /* Does the directory exist? This is legitimate: we just skip
- * this case.
- */
- ext2_ino_t ino;
- err = ext2fs_namei (fs, EXT2_ROOT_INO, dir_ino, basename, &ino);
- if (err == 0)
- return; /* skip */
-
- /* Otherwise, create it. */
- err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
- 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) {
- /* 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;
- err = ext2fs_read_inode (fs, ino, &inode);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
- inode.i_mode = mode;
- inode.i_uid = uid;
- inode.i_gid = gid;
- inode.i_ctime = ctime;
- inode.i_atime = atime;
- inode.i_mtime = mtime;
- err = ext2fs_write_inode (fs, ino, &inode);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
-}
-
-void
-ext2_empty_inode (ext2_ino_t dir_ino, const char *dirname, const char *basename,
- mode_t mode, uid_t uid, gid_t gid,
- time_t ctime, time_t atime, time_t mtime,
- int major, int minor, int dir_ft, ext2_ino_t *ino_ret)
-{
- errcode_t err;
- struct ext2_inode inode;
- ext2_ino_t ino;
-
- err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
-
- memset (&inode, 0, sizeof inode);
- inode.i_mode = mode;
- inode.i_uid = uid;
- inode.i_gid = gid;
- inode.i_blocks = 0;
- inode.i_links_count = 1;
- inode.i_ctime = ctime;
- inode.i_atime = atime;
- inode.i_mtime = mtime;
- inode.i_size = 0;
- inode.i_block[0] = (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
-
- err = ext2fs_write_new_inode (fs, ino, &inode);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
-
- ext2_link (dir_ino, basename, ino, dir_ft);
-
- ext2fs_inode_alloc_stats2 (fs, ino, 1, 0);
-
- if (ino_ret)
- *ino_ret = ino;
-}
-
-/* You must create the file first with ext2_empty_inode. */
-void
-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: %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.
- */
- unsigned int written;
- err = ext2fs_file_write (file, buf, size, &written);
- if (err != 0)
- 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: %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: %s",
- orig_filename, error_message (err));
- err = ext2fs_file_close (file);
- if (err != 0)
- 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: %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: %s",
- orig_filename, error_message (err));
-}
-
-/* This is just a wrapper around ext2fs_link which calls
- * ext2fs_expand_dir as necessary if the directory fills up. See
- * definition of expand_dir in the sources of debugfs.
- */
-void
-ext2_link (ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft)
-{
- errcode_t err;
-
- again:
- err = ext2fs_link (fs, dir_ino, basename, ino, dir_ft);
-
- if (err == EXT2_ET_DIR_NO_SPACE) {
- err = ext2fs_expand_dir (fs, dir_ino);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2_link: ext2fs_expand_dir: %s: %s",
- basename, error_message (err));
- goto again;
- }
-
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_link: %s: %s",
- basename, error_message (err));
-}
-
-static int
-release_block (ext2_filsys fs, blk_t *blocknr,
- int blockcnt, void *private)
-{
- blk_t block;
-
- block = *blocknr;
- ext2fs_block_alloc_stats (fs, block, -1);
- return 0;
-}
-
-/* unlink or rmdir path, if it exists. */
-void
-ext2_clean_path (ext2_ino_t dir_ino,
- const char *dirname, const char *basename,
- int isdir)
-{
- errcode_t err;
-
- ext2_ino_t ino;
- err = ext2fs_lookup (fs, dir_ino, basename, strlen (basename),
- NULL, &ino);
- if (err == EXT2_ET_FILE_NOT_FOUND)
- return;
-
- if (!isdir) {
- 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));
- inode.i_links_count--;
- err = ext2fs_write_inode (fs, ino, &inode);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
-
- err = ext2fs_unlink (fs, dir_ino, basename, 0, 0);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_unlink_inode: %s", error_message (err));
-
- if (inode.i_links_count == 0) {
- inode.i_dtime = time (NULL);
- err = ext2fs_write_inode (fs, ino, &inode);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
-
- 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);
- }
- }
- /* else it's a directory, what to do? XXX */
-}
-
-/* Read in the whole file into memory. Check the size is still 'size'. */
-static char *
-read_whole_file (const char *filename, size_t size)
-{
- char *buf = malloc (size);
- if (buf == NULL)
- error (EXIT_FAILURE, errno, "malloc");
-
- int fd = open (filename, O_RDONLY);
- if (fd == -1)
- error (EXIT_FAILURE, errno, "open: %s", filename);
-
- size_t n = 0;
- char *p = buf;
-
- while (n < size) {
- ssize_t r = read (fd, p, size - n);
- if (r == -1)
- error (EXIT_FAILURE, errno, "read: %s", filename);
- if (r == 0)
- error (EXIT_FAILURE, 0,
- "error: file has changed size unexpectedly: %s", filename);
- n += r;
- p += r;
- }
-
- if (close (fd) == -1)
- error (EXIT_FAILURE, errno, "close: %s", filename);
-
- return buf;
-}
-
-/* Add a file (or directory etc) from the host. */
-static void
-ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
-{
- errcode_t err;
-
- if (verbose >= 2)
- fprintf (stderr, "ext2_file_stat %s 0%o\n",
- orig_filename, statbuf->st_mode);
-
- /* Sanity check the path. These rules are always true for the paths
- * passed to us here from the appliance layer. The assertions just
- * verify that the rules haven't changed.
- */
- size_t n = strlen (orig_filename);
- assert (n <= PATH_MAX);
- assert (n > 0);
- assert (orig_filename[0] == '/'); /* always absolute path */
- assert (n == 1 || orig_filename[n-1] != '/'); /* no trailing slash */
-
- /* Don't make the root directory, it always exists. This simplifies
- * the code that follows.
- */
- if (n == 1) return;
-
- const char *dirname, *basename;
- const char *p = strrchr (orig_filename, '/');
- ext2_ino_t dir_ino;
- if (orig_filename == p) { /* "/foo" */
- dirname = "/";
- basename = orig_filename+1;
- dir_ino = EXT2_ROOT_INO;
- } else { /* "/foo/bar" */
- 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)
- error (EXIT_FAILURE, 0, "ext2: parent directory not found: %s: %s",
- dirname, error_message (err));
- }
-
- ext2_clean_path (dir_ino, dirname, basename, S_ISDIR (statbuf->st_mode));
-
- int dir_ft;
-
- /* Create regular file. */
- if (S_ISREG (statbuf->st_mode)) {
- /* XXX Hard links get duplicated here. */
- ext2_ino_t ino;
- ext2_empty_inode (dir_ino, dirname, basename,
- statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
- statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
- 0, 0, EXT2_FT_REG_FILE, &ino);
-
- if (statbuf->st_size > 0) {
- char *buf = read_whole_file (orig_filename, statbuf->st_size);
- ext2_write_file (ino, buf, statbuf->st_size, orig_filename);
- free (buf);
- }
- }
- /* Create a symlink. */
- else if (S_ISLNK (statbuf->st_mode)) {
- ext2_ino_t ino;
- ext2_empty_inode (dir_ino, dirname, basename,
- statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
- statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
- 0, 0, EXT2_FT_SYMLINK, &ino);
-
- char buf[PATH_MAX+1];
- 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, orig_filename);
- }
- /* Create directory. */
- else if (S_ISDIR (statbuf->st_mode))
- ext2_mkdir (dir_ino, dirname, basename,
- statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
- statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime);
- /* Create a special file. */
- else if (S_ISBLK (statbuf->st_mode)) {
- dir_ft = EXT2_FT_BLKDEV;
- goto make_special;
- }
- else if (S_ISCHR (statbuf->st_mode)) {
- dir_ft = EXT2_FT_CHRDEV;
- goto make_special;
- } else if (S_ISFIFO (statbuf->st_mode)) {
- dir_ft = EXT2_FT_FIFO;
- goto make_special;
- } else if (S_ISSOCK (statbuf->st_mode)) {
- dir_ft = EXT2_FT_SOCK;
- make_special:
- ext2_empty_inode (dir_ino, dirname, basename,
- statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
- statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
- major (statbuf->st_rdev), minor (statbuf->st_rdev),
- dir_ft, NULL);
- }
-}
-
-static void
-ext2_file (const char *filename)
-{
- struct stat statbuf;
-
- if (lstat (filename, &statbuf) == -1)
- error (EXIT_FAILURE, errno, "lstat: %s", filename);
- ext2_file_stat (filename, &statbuf);
-}
-
-/* In theory this could be optimized to avoid a namei lookup, but
- * it probably wouldn't make much difference.
- */
-static void
-ext2_fts_entry (FTSENT *entry)
-{
- if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
- ext2_file (entry->fts_path);
- else
- ext2_file_stat (entry->fts_path, entry->fts_statp);
-}
-
-struct writer ext2_writer = {
- .wr_start = ext2_start,
- .wr_end = ext2_end,
- .wr_file = ext2_file,
- .wr_file_stat = ext2_file_stat,
- .wr_fts_entry = ext2_fts_entry,
- .wr_cpio_file = ext2_cpio_file,
-};
View
418 helper/ext2cpio.c
@@ -1,418 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013, 2012 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <assert.h>
-
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-#endif
-
-#include "error.h"
-
-#include "helper.h"
-#include "ext2internal.h"
-
-/* This function must unpack the cpio file and add the files it
- * contains to the ext2 filesystem. Essentially this is doing the
- * same thing as the kernel init/initramfs.c code. Note that we
- * assume that the cpio is 'newc' format and can't/won't deal with
- * anything else. All this cpio parsing code is copied to some extent
- * from init/initramfs.c in the kernel.
- */
-#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
-
-static unsigned long cpio_ino, nlink;
-static mode_t mode;
-static unsigned long body_len, name_len;
-static uid_t uid;
-static gid_t gid;
-static time_t mtime;
-static int dev_major, dev_minor, rdev_major, rdev_minor;
-static loff_t curr, next_header;
-#ifdef HAVE_ZLIB
-static gzFile gzfp;
-#else
-static FILE *fp;
-#endif
-static const char *input_file;
-
-static int xread (void *buffer, size_t size);
-static void parse_header (char *s);
-static int parse_next_entry (void);
-static void skip_to_next_header (void);
-static void read_file (void);
-static char *read_whole_body (void);
-static ext2_ino_t maybe_link (void);
-static void add_link (ext2_ino_t real_ino);
-static void clear_links (void);
-
-void
-ext2_cpio_file (const char *cpio_file)
-{
- /* Save this for error messages in xread. */
- input_file = cpio_file;
-
-#ifdef HAVE_ZLIB
- gzfp = gzopen (cpio_file, "rb");
- if (gzfp == NULL)
- error (EXIT_FAILURE, errno, "open: %s", cpio_file);
-#else
- fp = fopen (cpio_file, "r");
- if (fp == NULL)
- error (EXIT_FAILURE, errno, "open: %s", cpio_file);
-#endif
-
- curr = 0;
- while (parse_next_entry ())
- ;
-
-#ifdef HAVE_ZLIB
- gzclose (gzfp);
-#else
- fclose (fp);
-#endif
-}
-
-/* Read 'size' bytes from the handle and write it into 'buffer'. If
- * the read fails or there is a partial read, exit with an error. If
- * end of file, return 0. If the full data was read, return > 0.
- */
-static int
-xread (void *buffer, size_t size)
-{
-#ifdef HAVE_ZLIB
- int r;
-
- r = gzread (gzfp, buffer, size);
- if (r == -1) {
- int errnum;
- const char *errstr;
-
- errstr = gzerror (gzfp, &errnum);
- error (EXIT_FAILURE, 0, "gzread: %s: %s (%d)", input_file, errstr, errnum);
- }
- if (r == 0)
- return 0;
- if ((size_t) r < size)
- error (EXIT_FAILURE, 0, "gzread: %s: unexpected end of file", input_file);
-#else
- clearerr (fp);
- if (fread (buffer, size, 1, fp) != 1) {
- if (feof (fp))
- return 0;
- error (EXIT_FAILURE, errno, "fread: %s: read failure", input_file);
- }
-#endif
- return 1;
-}
-
-static int
-parse_next_entry (void)
-{
- char header[110];
-
- /* Skip padding and synchronize with the next header. */
- again:
- if (xread (&header[0], 4) == 0)
- return 0;
-
- curr += 4;
- if (memcmp (header, "\0\0\0\0", 4) == 0)
- goto again;
-
- /* Read the rest of the header field. */
- if (xread (&header[4], sizeof header - 4) == 0)
- error (EXIT_FAILURE, errno, "%s: unexpected end of file reading cpio file",
- input_file);
- curr += sizeof header - 4;
-
- if (verbose >= 2) {
- char header2[sizeof header + 1];
- memcpy (header2, header, sizeof header);
- header2[sizeof header] = '\0';
- fprintf (stderr, "cpio header %s\n", header2);
- }
-
- if (memcmp (header, "070707", 6) == 0)
- error (EXIT_FAILURE, 0, "incorrect cpio method: use -H newc option");
- if (memcmp (header, "070701", 6) != 0)
- error (EXIT_FAILURE, 0, "input is not a cpio file");
-
- parse_header (header);
-
- next_header = curr + N_ALIGN(name_len) + body_len;
- next_header = (next_header + 3) & ~3;
- if (name_len <= 0 || name_len > PATH_MAX)
- skip_to_next_header ();
- else if (S_ISLNK (mode)) {
- if (body_len <= 0 || body_len > PATH_MAX)
- skip_to_next_header ();
- else
- read_file ();
- }
- else if (!S_ISREG (mode) && body_len > 0)
- skip_to_next_header (); /* only regular files have bodies */
- else
- read_file (); /* could be file, directory, block special, ... */
-
- return 1;
-}
-
-static void
-parse_header (char *s)
-{
- unsigned long parsed[12];
- char buf[9];
- int i;
-
- buf[8] = '\0';
- for (i = 0, s += 6; i < 12; i++, s += 8) {
- memcpy (buf, s, 8);
- parsed[i] = strtoul (buf, NULL, 16);
- }
- cpio_ino = parsed[0]; /* fake inode number from cpio file */
- mode = parsed[1];
- uid = parsed[2];
- gid = parsed[3];
- nlink = parsed[4];
- mtime = parsed[5];
- body_len = parsed[6];
- dev_major = parsed[7];
- dev_minor = parsed[8];
- rdev_major = parsed[9];
- rdev_minor = parsed[10];
- name_len = parsed[11];
-}
-
-static void
-skip_to_next_header (void)
-{
- char buf[65536];
-
- while (curr < next_header) {
- size_t bytes = (size_t) (next_header - curr);
- if (bytes > sizeof buf)
- bytes = sizeof buf;
- if (xread (buf, bytes) == 0)
- error (EXIT_FAILURE, errno, "%s: unexpected end of cpio file",
- input_file);
- curr += bytes;
- }
-}
-
-/* Read any sort of file. The body will only be present for
- * regular files and symlinks.
- */
-static void
-read_file (void)
-{
- errcode_t err;
- int dir_ft;
- char name[N_ALIGN(name_len)+1]; /* asserted above this is <= PATH_MAX */
-
- if (xread (name, N_ALIGN(name_len)) == 0)
- error (EXIT_FAILURE, errno, "%s: unexpected end of file reading name field in cpio file",
- input_file);
- curr += N_ALIGN(name_len);
-
- name[name_len] = '\0';
-
- if (verbose >= 2)
- fprintf (stderr, "ext2 read_file %s %o\n", name, mode);
-
- if (strcmp (name, "TRAILER!!!") == 0) {
- clear_links ();
- goto skip;
- }
-
- /* The name will be something like "bin/ls" or "./bin/ls". It won't
- * (ever?) be an absolute path. Skip leading parts, and if it refers
- * to the root directory just skip it entirely.
- */
- char *dirname = name, *basename;
- if (*dirname == '.')
- dirname++;
- if (*dirname == '/')
- dirname++;
- if (*dirname == '\0')
- goto skip;
-
- ext2_ino_t dir_ino;
- basename = strrchr (dirname, '/');
- if (basename == NULL) {
- basename = dirname;
- dir_ino = EXT2_ROOT_INO;
- } else {
- *basename++ = '\0';
-
- /* Look up the parent directory. */
- err = ext2fs_namei (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, dirname, &dir_ino);
- if (err != 0)
- error (EXIT_FAILURE, 0, "ext2: parent directory not found: %s: %s",
- dirname, error_message (err));
- }
-
- if (verbose >= 2)
- fprintf (stderr, "ext2 read_file dirname %s basename %s\n",
- dirname, basename);
-
- ext2_clean_path (dir_ino, dirname, basename, S_ISDIR (mode));
-
- /* Create a regular file. */
- if (S_ISREG (mode)) {
- ext2_ino_t ml = maybe_link ();
- ext2_ino_t ino;
- if (ml <= 1) {
- ext2_empty_inode (dir_ino, dirname, basename,
- mode, uid, gid, mtime, mtime, mtime,
- 0, 0, EXT2_FT_REG_FILE, &ino);
- if (ml == 1)
- add_link (ino);
- }
- else /* ml >= 2 */ {
- /* It's a hard link back to a previous file. */
- ino = ml;
- ext2_link (dir_ino, basename, ino, EXT2_FT_REG_FILE);
- }
-
- if (body_len) {
- char *buf = read_whole_body ();
- ext2_write_file (ino, buf, body_len, name);
- free (buf);
- }
- }
- /* Create a symlink. */
- else if (S_ISLNK (mode)) {
- ext2_ino_t ino;
- ext2_empty_inode (dir_ino, dirname, basename,
- mode, uid, gid, mtime, mtime, mtime,
- 0, 0, EXT2_FT_SYMLINK, &ino);
-
- char *buf = read_whole_body ();
- ext2_write_file (ino, buf, body_len, name);
- free (buf);
- }
- /* Create a directory. */
- else if (S_ISDIR (mode)) {
- ext2_mkdir (dir_ino, dirname, basename,
- mode, uid, gid, mtime, mtime, mtime);
- }
- /* Create a special file. */
- else if (S_ISBLK (mode)) {
- dir_ft = EXT2_FT_BLKDEV;
- goto make_special;
- }
- else if (S_ISCHR (mode)) {
- dir_ft = EXT2_FT_CHRDEV;
- goto make_special;
- } else if (S_ISFIFO (mode)) {
- dir_ft = EXT2_FT_FIFO;
- goto make_special;
- } else if (S_ISSOCK (mode)) {
- dir_ft = EXT2_FT_SOCK;
- make_special:
- /* Just like the kernel, we ignore special files with nlink > 1. */
- if (maybe_link () == 0)
- ext2_empty_inode (dir_ino, dirname, basename,
- mode, uid, gid, mtime, mtime, mtime,
- rdev_major, rdev_minor, dir_ft, NULL);
- }
-
- skip:
- skip_to_next_header ();
-}
-
-static char *
-read_whole_body (void)
-{
- char *buf = malloc (body_len);
- if (buf == NULL)
- error (EXIT_FAILURE, errno, "malloc");
-
- if (xread (buf, body_len) == 0)
- error (EXIT_FAILURE, errno, "%s: unexpected end of file reading body in cpio file",
- input_file);
- curr += body_len;
-
- return buf;
-}
-
-struct links {
- struct links *next;
- unsigned long cpio_ino; /* fake ino from cpio file */
- int minor;
- int major;
- ext2_ino_t real_ino; /* real inode number on ext2 filesystem */
-};
-static struct links *links_head = NULL;
-
-/* If it's a hard link, return the linked inode number in the real
- * ext2 filesystem.
- *
- * Returns: 0 = not a hard link
- * 1 = possible unresolved hard link
- * inode number = resolved hard link to this inode
- */
-static ext2_ino_t
-maybe_link (void)
-{
- if (nlink >= 2) {
- struct links *p;
- for (p = links_head; p; p = p->next) {
- if (p->cpio_ino != cpio_ino)
- continue;
- if (p->minor != dev_minor)
- continue;
- if (p->major != dev_major)
- continue;
- return p->real_ino;
- }
- return 1;
- }
-
- return 0;
-}
-
-static void
-add_link (ext2_ino_t real_ino)
-{
- struct links *p = malloc (sizeof (*p));
- p->cpio_ino = cpio_ino;
- p->minor = dev_minor;
- p->major = dev_major;
- p->real_ino = real_ino;
- p->next = links_head;
- links_head = p;
-}
-
-static void
-clear_links (void)
-{
- /* Don't bother to free the linked list in this short-lived program. */
- links_head = NULL;
-}
View
281 helper/ext2initrd.c
@@ -1,281 +0,0 @@
-/* supermin-helper reimplementation in C.
- * Copyright (C) 2009-2013 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* ext2 requires a small initrd in order to boot. This builds it. */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <errno.h>
-#include <assert.h>
-#include <fnmatch.h>
-
-#include "error.h"
-#include "full-write.h"
-#include "xalloc.h"
-#include "xvasprintf.h"
-
-#include "helper.h"
-#include "ext2internal.h"
-
-static void read_module_deps (const char *modpath);
-static void free_module_deps (void);
-static void add_module_dep (const char *name, const char *dep);
-static struct module * add_module (const char *name);
-static struct module * find_module (const char *name);
-static void print_module_load_order (FILE *f, FILE *pp, struct module *m);
-
-/* The init binary. */
-extern char _binary_init_start, _binary_init_end, _binary_init_size;
-
-/* The list of modules (wildcards) we consider for inclusion in the
- * mini initrd. Only what is needed in order to find a device with an
- * ext2 filesystem on it.
- */
-static const char *kmods[] = {
- "ext2.ko*",
- "ext4.ko*", /* CONFIG_EXT4_USE_FOR_EXT23=y option might be set */
- "virtio*.ko*",
- "ide*.ko*",
- "libata*.ko*",
- "piix*.ko*",
- "scsi_transport_spi.ko*",
- "scsi_mod.ko*",
- "sd_mod.ko*",
- "sym53c8xx.ko*",
- "ata_piix.ko*",
- "sr_mod.ko*",
- "mbcache.ko*",
- "crc*.ko*",
- "libcrc*.ko*",
- "ibmvscsic.ko*",
- "megaraid*.ko*",
- NULL
-};
-
-/* Module dependencies. */
-struct module {
- struct module *next;
- struct moddep *deps;
- char *name;
- int visited;
-};
-struct module *modules = NULL;
-
-struct moddep {
- struct moddep *next;
- struct module *dep;
-};
-
-void
-ext2_make_initrd (const char *modpath, const char *initrd)
-{
- char dir[] = "/tmp/ext2initrdXXXXXX";
- if (mkdtemp (dir) == NULL)
- error (EXIT_FAILURE, errno, "mkdtemp");
-
- read_module_deps (modpath);
- add_module ("");
- int i;
- struct module *m;
- for (i = 0; kmods[i] != NULL; ++i) {
- for (m = modules; m; m = m->next) {
- char *n = strrchr (m->name, '/');
- if (n)
- n += 1;
- else
- n = m->name;
- if (fnmatch (kmods[i], n, FNM_PATHNAME) == 0) {
- if (verbose >= 2)
- fprintf (stderr, "Adding top-level dependency %s (%s)\n", m->name, kmods[i]);
- add_module_dep ("", m->name);
- }
- }
- }
-
- char *cmd = xasprintf ("cd %s; xargs cp -t %s", modpath, dir);
- char *outfile = xasprintf ("%s/modules", dir);
- if (verbose >= 2) fprintf (stderr, "writing to %s\n", cmd);
-
- FILE *f = fopen (outfile, "w");
- if (f == NULL)
- error (EXIT_FAILURE, errno, "failed to create modules list (%s)", outfile);
- free (outfile);
- FILE *pp = popen (cmd, "w");
- if (pp == NULL)
- error (EXIT_FAILURE, errno, "failed to create pipe (%s)", cmd);
-
- /* The "pseudo" module depends on all modules matched by the contents of kmods */
- struct module *pseudo = find_module ("");
- print_module_load_order (pp, f, pseudo);
- fclose (pp);
- pclose (f);
-
- free (cmd);
- free_module_deps ();
-
- /* Copy in the init program, linked into this program as a data blob. */
- char *init = xasprintf ("%s/init", dir);
- int fd = open (init, O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0755);
- if (fd == -1)
- error (EXIT_FAILURE, errno, "open: %s", init);
-
- size_t n = (size_t) &_binary_init_size;
- if (full_write (fd, &_binary_init_start, n) != n)
- error (EXIT_FAILURE, errno, "write: %s", init);
-
- if (close (fd) == -1)
- error (EXIT_FAILURE, errno, "close: %s", init);
-
- free (init);
-
- /* Build the cpio file. */
- cmd = xasprintf ("(cd %s && (echo . ; ls -1)"
- " | cpio --quiet -o -H newc) > '%s'",
- dir, initrd);
- if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
- int r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0)
- error (EXIT_FAILURE, 0, "ext2_make_initrd: cpio failed");
- free (cmd);
-
- /* Construction of 'dir' above ensures this is safe. */
- cmd = xasprintf ("rm -rf %s", dir);
- if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
- system (cmd);
- free (cmd);
-}
-
-static void
-free_module_deps (void)
-{
- /* Short-lived program, don't bother to free it. */
- modules = NULL;
-}
-
-/* Read modules.dep into internal structure. */
-static void
-read_module_deps (const char *modpath)
-{
- free_module_deps ();
-
- char *filename = xasprintf ("%s/modules.dep", modpath);
- FILE *fp = fopen (filename, "r");
- free (filename);
- if (fp == NULL)
- error (EXIT_FAILURE, errno, "open: %s/modules.dep", modpath);
-
- char *line = NULL;
- size_t llen = 0;
- ssize_t len;
- while ((len = getline (&line, &llen, fp)) != -1) {
- if (len > 0 && line[len-1] == '\n')
- line[--len] = '\0';
-
- char *name = strtok (line, ": ");
- if (!name) continue;
-
- add_module (name);
- char *dep;
- while ((dep = strtok (NULL, " ")) != NULL) {
- add_module_dep (name, dep);
- }
- }
-
- free (line);
- fclose (fp);
-}
-
-static struct module *
-add_module (const char *name)
-{
- struct module *m = find_module (name);
- if (m)
- return m;
- m = xmalloc (sizeof *m);
- m->name = xstrdup (name);
- m->deps = NULL;
- m->next = modules;
- m->visited = 0;
- modules = m;
- return m;
-}
-
-static struct module *
-find_module (const char *name)
-{
- struct module *m;
- for (m = modules; m; m = m->next) {
- if (strcmp (name, m->name) == 0)
- break;
- }
- return m;