mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Rewrite virt-make-fs in C (originally Perl).
It should be very compatible with the Perl version.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -282,6 +282,9 @@ Makefile.in
|
||||
/m4/ltsugar.m4
|
||||
/m4/ltversion.m4
|
||||
/maint.mk
|
||||
/make-fs/stamp-virt-make-fs.pod
|
||||
/make-fs/virt-make-fs
|
||||
/make-fs/virt-make-fs.1
|
||||
/missing
|
||||
/mllib/.depend
|
||||
/mllib/common_gettext.ml
|
||||
|
||||
@@ -74,7 +74,7 @@ SUBDIRS += test-tool
|
||||
SUBDIRS += fish
|
||||
|
||||
# virt-tools in C.
|
||||
SUBDIRS += align cat diff df edit format inspector rescue
|
||||
SUBDIRS += align cat diff df edit format inspector make-fs rescue
|
||||
|
||||
# bash-completion
|
||||
SUBDIRS += bash
|
||||
|
||||
@@ -1714,6 +1714,7 @@ AC_CONFIG_FILES([Makefile
|
||||
java/examples/Makefile
|
||||
lua/Makefile
|
||||
lua/examples/Makefile
|
||||
make-fs/Makefile
|
||||
mllib/Makefile
|
||||
mllib/config.ml
|
||||
ocaml/META
|
||||
|
||||
82
make-fs/Makefile.am
Normal file
82
make-fs/Makefile.am
Normal file
@@ -0,0 +1,82 @@
|
||||
# libguestfs virt-diff
|
||||
# Copyright (C) 2010-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-virt-make-fs.sh \
|
||||
virt-make-fs.pod
|
||||
|
||||
CLEANFILES = stamp-virt-make-fs.pod
|
||||
|
||||
bin_PROGRAMS = virt-make-fs
|
||||
|
||||
SHARED_SOURCE_FILES = \
|
||||
../fish/options.h \
|
||||
../fish/options.c \
|
||||
../fish/domain.c \
|
||||
../fish/uri.c
|
||||
|
||||
virt_make_fs_SOURCES = \
|
||||
$(SHARED_SOURCE_FILES) \
|
||||
make-fs.c
|
||||
|
||||
virt_make_fs_CPPFLAGS = \
|
||||
-DGUESTFS_WARN_DEPRECATED=1 \
|
||||
-DLOCALEBASEDIR=\""$(datadir)/locale"\" \
|
||||
-I$(top_srcdir)/src -I$(top_builddir)/src \
|
||||
-I$(top_srcdir)/fish \
|
||||
-I$(srcdir)/../gnulib/lib -I../gnulib/lib
|
||||
|
||||
virt_make_fs_CFLAGS = \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
$(GPROF_CFLAGS) $(GCOV_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS)
|
||||
|
||||
virt_make_fs_LDADD = \
|
||||
$(top_builddir)/src/libutils.la \
|
||||
$(top_builddir)/src/libguestfs.la \
|
||||
$(LIBXML2_LIBS) \
|
||||
../gnulib/lib/libgnu.la
|
||||
|
||||
# Manual pages and HTML files for the website.
|
||||
man_MANS = virt-make-fs.1
|
||||
|
||||
noinst_DATA = \
|
||||
$(top_builddir)/html/virt-make-fs.1.html
|
||||
|
||||
virt-make-fs.1 $(top_builddir)/html/virt-make-fs.1.html: stamp-virt-make-fs.pod
|
||||
|
||||
stamp-virt-make-fs.pod: virt-make-fs.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-make-fs.1 \
|
||||
--html $(top_builddir)/html/virt-make-fs.1.html \
|
||||
--license GPLv2+ \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
# Tests.
|
||||
|
||||
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
if ENABLE_APPLIANCE
|
||||
TESTS = \
|
||||
test-virt-make-fs.sh
|
||||
endif ENABLE_APPLIANCE
|
||||
|
||||
check-valgrind:
|
||||
$(MAKE) VG="$(top_builddir)/run @VG@" check
|
||||
854
make-fs/make-fs.c
Normal file
854
make-fs/make-fs.c
Normal file
@@ -0,0 +1,854 @@
|
||||
/* virt-make-fs
|
||||
* Copyright (C) 2010-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <assert.h>
|
||||
#include <libintl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "guestfs.h"
|
||||
#include "guestfs-internal-frontend.h"
|
||||
|
||||
#include "xstrtol.h"
|
||||
|
||||
#include "options.h"
|
||||
|
||||
guestfs_h *g;
|
||||
const char *libvirt_uri;
|
||||
int live;
|
||||
int read_only;
|
||||
int verbose;
|
||||
|
||||
static const char *format = "raw", *label = NULL,
|
||||
*partition = NULL, *size_str = NULL, *type = "ext2";
|
||||
|
||||
enum { HELP_OPTION = CHAR_MAX + 1 };
|
||||
static const char *options = "F:s:t:Vvx";
|
||||
static const struct option long_options[] = {
|
||||
{ "debug", 0, 0, 'v' }, /* for compat with Perl tool */
|
||||
{ "floppy", 0, 0, 0 },
|
||||
{ "format", 1, 0, 'F' },
|
||||
{ "help", 0, 0, HELP_OPTION },
|
||||
{ "label", 1, 0, 0 },
|
||||
{ "long-options", 0, 0, 0 },
|
||||
{ "partition", 2, 0, 0 },
|
||||
{ "size", 1, 0, 's' },
|
||||
{ "type", 1, 0, 't' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static void __attribute__((noreturn))
|
||||
usage (int status)
|
||||
{
|
||||
if (status != EXIT_SUCCESS)
|
||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
||||
program_name);
|
||||
else {
|
||||
fprintf (stdout,
|
||||
_("%s: make a filesystem from a tar archive or files\n"
|
||||
"Copyright (C) 2010-2014 Red Hat Inc.\n"
|
||||
"Usage:\n"
|
||||
" %s [--options] input.tar output.img\n"
|
||||
" %s [--options] input.tar.gz output.img\n"
|
||||
" %s [--options] directory output.img\n"
|
||||
"Options:\n"
|
||||
" --floppy Make a virtual floppy disk\n"
|
||||
" --format=raw|qcow2|.. Set output format\n"
|
||||
" --help Display brief help\n"
|
||||
" --label=label Filesystem label\n"
|
||||
" --partition=mbr|gpt|.. Set partition type\n"
|
||||
" --size=size|+size Set size of output disk\n"
|
||||
" --type=ext4|.. Set filesystem type\n"
|
||||
" -v|--verbose Verbose messages\n"
|
||||
" -V|--version Display version and exit\n"
|
||||
" -x Trace libguestfs API calls\n"
|
||||
"For more information, see the manpage %s(1).\n"),
|
||||
program_name, program_name, program_name, program_name,
|
||||
program_name);
|
||||
}
|
||||
exit (status);
|
||||
}
|
||||
|
||||
static int do_make_fs (const char *input, const char *output_str);
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int option_index;
|
||||
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEBASEDIR);
|
||||
textdomain (PACKAGE);
|
||||
|
||||
g = guestfs_create ();
|
||||
if (g == NULL) {
|
||||
fprintf (stderr, _("guestfs_create: failed to create handle\n"));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
c = getopt_long (argc, argv, options, long_options, &option_index);
|
||||
if (c == -1) break;
|
||||
|
||||
switch (c) {
|
||||
case 0: /* options which are long only */
|
||||
if (STREQ (long_options[option_index].name, "long-options")) {
|
||||
display_long_options (long_options);
|
||||
}
|
||||
else if (STREQ (long_options[option_index].name, "floppy")) {
|
||||
size_str = "1440K";
|
||||
partition = "mbr";
|
||||
type = "vfat";
|
||||
}
|
||||
else if (STREQ (long_options[option_index].name, "label")) {
|
||||
label = optarg;
|
||||
}
|
||||
else if (STREQ (long_options[option_index].name, "partition")) {
|
||||
partition = optarg;
|
||||
} else {
|
||||
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
|
||||
program_name, long_options[option_index].name, option_index);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
format = optarg;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
size_str = optarg;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
type = optarg;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
OPTION_v;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
OPTION_V;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
OPTION_x;
|
||||
break;
|
||||
|
||||
case HELP_OPTION:
|
||||
usage (EXIT_SUCCESS);
|
||||
|
||||
default:
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind + 2 != argc) {
|
||||
fprintf (stderr, _("%s: missing input and output arguments on the command line\n"),
|
||||
program_name);
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (do_make_fs (argv[optind], argv[optind+1]) == -1)
|
||||
exit (EXIT_FAILURE);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
check_ntfs_available (void)
|
||||
{
|
||||
const char *ntfs_features[] = { "ntfs3g", "ntfsprogs", NULL };
|
||||
|
||||
if (STREQ (type, "ntfs") &&
|
||||
guestfs_feature_available (g, (char **) ntfs_features) == 0) {
|
||||
fprintf (stderr, _("%s: NTFS support was disabled when libguestfs was compiled\n"),
|
||||
program_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Execute a command, sending output to a file. */
|
||||
static int
|
||||
exec_command (char **argv, const char *file)
|
||||
{
|
||||
pid_t pid;
|
||||
int status, fd;
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
perror ("fork");
|
||||
return -1;
|
||||
}
|
||||
if (pid > 0) {
|
||||
if (waitpid (pid, &status, 0) == -1) {
|
||||
perror ("waitpid");
|
||||
return -1;
|
||||
}
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
|
||||
fprintf (stderr, _("%s: %s command failed\n"), program_name, argv[0]);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Child process. */
|
||||
fd = open (file, O_WRONLY|O_NOCTTY);
|
||||
if (fd == -1) {
|
||||
perror (file);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
dup2 (fd, 1);
|
||||
close (fd);
|
||||
|
||||
execvp (argv[0], argv);
|
||||
perror ("execvp");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Execute a command, counting the amount of bytes output. */
|
||||
static int
|
||||
exec_command_count_output (char **argv, uint64_t *bytes_rtn)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int fd[2];
|
||||
char buffer[BUFSIZ];
|
||||
ssize_t r;
|
||||
|
||||
if (pipe (fd) == -1) {
|
||||
perror ("pipe");
|
||||
return -1;
|
||||
}
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
perror ("fork");
|
||||
return -1;
|
||||
}
|
||||
if (pid > 0) {
|
||||
close (fd[1]);
|
||||
|
||||
/* Read output from the subprocess and count the length. */
|
||||
while ((r = read (fd[0], buffer, sizeof buffer)) > 0) {
|
||||
*bytes_rtn += r;
|
||||
}
|
||||
if (r == -1) {
|
||||
perror ("read");
|
||||
return -1;
|
||||
}
|
||||
close (fd[0]);
|
||||
|
||||
if (waitpid (pid, &status, 0) == -1) {
|
||||
perror ("waitpid");
|
||||
return -1;
|
||||
}
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
|
||||
fprintf (stderr, _("%s: %s command failed\n"), program_name, argv[0]);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Child process. */
|
||||
close (fd[0]);
|
||||
dup2 (fd[1], 1);
|
||||
close (fd[1]);
|
||||
|
||||
execvp (argv[0], argv);
|
||||
perror ("execvp");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Execute a command in the background (don't wait) and send the output
|
||||
* to a file.
|
||||
*/
|
||||
static int
|
||||
bg_command (char **argv, const char *file)
|
||||
{
|
||||
pid_t pid;
|
||||
int fd;
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
perror ("fork");
|
||||
return -1;
|
||||
}
|
||||
if (pid > 0)
|
||||
/* Return immediately in the parent without waiting. */
|
||||
return 0;
|
||||
|
||||
/* Child process. */
|
||||
fd = open (file, O_WRONLY|O_NOCTTY);
|
||||
if (fd == -1) {
|
||||
perror (file);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
dup2 (fd, 1);
|
||||
close (fd);
|
||||
|
||||
execvp (argv[0], argv);
|
||||
perror ("execvp");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static char *
|
||||
create_pipe (void)
|
||||
{
|
||||
char *tmppipe;
|
||||
|
||||
if (asprintf (&tmppipe, "/tmp/makefsXXXXXX") == -1) {
|
||||
perror ("asprintf");
|
||||
return NULL;
|
||||
}
|
||||
if (mkstemp (tmppipe) == -1) {
|
||||
perror (tmppipe);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert the temporary file into a pipe. */
|
||||
unlink (tmppipe);
|
||||
if (mkfifo (tmppipe, 0600) == -1) {
|
||||
perror ("mkfifo");
|
||||
return NULL;
|
||||
}
|
||||
return tmppipe;
|
||||
}
|
||||
|
||||
/* Estimate the size of the input. This returns the estimated size
|
||||
* (in bytes) of the input. It also sets ifmt to the format of the
|
||||
* input, either the string "directory" if the input is a directory,
|
||||
* or the output of the "file" command on the input.
|
||||
*
|
||||
* Estimation is a Hard Problem. Some factors which make it hard:
|
||||
*
|
||||
* - Superblocks, block free bitmaps, FAT and other fixed overhead
|
||||
* - Indirect blocks (ext2, ext3), and extents
|
||||
* - Journal size
|
||||
* - Internal fragmentation of files
|
||||
*
|
||||
* What we could also do is try shrinking the filesystem after
|
||||
* creating and populating it, but that is complex given partitions.
|
||||
*/
|
||||
static int
|
||||
estimate_input (const char *input, uint64_t *estimate_rtn, char **ifmt_rtn)
|
||||
{
|
||||
struct stat statbuf;
|
||||
const char *argv[6];
|
||||
CLEANUP_UNLINK_FREE char *tmpfile = NULL;
|
||||
FILE *fp;
|
||||
char line[256];
|
||||
size_t len;
|
||||
|
||||
if (asprintf (&tmpfile, "/tmp/makefsXXXXXX") == -1) {
|
||||
perror ("asprintf");
|
||||
return -1;
|
||||
}
|
||||
if (mkstemp (tmpfile) == -1) {
|
||||
perror (tmpfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stat (input, &statbuf) == -1) {
|
||||
perror (input);
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR (statbuf.st_mode)) {
|
||||
*ifmt_rtn = strdup ("directory");
|
||||
if (*ifmt_rtn == NULL) {
|
||||
perror ("strdup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[0] = "du";
|
||||
argv[1] = "--apparent-size";
|
||||
argv[2] = "-b";
|
||||
argv[3] = "-s";
|
||||
argv[4] = input;
|
||||
argv[5] = NULL;
|
||||
|
||||
if (exec_command ((char **) argv, tmpfile) == -1)
|
||||
return -1;
|
||||
|
||||
fp = fopen (tmpfile, "r");
|
||||
if (fp == NULL) {
|
||||
perror (tmpfile);
|
||||
return -1;
|
||||
}
|
||||
if (fgets (line, sizeof line, fp) == NULL) {
|
||||
perror ("fgets");
|
||||
return -1;
|
||||
}
|
||||
fclose (fp);
|
||||
|
||||
if (sscanf (line, "%" SCNu64, estimate_rtn) != 1) {
|
||||
fprintf (stderr, _("%s: cannot parse the output of 'du' command: %s\n"),
|
||||
program_name, line);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
argv[0] = "file";
|
||||
argv[1] = "-bsLz";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
|
||||
if (exec_command ((char **) argv, tmpfile) == -1)
|
||||
return -1;
|
||||
|
||||
fp = fopen (tmpfile, "r");
|
||||
if (fp == NULL) {
|
||||
perror (tmpfile);
|
||||
return -1;
|
||||
}
|
||||
if (fgets (line, sizeof line, fp) == NULL) {
|
||||
perror ("fgets");
|
||||
return -1;
|
||||
}
|
||||
fclose (fp);
|
||||
|
||||
len = strlen (line);
|
||||
if (len > 0 && line[len-1] == '\n')
|
||||
line[len-1] = '\0';
|
||||
|
||||
*ifmt_rtn = strdup (line);
|
||||
if (*ifmt_rtn == NULL) {
|
||||
perror ("strdup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strstr (line, "tar archive") == NULL) {
|
||||
fprintf (stderr, _("%s: %s: input is not a directory, tar archive or compressed tar achive\n"),
|
||||
program_name, input);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strstr (line, "compress")) {
|
||||
if (strstr (line, "compress'd")) {
|
||||
argv[0] = "uncompress";
|
||||
argv[1] = "-c";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (line, "gzip compressed")) {
|
||||
argv[0] = "gzip";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (line, "bzip2 compressed")) {
|
||||
argv[0] = "bzip2";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (line, "xz compressed")) {
|
||||
argv[0] = "xz";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else {
|
||||
fprintf (stderr, _("%s: %s: unknown compressed input format (%s)\n"),
|
||||
program_name, input, line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*estimate_rtn = 0;
|
||||
if (exec_command_count_output ((char **) argv, estimate_rtn) == -1)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* Plain tar file, just get the size directly. Tar files have
|
||||
* a 512 byte block size (compared with typically 1K or 4K for
|
||||
* filesystems) so this isn't very accurate.
|
||||
*/
|
||||
*estimate_rtn = statbuf.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare the input source. If the input is a regular tar file, this
|
||||
* just sets ifile = input. However normally the input will be either
|
||||
* a directory or a compressed tarball. In that case we set up an
|
||||
* external command to do the tar/uncompression to a temporary pipe,
|
||||
* and set ifile to the name of the pipe.
|
||||
*/
|
||||
static int
|
||||
prepare_input (const char *input, const char *ifmt,
|
||||
char **ifile_rtn, int *ifile_delete_on_exit)
|
||||
{
|
||||
char *tmppipe;
|
||||
const char *argv[7];
|
||||
|
||||
if (STREQ (ifmt, "directory")) {
|
||||
tmppipe = create_pipe ();
|
||||
if (tmppipe == NULL)
|
||||
return -1;
|
||||
argv[0] = "tar";
|
||||
argv[1] = "-C";
|
||||
argv[2] = input;
|
||||
argv[3] = "-cf";
|
||||
argv[4] = "-";
|
||||
argv[5] = ".";
|
||||
argv[6] = NULL;
|
||||
|
||||
if (bg_command ((char **) argv, tmppipe) == -1) {
|
||||
unlink (tmppipe);
|
||||
free (tmppipe);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ifile_rtn = tmppipe;
|
||||
*ifile_delete_on_exit = 1;
|
||||
}
|
||||
else {
|
||||
if (strstr (ifmt, "compress")) {
|
||||
tmppipe = create_pipe ();
|
||||
if (tmppipe == NULL)
|
||||
return -1;
|
||||
if (strstr (ifmt, "compress'd")) {
|
||||
argv[0] = "uncompress";
|
||||
argv[1] = "-c";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (ifmt, "gzip compressed")) {
|
||||
argv[0] = "gzip";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (ifmt, "bzip2 compressed")) {
|
||||
argv[0] = "bzip2";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else if (strstr (ifmt, "xz compressed")) {
|
||||
argv[0] = "xz";
|
||||
argv[1] = "-cd";
|
||||
argv[2] = input;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else
|
||||
/* Shouldn't happen - see estimate_input above. */
|
||||
abort ();
|
||||
|
||||
if (bg_command ((char **) argv, tmppipe) == -1) {
|
||||
unlink (tmppipe);
|
||||
free (tmppipe);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ifile_rtn = tmppipe;
|
||||
*ifile_delete_on_exit = 1;
|
||||
}
|
||||
else {
|
||||
/* Plain tar file, read directly from the file. */
|
||||
*ifile_rtn = strdup (input);
|
||||
if (*ifile_rtn == NULL) {
|
||||
perror ("strdup");
|
||||
return -1;
|
||||
}
|
||||
*ifile_delete_on_exit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Adapted from fish/alloc.c */
|
||||
static int
|
||||
parse_size (const char *str, uint64_t estimate, uint64_t *size_rtn)
|
||||
{
|
||||
unsigned long long size;
|
||||
strtol_error xerr;
|
||||
int plus = 0;
|
||||
|
||||
assert (str);
|
||||
|
||||
if (str[0] == '+') {
|
||||
plus = 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
xerr = xstrtoull (str, NULL, 0, &size, "0kKMGTPEZY");
|
||||
if (xerr != LONGINT_OK) {
|
||||
fprintf (stderr,
|
||||
_("%s: %s: invalid size parameter '%s' (%s returned %d)\n"),
|
||||
program_name, "parse_size", str, "xstrtoull", xerr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (plus)
|
||||
*size_rtn = estimate + size;
|
||||
else
|
||||
*size_rtn = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_make_fs (const char *input, const char *output_str)
|
||||
{
|
||||
const char *dev, *options;
|
||||
CLEANUP_UNLINK_FREE char *output = NULL;
|
||||
uint64_t estimate, size;
|
||||
struct guestfs_disk_create_argv optargs;
|
||||
CLEANUP_FREE char *ifmt = NULL;
|
||||
CLEANUP_FREE char *ifile = NULL;
|
||||
int ifile_delete_on_exit, r;
|
||||
|
||||
/* Use of CLEANUP_UNLINK_FREE *output ensures the output file is
|
||||
* deleted unless we successfully reach the end of this function.
|
||||
*/
|
||||
output = strdup (output_str);
|
||||
if (output == NULL) {
|
||||
perror ("strdup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Input. What is it? Estimate how much space it will need. */
|
||||
if (estimate_input (input, &estimate, &ifmt) == -1)
|
||||
return -1;
|
||||
|
||||
if (verbose) {
|
||||
fprintf (stderr, "input format = %s\n", ifmt);
|
||||
fprintf (stderr, "estimate = %" PRIu64 " bytes "
|
||||
"(%" PRIu64 " 1K blocks, %" PRIu64 " 4K blocks)\n",
|
||||
estimate, estimate / 1024, estimate / 4096);
|
||||
}
|
||||
|
||||
estimate += 256 * 1024; /* For superblocks &c. */
|
||||
|
||||
if (STRPREFIX (type, "ext") && type[3] >= '3') {
|
||||
/* For ext3+, add some more for the journal. */
|
||||
estimate += 1024 * 1024;
|
||||
}
|
||||
|
||||
else if (STREQ (type, "ntfs")) {
|
||||
estimate += 4 * 1024 * 1024; /* NTFS journal. */
|
||||
}
|
||||
|
||||
else if (STREQ (type, "btrfs")) {
|
||||
/* For BTRFS, the minimum metadata allocation is 256MB, with data
|
||||
* additional to that. Note that we disable data and metadata
|
||||
* duplication below.
|
||||
*/
|
||||
estimate += 256 * 1024 * 1024;
|
||||
}
|
||||
|
||||
/* Add 10%, see above. */
|
||||
estimate *= 1.10;
|
||||
|
||||
/* Calculate the output size. */
|
||||
if (size_str == NULL)
|
||||
size = estimate;
|
||||
else
|
||||
if (parse_size (size_str, estimate, &size) == -1)
|
||||
return -1;
|
||||
|
||||
/* Create the output disk. */
|
||||
optargs.bitmask = 0;
|
||||
if (STREQ (format, "qcow2")) {
|
||||
optargs.bitmask |= GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK;
|
||||
optargs.preallocation = "metadata";
|
||||
}
|
||||
if (guestfs_disk_create_argv (g, output, format, size, &optargs) == -1)
|
||||
return -1;
|
||||
|
||||
if (guestfs_add_drive_opts (g, output,
|
||||
GUESTFS_ADD_DRIVE_OPTS_FORMAT, format,
|
||||
-1) == -1)
|
||||
return -1;
|
||||
|
||||
if (guestfs_launch (g) == -1)
|
||||
return -1;
|
||||
|
||||
if (check_ntfs_available () == -1)
|
||||
return -1;
|
||||
|
||||
/* Partition the disk. */
|
||||
dev = "/dev/sda";
|
||||
if (partition) {
|
||||
int mbr_id = 0;
|
||||
|
||||
if (STREQ (partition, ""))
|
||||
partition = "mbr";
|
||||
|
||||
if (guestfs_part_disk (g, dev, partition) == -1)
|
||||
return -1;
|
||||
|
||||
dev = "/dev/sda1";
|
||||
|
||||
/* Set the partition type byte if it's MBR and the filesystem type
|
||||
* is one that we know about.
|
||||
*/
|
||||
if (STREQ (partition, "mbr") || STREQ (partition, "msdos")) {
|
||||
if (STREQ (type, "msdos"))
|
||||
/* According to Wikipedia. However I have not actually tried this. */
|
||||
mbr_id = 0x1;
|
||||
else if (STREQ (type, "vfat") || STREQ (type, "fat"))
|
||||
mbr_id = 0xb;
|
||||
else if (STREQ (type, "ntfs"))
|
||||
mbr_id = 0x7;
|
||||
else if (STRPREFIX (type, "ext"))
|
||||
mbr_id = 0x83;
|
||||
else if (STREQ (type, "minix"))
|
||||
mbr_id = 0x81;
|
||||
}
|
||||
if (mbr_id != 0) {
|
||||
if (guestfs_part_set_mbr_id (g, "/dev/sda", 1, mbr_id) == -1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
fprintf (stderr, "creating %s filesystem on %s ...\n", type, dev);
|
||||
|
||||
/* Create the filesystem. */
|
||||
if (STRNEQ (type, "btrfs")) {
|
||||
int r;
|
||||
|
||||
guestfs_push_error_handler (g, NULL, NULL);
|
||||
r = guestfs_mkfs (g, type, dev);
|
||||
guestfs_pop_error_handler (g);
|
||||
|
||||
if (r == -1) {
|
||||
/* Provide more guidance in the error message (RHBZ#823883). */
|
||||
fprintf (stderr, "%s: 'mkfs' (create filesystem) operation failed.\n",
|
||||
program_name);
|
||||
if (STREQ (type, "fat"))
|
||||
fprintf (stderr, "Instead of 'fat', try 'vfat' (long filenames) or 'msdos' (short filenames).\n");
|
||||
else
|
||||
fprintf (stderr, "Is '%s' a correct filesystem type?\n", type);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *devs[] = { dev, NULL };
|
||||
|
||||
if (guestfs_mkfs_btrfs (g, (char **) devs,
|
||||
GUESTFS_MKFS_BTRFS_DATATYPE, "single",
|
||||
GUESTFS_MKFS_BTRFS_METADATA, "single",
|
||||
-1) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set label. */
|
||||
if (label) {
|
||||
if (guestfs_set_label (g, dev, label) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Mount it. */
|
||||
|
||||
/* For vfat, add the utf8 mount option because we want to be able to
|
||||
* encode any non-ASCII characters into UCS2 which is what modern
|
||||
* vfat uses on disk (RHBZ#823885).
|
||||
*/
|
||||
if (STREQ (type, "vfat"))
|
||||
options = "utf8";
|
||||
else
|
||||
options = "";
|
||||
|
||||
if (guestfs_mount_options (g, options, dev, "/") == -1)
|
||||
return -1;
|
||||
|
||||
/* For debugging, print statvfs before and after doing the tar-in. */
|
||||
if (verbose) {
|
||||
CLEANUP_FREE_STATVFS struct guestfs_statvfs *stats =
|
||||
guestfs_statvfs (g, "/");
|
||||
fprintf (stderr, "before uploading:\n");
|
||||
fprintf (stderr, " bsize = %" PRIi64 "\n", stats->bsize);
|
||||
fprintf (stderr, " frsize = %" PRIi64 "\n", stats->frsize);
|
||||
fprintf (stderr, " blocks = %" PRIi64 "\n", stats->blocks);
|
||||
fprintf (stderr, " bfree = %" PRIi64 "\n", stats->bfree);
|
||||
fprintf (stderr, " bavail = %" PRIi64 "\n", stats->bavail);
|
||||
fprintf (stderr, " files = %" PRIi64 "\n", stats->files);
|
||||
fprintf (stderr, " ffree = %" PRIi64 "\n", stats->ffree);
|
||||
fprintf (stderr, " favail = %" PRIi64 "\n", stats->favail);
|
||||
fprintf (stderr, " fsid = %" PRIi64 "\n", stats->fsid);
|
||||
fprintf (stderr, " flag = %" PRIi64 "\n", stats->flag);
|
||||
fprintf (stderr, " namemax = %" PRIi64 "\n", stats->namemax);
|
||||
}
|
||||
|
||||
/* Prepare the input to be copied in. */
|
||||
if (prepare_input (input, ifmt, &ifile, &ifile_delete_on_exit) == -1)
|
||||
return -1;
|
||||
|
||||
if (verbose)
|
||||
fprintf (stderr, "uploading from %s to / ...\n", ifile);
|
||||
r = guestfs_tar_in (g, ifile, "/");
|
||||
if (ifile_delete_on_exit)
|
||||
unlink (ifile);
|
||||
if (r == -1)
|
||||
return -1;
|
||||
|
||||
if (verbose) {
|
||||
CLEANUP_FREE_STATVFS struct guestfs_statvfs *stats =
|
||||
guestfs_statvfs (g, "/");
|
||||
fprintf (stderr, "after uploading:\n");
|
||||
fprintf (stderr, " bsize = %" PRIi64 "\n", stats->bsize);
|
||||
fprintf (stderr, " frsize = %" PRIi64 "\n", stats->frsize);
|
||||
fprintf (stderr, " blocks = %" PRIi64 "\n", stats->blocks);
|
||||
fprintf (stderr, " bfree = %" PRIi64 "\n", stats->bfree);
|
||||
fprintf (stderr, " bavail = %" PRIi64 "\n", stats->bavail);
|
||||
fprintf (stderr, " files = %" PRIi64 "\n", stats->files);
|
||||
fprintf (stderr, " ffree = %" PRIi64 "\n", stats->ffree);
|
||||
fprintf (stderr, " favail = %" PRIi64 "\n", stats->favail);
|
||||
fprintf (stderr, " fsid = %" PRIi64 "\n", stats->fsid);
|
||||
fprintf (stderr, " flag = %" PRIi64 "\n", stats->flag);
|
||||
fprintf (stderr, " namemax = %" PRIi64 "\n", stats->namemax);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
fprintf (stderr, "finishing off\n");
|
||||
if (guestfs_shutdown (g) == -1)
|
||||
return -1;
|
||||
guestfs_close (g);
|
||||
|
||||
/* Output was created OK, so save it from being deleted by
|
||||
* CLEANUP_UNLINK_FREE.
|
||||
*/
|
||||
free (output);
|
||||
output = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2010-2012 Red Hat Inc.
|
||||
# Copyright (C) 2010-2014 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
|
||||
@@ -16,7 +16,9 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# Engage in some montecarlo testing of virt-make-fs.
|
||||
# Engage in some montecarlo testing of virt-make-fs. This test is
|
||||
# copied from the original Perl tool virt-make-fs, on the basis that
|
||||
# the new C tool should be able to pass the same tests.
|
||||
|
||||
export LANG=C
|
||||
set -e
|
||||
249
make-fs/virt-make-fs.pod
Normal file
249
make-fs/virt-make-fs.pod
Normal file
@@ -0,0 +1,249 @@
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
virt-make-fs - Make a filesystem from a tar archive or files
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
virt-make-fs [--options] input.tar output.img
|
||||
|
||||
virt-make-fs [--options] input.tar.gz output.img
|
||||
|
||||
virt-make-fs [--options] directory output.img
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Virt-make-fs is a command line tool for creating a filesystem from a
|
||||
tar archive or some files in a directory. It is similar to tools like
|
||||
L<mkisofs(1)>, L<genisoimage(1)> and L<mksquashfs(1)>. Unlike those
|
||||
tools, it can create common filesystem types like ext2/3 or NTFS,
|
||||
which can be useful if you want to attach these filesystems to
|
||||
existing virtual machines (eg. to import large amounts of read-only
|
||||
data to a VM).
|
||||
|
||||
To create blank disks, use L<virt-format(1)>. To create complex
|
||||
layouts, use L<guestfish(1)>.
|
||||
|
||||
Basic usage is:
|
||||
|
||||
virt-make-fs input output.img
|
||||
|
||||
where C<input> is either a directory containing files that you want to
|
||||
add, or a tar archive (either uncompressed tar or gzip-compressed
|
||||
tar); and C<output.img> is a disk image. The input type is detected
|
||||
automatically. The output disk image defaults to a raw ext2 sparse
|
||||
image unless you specify extra flags (see L</OPTIONS> below).
|
||||
|
||||
=head2 FILESYSTEM TYPE
|
||||
|
||||
The default filesystem type is C<ext2>. Just about any filesystem
|
||||
type that libguestfs supports can be used (but I<not> read-only
|
||||
formats like ISO9660). Here are some of the more common choices:
|
||||
|
||||
=over 4
|
||||
|
||||
=item I<ext3>
|
||||
|
||||
Note that ext3 filesystems contain a journal, typically 1-32 MB in size.
|
||||
If you are not going to use the filesystem in a way that requires the
|
||||
journal, then this is just wasted overhead.
|
||||
|
||||
=item I<ntfs> or I<vfat>
|
||||
|
||||
Useful if exporting data to a Windows guest.
|
||||
|
||||
=item I<minix>
|
||||
|
||||
Lower overhead than C<ext2>, but certain limitations on filename
|
||||
length and total filesystem size.
|
||||
|
||||
=back
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
virt-make-fs --type=minix input minixfs.img
|
||||
|
||||
=head2 TO PARTITION OR NOT TO PARTITION
|
||||
|
||||
Optionally virt-make-fs can add a partition table to the output disk.
|
||||
|
||||
Adding a partition can make the disk image more compatible with
|
||||
certain virtualized operating systems which don't expect to see a
|
||||
filesystem directly located on a block device (Linux doesn't care and
|
||||
will happily handle both types).
|
||||
|
||||
On the other hand, if you have a partition table then the output image
|
||||
is no longer a straight filesystem. For example you cannot run
|
||||
L<fsck(8)> directly on a partitioned disk image. (However libguestfs
|
||||
tools such as L<guestfish(1)> and L<virt-resize(1)> can still be
|
||||
used).
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
Add an MBR partition:
|
||||
|
||||
virt-make-fs --partition -- input disk.img
|
||||
|
||||
If the output disk image could be terabyte-sized or larger, it's
|
||||
better to use an EFI/GPT-compatible partition table:
|
||||
|
||||
virt-make-fs --partition=gpt --size=+4T --format=qcow2 input disk.img
|
||||
|
||||
=head2 EXTRA SPACE
|
||||
|
||||
Unlike formats such as tar and squashfs, a filesystem does not "just
|
||||
fit" the files that it contains, but might have extra space.
|
||||
Depending on how you are going to use the output, you might think this
|
||||
extra space is wasted and want to minimize it, or you might want to
|
||||
leave space so that more files can be added later. Virt-make-fs
|
||||
defaults to minimizing the extra space, but you can use the I<--size>
|
||||
flag to leave space in the filesystem if you want it.
|
||||
|
||||
An alternative way to leave extra space but not make the output image
|
||||
any bigger is to use an alternative disk image format (instead of the
|
||||
default "raw" format). Using I<--format=qcow2> will use the native
|
||||
QEmu/KVM qcow2 image format (check your hypervisor supports this
|
||||
before using it). This allows you to choose a large I<--size> but the
|
||||
extra space won't actually be allocated in the image until you try to
|
||||
store something in it.
|
||||
|
||||
Don't forget that you can also use local commands including
|
||||
L<resize2fs(8)> and L<virt-resize(1)> to resize existing filesystems,
|
||||
or rerun virt-make-fs to build another image from scratch.
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
virt-make-fs --format=qcow2 --size=+200M input output.img
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display brief help.
|
||||
|
||||
=item B<--floppy>
|
||||
|
||||
Create a virtual floppy disk.
|
||||
|
||||
Currently this preselects the size (1440K), partition type (MBR) and
|
||||
filesystem type (VFAT). In future it may also choose the geometry.
|
||||
|
||||
=item B<--size=E<lt>NE<gt>>
|
||||
|
||||
=item B<--size=+E<lt>NE<gt>>
|
||||
|
||||
=item B<-s E<lt>NE<gt>>
|
||||
|
||||
=item B<-s +E<lt>NE<gt>>
|
||||
|
||||
Use the I<--size> (or I<-s>) option to choose the size of the output
|
||||
image.
|
||||
|
||||
If this option is I<not> given, then the output image will be just
|
||||
large enough to contain all the files, with not much wasted space.
|
||||
|
||||
To choose a fixed size output disk, specify an absolute number
|
||||
followed by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes,
|
||||
Gigabytes, Terabytes, Petabytes or Exabytes. This must be large
|
||||
enough to contain all the input files, else you will get an error.
|
||||
|
||||
To leave extra space, specify C<+> (plus sign) and a number followed
|
||||
by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes, Gigabytes,
|
||||
Terabytes, Petabytes or Exabytes. For example: I<--size=+200M> means
|
||||
enough space for the input files, and (approximately) an extra 200 MB
|
||||
free space.
|
||||
|
||||
Note that virt-make-fs estimates free space, and therefore will not
|
||||
produce filesystems containing precisely the free space requested.
|
||||
(It is much more expensive and time-consuming to produce a filesystem
|
||||
which has precisely the desired free space).
|
||||
|
||||
=item B<--format=E<lt>fmtE<gt>>
|
||||
|
||||
=item B<-F E<lt>fmtE<gt>>
|
||||
|
||||
Choose the output disk image format.
|
||||
|
||||
The default is C<raw> (raw sparse disk image).
|
||||
|
||||
=item B<--type=E<lt>fsE<gt>>
|
||||
|
||||
=item B<-t E<lt>fsE<gt>>
|
||||
|
||||
Choose the output filesystem type.
|
||||
|
||||
The default is C<ext2>.
|
||||
|
||||
Any filesystem which is supported read-write by libguestfs can be used
|
||||
here.
|
||||
|
||||
=item B<--label=E<lt>LABELE<gt>>
|
||||
|
||||
Set the filesystem label.
|
||||
|
||||
=item B<--partition>
|
||||
|
||||
=item B<--partition=E<lt>parttypeE<gt>>
|
||||
|
||||
If specified, this flag adds an MBR partition table to the output disk
|
||||
image.
|
||||
|
||||
You can change the partition table type, eg. I<--partition=gpt> for
|
||||
large disks.
|
||||
|
||||
Note that if you just use a lonesome I<--partition>, the option parser
|
||||
might consider the next parameter to be the partition type. For
|
||||
example:
|
||||
|
||||
virt-make-fs --partition input.tar output.img
|
||||
|
||||
would cause virt-make-fs to think you wanted to use a partition type
|
||||
of C<input.tar> which is completely wrong. To avoid this, use I<-->
|
||||
(a double dash) between options and the input and output arguments:
|
||||
|
||||
virt-make-fs --partition -- input.tar output.img
|
||||
|
||||
For MBR, virt-make-fs sets the partition type byte automatically.
|
||||
|
||||
=item B<-v>
|
||||
|
||||
=item B<--verbose>
|
||||
|
||||
Enable debugging information.
|
||||
|
||||
=item B<-V>
|
||||
|
||||
=item B<--version>
|
||||
|
||||
Display version number and exit.
|
||||
|
||||
=item B<-x>
|
||||
|
||||
Enable libguestfs trace.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<guestfish(1)>,
|
||||
L<virt-format(1)>,
|
||||
L<virt-resize(1)>,
|
||||
L<virt-tar-in(1)>,
|
||||
L<mkisofs(1)>,
|
||||
L<genisoimage(1)>,
|
||||
L<mksquashfs(1)>,
|
||||
L<mke2fs(8)>,
|
||||
L<resize2fs(8)>,
|
||||
L<guestfs(3)>,
|
||||
L<http://libguestfs.org/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2010-2014 Red Hat Inc.
|
||||
@@ -235,6 +235,7 @@ gobject/src/tristate.c
|
||||
inspector/inspector.c
|
||||
java/com_redhat_et_libguestfs_GuestFS.c
|
||||
lua/lua-guestfs.c
|
||||
make-fs/make-fs.c
|
||||
mllib/crypt-c.c
|
||||
mllib/fsync-c.c
|
||||
mllib/progress-c.c
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
tools/virt-list-filesystems
|
||||
tools/virt-list-partitions
|
||||
tools/virt-make-fs
|
||||
tools/virt-tar
|
||||
tools/virt-win-reg
|
||||
|
||||
2
run.in
2
run.in
@@ -74,7 +74,7 @@ fi
|
||||
|
||||
# Set the PATH to contain all the libguestfs binaries. There are a
|
||||
# lot of binaries, so a lot of path entries.
|
||||
PATH="$b/align:$b/builder:$b/cat:$b/df:$b/diff:$b/edit:$b/erlang:$b/fish:$b/format:$b/fuse:$b/rescue:$b/resize:$b/sparsify:$b/sysprep:$b/test-tool:$b/tools:$PATH"
|
||||
PATH="$b/align:$b/builder:$b/cat:$b/df:$b/diff:$b/edit:$b/erlang:$b/fish:$b/format:$b/fuse:$b/make-fs:$b/rescue:$b/resize:$b/sparsify:$b/sysprep:$b/test-tool:$b/tools:$PATH"
|
||||
export PATH
|
||||
|
||||
# Set LD_LIBRARY_PATH to contain library.
|
||||
|
||||
@@ -4335,6 +4335,10 @@ Logo used on the website. The fish is called Arthur by the way.
|
||||
|
||||
M4 macros used by autoconf.
|
||||
|
||||
=item C<make-fs>
|
||||
|
||||
L<virt-make-fs(1)> command and documentation.
|
||||
|
||||
=item C<mllib>
|
||||
|
||||
Various libraries and common code used by L<virt-resize(1)> and
|
||||
|
||||
@@ -20,7 +20,6 @@ include $(top_srcdir)/subdir-rules.mk
|
||||
tools = \
|
||||
list-filesystems \
|
||||
list-partitions \
|
||||
make-fs \
|
||||
tar \
|
||||
win-reg
|
||||
|
||||
@@ -62,7 +61,6 @@ TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
if ENABLE_APPLIANCE
|
||||
TESTS = test-virt-list-filesystems.sh \
|
||||
test-virt-make-fs.sh \
|
||||
test-virt-tar.sh
|
||||
endif ENABLE_APPLIANCE
|
||||
|
||||
|
||||
@@ -1,632 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# virt-make-fs
|
||||
# Copyright (C) 2010-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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Sys::Guestfs;
|
||||
|
||||
use Pod::Usage;
|
||||
use Getopt::Long;
|
||||
use File::Temp qw(tempfile tempdir);
|
||||
use POSIX qw(mkfifo floor);
|
||||
use Data::Dumper;
|
||||
use String::ShellQuote qw(shell_quote);
|
||||
use Locale::TextDomain 'libguestfs';
|
||||
use Fcntl qw(SEEK_SET);
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
virt-make-fs - Make a filesystem from a tar archive or files
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
virt-make-fs [--options] input.tar output.img
|
||||
|
||||
virt-make-fs [--options] input.tar.gz output.img
|
||||
|
||||
virt-make-fs [--options] directory output.img
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Virt-make-fs is a command line tool for creating a filesystem from a
|
||||
tar archive or some files in a directory. It is similar to tools like
|
||||
L<mkisofs(1)>, L<genisoimage(1)> and L<mksquashfs(1)>. Unlike those
|
||||
tools, it can create common filesystem types like ext2/3 or NTFS,
|
||||
which can be useful if you want to attach these filesystems to
|
||||
existing virtual machines (eg. to import large amounts of read-only
|
||||
data to a VM).
|
||||
|
||||
To create blank disks, use L<virt-format(1)>. To create complex
|
||||
layouts, use L<guestfish(1)>.
|
||||
|
||||
Basic usage is:
|
||||
|
||||
virt-make-fs input output.img
|
||||
|
||||
where C<input> is either a directory containing files that you want to
|
||||
add, or a tar archive (either uncompressed tar or gzip-compressed
|
||||
tar); and C<output.img> is a disk image. The input type is detected
|
||||
automatically. The output disk image defaults to a raw ext2 sparse
|
||||
image unless you specify extra flags (see L</OPTIONS> below).
|
||||
|
||||
=head2 FILESYSTEM TYPE
|
||||
|
||||
The default filesystem type is C<ext2>. Just about any filesystem
|
||||
type that libguestfs supports can be used (but I<not> read-only
|
||||
formats like ISO9660). Here are some of the more common choices:
|
||||
|
||||
=over 4
|
||||
|
||||
=item I<ext3>
|
||||
|
||||
Note that ext3 filesystems contain a journal, typically 1-32 MB in size.
|
||||
If you are not going to use the filesystem in a way that requires the
|
||||
journal, then this is just wasted overhead.
|
||||
|
||||
=item I<ntfs> or I<vfat>
|
||||
|
||||
Useful if exporting data to a Windows guest.
|
||||
|
||||
=item I<minix>
|
||||
|
||||
Lower overhead than C<ext2>, but certain limitations on filename
|
||||
length and total filesystem size.
|
||||
|
||||
=back
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
virt-make-fs --type=minix input minixfs.img
|
||||
|
||||
=head2 TO PARTITION OR NOT TO PARTITION
|
||||
|
||||
Optionally virt-make-fs can add a partition table to the output disk.
|
||||
|
||||
Adding a partition can make the disk image more compatible with
|
||||
certain virtualized operating systems which don't expect to see a
|
||||
filesystem directly located on a block device (Linux doesn't care and
|
||||
will happily handle both types).
|
||||
|
||||
On the other hand, if you have a partition table then the output image
|
||||
is no longer a straight filesystem. For example you cannot run
|
||||
L<fsck(8)> directly on a partitioned disk image. (However libguestfs
|
||||
tools such as L<guestfish(1)> and L<virt-resize(1)> can still be
|
||||
used).
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
Add an MBR partition:
|
||||
|
||||
virt-make-fs --partition -- input disk.img
|
||||
|
||||
If the output disk image could be terabyte-sized or larger, it's
|
||||
better to use an EFI/GPT-compatible partition table:
|
||||
|
||||
virt-make-fs --partition=gpt --size=+4T --format=qcow2 input disk.img
|
||||
|
||||
=head2 EXTRA SPACE
|
||||
|
||||
Unlike formats such as tar and squashfs, a filesystem does not "just
|
||||
fit" the files that it contains, but might have extra space.
|
||||
Depending on how you are going to use the output, you might think this
|
||||
extra space is wasted and want to minimize it, or you might want to
|
||||
leave space so that more files can be added later. Virt-make-fs
|
||||
defaults to minimizing the extra space, but you can use the I<--size>
|
||||
flag to leave space in the filesystem if you want it.
|
||||
|
||||
An alternative way to leave extra space but not make the output image
|
||||
any bigger is to use an alternative disk image format (instead of the
|
||||
default "raw" format). Using I<--format=qcow2> will use the native
|
||||
QEmu/KVM qcow2 image format (check your hypervisor supports this
|
||||
before using it). This allows you to choose a large I<--size> but the
|
||||
extra space won't actually be allocated in the image until you try to
|
||||
store something in it.
|
||||
|
||||
Don't forget that you can also use local commands including
|
||||
L<resize2fs(8)> and L<virt-resize(1)> to resize existing filesystems,
|
||||
or rerun virt-make-fs to build another image from scratch.
|
||||
|
||||
=head3 EXAMPLE
|
||||
|
||||
virt-make-fs --format=qcow2 --size=+200M input output.img
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=cut
|
||||
|
||||
my $help;
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display brief help.
|
||||
|
||||
=cut
|
||||
|
||||
my $version;
|
||||
|
||||
=item B<--version>
|
||||
|
||||
Display version number and exit.
|
||||
|
||||
=cut
|
||||
|
||||
my $debug;
|
||||
|
||||
=item B<--debug>
|
||||
|
||||
Enable debugging information.
|
||||
|
||||
=cut
|
||||
|
||||
=item B<--floppy>
|
||||
|
||||
Create a virtual floppy disk.
|
||||
|
||||
Currently this preselects the size (1440K), partition type (MBR) and
|
||||
filesystem type (VFAT). In future it may also choose the geometry.
|
||||
|
||||
=cut
|
||||
|
||||
my $size;
|
||||
|
||||
=item B<--size=E<lt>NE<gt>>
|
||||
|
||||
=item B<--size=+E<lt>NE<gt>>
|
||||
|
||||
=item B<-s E<lt>NE<gt>>
|
||||
|
||||
=item B<-s +E<lt>NE<gt>>
|
||||
|
||||
Use the I<--size> (or I<-s>) option to choose the size of the output
|
||||
image.
|
||||
|
||||
If this option is I<not> given, then the output image will be just
|
||||
large enough to contain all the files, with not much wasted space.
|
||||
|
||||
To choose a fixed size output disk, specify an absolute number
|
||||
followed by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes,
|
||||
Gigabytes, Terabytes, Petabytes or Exabytes. This must be large
|
||||
enough to contain all the input files, else you will get an error.
|
||||
|
||||
To leave extra space, specify C<+> (plus sign) and a number followed
|
||||
by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes, Gigabytes,
|
||||
Terabytes, Petabytes or Exabytes. For example: I<--size=+200M> means
|
||||
enough space for the input files, and (approximately) an extra 200 MB
|
||||
free space.
|
||||
|
||||
Note that virt-make-fs estimates free space, and therefore will not
|
||||
produce filesystems containing precisely the free space requested.
|
||||
(It is much more expensive and time-consuming to produce a filesystem
|
||||
which has precisely the desired free space).
|
||||
|
||||
=cut
|
||||
|
||||
my $format = "raw";
|
||||
|
||||
=item B<--format=E<lt>fmtE<gt>>
|
||||
|
||||
=item B<-F E<lt>fmtE<gt>>
|
||||
|
||||
Choose the output disk image format.
|
||||
|
||||
The default is C<raw> (raw sparse disk image).
|
||||
|
||||
=cut
|
||||
|
||||
my $type = "ext2";
|
||||
|
||||
=item B<--type=E<lt>fsE<gt>>
|
||||
|
||||
=item B<-t E<lt>fsE<gt>>
|
||||
|
||||
Choose the output filesystem type.
|
||||
|
||||
The default is C<ext2>.
|
||||
|
||||
Any filesystem which is supported read-write by libguestfs can be used
|
||||
here.
|
||||
|
||||
=cut
|
||||
|
||||
my $label;
|
||||
|
||||
=item B<--label=E<lt>LABELE<gt>>
|
||||
|
||||
Set the filesystem label.
|
||||
|
||||
=cut
|
||||
|
||||
my $partition;
|
||||
|
||||
=item B<--partition>
|
||||
|
||||
=item B<--partition=E<lt>parttypeE<gt>>
|
||||
|
||||
If specified, this flag adds an MBR partition table to the output disk
|
||||
image.
|
||||
|
||||
You can change the partition table type, eg. I<--partition=gpt> for
|
||||
large disks.
|
||||
|
||||
Note that if you just use a lonesome I<--partition>, the Perl option
|
||||
parser might consider the next parameter to be the partition type.
|
||||
For example:
|
||||
|
||||
virt-make-fs --partition input.tar output.img
|
||||
|
||||
would cause virt-make-fs to think you wanted to use a partition type
|
||||
of C<input.tar> which is completely wrong. To avoid this, use I<-->
|
||||
(a double dash) between options and the input and output arguments:
|
||||
|
||||
virt-make-fs --partition -- input.tar output.img
|
||||
|
||||
For MBR, virt-make-fs sets the partition type byte automatically.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
GetOptions ("help|?" => \$help,
|
||||
"version" => \$version,
|
||||
"debug" => \$debug,
|
||||
"floppy" => sub {
|
||||
$size = "1440K";
|
||||
$partition = "mbr";
|
||||
$type = "vfat";
|
||||
},
|
||||
"s|size=s" => \$size,
|
||||
"F|format=s" => \$format,
|
||||
"t|type=s" => \$type,
|
||||
"label=s" => \$label,
|
||||
"partition:s" => \$partition,
|
||||
) or pod2usage (2);
|
||||
pod2usage (1) if $help;
|
||||
if ($version) {
|
||||
my $g = Sys::Guestfs->new ();
|
||||
my %h = $g->version ();
|
||||
print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
|
||||
exit
|
||||
}
|
||||
|
||||
die __"virt-make-fs input output\n" if @ARGV != 2;
|
||||
|
||||
my $input = $ARGV[0];
|
||||
my $output = $ARGV[1];
|
||||
|
||||
# Input. What is it? Estimate how much space it will need.
|
||||
#
|
||||
# Estimation is a Hard Problem. Some factors which make it hard:
|
||||
#
|
||||
# - Superblocks, block free bitmaps, FAT and other fixed overhead
|
||||
# - Indirect blocks (ext2, ext3), and extents
|
||||
# - Journal size
|
||||
# - Internal fragmentation of files
|
||||
#
|
||||
# What we could also do is try shrinking the filesystem after creating
|
||||
# and populating it, but that is complex given partitions.
|
||||
|
||||
my $estimate; # Estimated size required (in bytes).
|
||||
my $ifmt; # Input format.
|
||||
|
||||
if (-d $input) {
|
||||
$ifmt = "directory";
|
||||
|
||||
my @cmd = ("du", "--apparent-size", "-b", "-s", $input);
|
||||
open PIPE, "-|", @cmd or die "du $input: $!";
|
||||
|
||||
$_ = <PIPE>;
|
||||
if (/^(\d+)/) {
|
||||
$estimate = $1;
|
||||
} else {
|
||||
die __"unexpected output from 'du' command";
|
||||
}
|
||||
} else {
|
||||
local $ENV{LANG} = "C";
|
||||
my @cmd = ("file", "-bsLz", $input);
|
||||
open PIPE, "-|", @cmd or die "file $input: $!";
|
||||
|
||||
$ifmt = <PIPE>;
|
||||
chomp $ifmt;
|
||||
close PIPE;
|
||||
|
||||
if ($ifmt !~ /tar archive/) {
|
||||
die __x("{f}: unknown input format: {fmt}\n",
|
||||
f => $input, fmt => $ifmt);
|
||||
}
|
||||
|
||||
if ($ifmt =~ /compress.d/) {
|
||||
if ($ifmt =~ /compress'd/) {
|
||||
@cmd = ("uncompress", "-c", $input);
|
||||
} elsif ($ifmt =~ /gzip compressed/) {
|
||||
@cmd = ("gzip", "-cd", $input);
|
||||
} elsif ($ifmt =~ /bzip2 compressed/) {
|
||||
@cmd = ("bzip2", "-cd", $input);
|
||||
} elsif ($ifmt =~ /xz compressed/) {
|
||||
@cmd = ("xz", "-cd", $input);
|
||||
} else {
|
||||
die __x("{f}: unknown input format: {fmt}\n",
|
||||
f => $input, fmt => $ifmt);
|
||||
}
|
||||
|
||||
open PIPE, "-|", @cmd or die "uncompress $input: $!";
|
||||
$estimate = 0;
|
||||
$estimate += length while <PIPE>;
|
||||
close PIPE or die "close: $!";
|
||||
} else {
|
||||
# Plain tar file, just get the size directly. Tar files have
|
||||
# a 512 byte block size (compared with typically 1K or 4K for
|
||||
# filesystems) so this isn't very accurate.
|
||||
$estimate = -s $input;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
printf STDERR "input format = %s\n", $ifmt;
|
||||
printf STDERR "estimate = %s bytes (%s 1K blocks, %s 4K blocks)\n",
|
||||
$estimate, $estimate / 1024, $estimate / 4096;
|
||||
}
|
||||
|
||||
$estimate += 256 * 1024; # For superblocks &c.
|
||||
|
||||
if ($type =~ /^ext[3-9]/) {
|
||||
$estimate += 1024 * 1024; # For ext3/4, add some more for the journal.
|
||||
}
|
||||
|
||||
if ($type eq "ntfs") {
|
||||
$estimate += 4 * 1024 * 1024; # NTFS journal.
|
||||
}
|
||||
|
||||
if ($type eq "btrfs") {
|
||||
# For BTRFS, the minimum metadata allocation is 256MB, with data
|
||||
# additional to that. Note that we disable data and metadata
|
||||
# duplication below.
|
||||
$estimate += 256 * 1024 * 1024;
|
||||
}
|
||||
|
||||
$estimate *= 1.10; # Add 10%, see above.
|
||||
|
||||
# Calculate the output size.
|
||||
|
||||
if (!defined $size) {
|
||||
$size = $estimate;
|
||||
} else {
|
||||
if ($size =~ /^\+([.\d]+)([bKMGTPE])$/) {
|
||||
$size = $estimate + sizebytes ($1, $2);
|
||||
} elsif ($size =~ /^([.\d]+)([bKMGTPE])$/) {
|
||||
$size = sizebytes ($1, $2);
|
||||
} else {
|
||||
die __x("virt-make-fs: cannot parse size parameter: {sz}\n",
|
||||
sz => $size);
|
||||
}
|
||||
}
|
||||
|
||||
$size = int ($size);
|
||||
|
||||
eval {
|
||||
print STDERR "starting libguestfs ...\n" if $debug;
|
||||
|
||||
# Run libguestfs.
|
||||
my $g = Sys::Guestfs->new ();
|
||||
|
||||
# Create the output disk.
|
||||
my %options = ();
|
||||
$options{preallocation} = "metadata" if $format eq "qcow2";
|
||||
$g->disk_create ($output, $format, $size, %options);
|
||||
|
||||
$g->add_drive ($output, format => $format);
|
||||
$g->launch ();
|
||||
|
||||
if ($type eq "ntfs" && !$g->feature_available (["ntfs3g", "ntfsprogs"])) {
|
||||
die __"virt-make-fs: NTFS support was disabled when libguestfs was compiled\n"
|
||||
}
|
||||
|
||||
# Partition the disk.
|
||||
my $dev = "/dev/sda";
|
||||
if (defined $partition) {
|
||||
$partition = "mbr" if $partition eq "";
|
||||
$g->part_disk ($dev, $partition);
|
||||
$dev = "/dev/sda1";
|
||||
|
||||
# Set the partition type byte if it's MBR and the filesystem
|
||||
# type is one that we know about.
|
||||
my $mbr_id;
|
||||
if ($partition eq "mbr" || $partition eq "msdos") {
|
||||
if ($type eq "msdos") {
|
||||
# According to Wikipedia. However I have not actually
|
||||
# tried this.
|
||||
$mbr_id = 0x1;
|
||||
} elsif ($type =~ /^v?fat$/) {
|
||||
$mbr_id = 0xb;
|
||||
} elsif ($type eq "ntfs") {
|
||||
$mbr_id = 0x7;
|
||||
} elsif ($type =~ /^ext\d$/) {
|
||||
$mbr_id = 0x83;
|
||||
} elsif ($type eq "minix") {
|
||||
$mbr_id = 0x81;
|
||||
}
|
||||
}
|
||||
$g->part_set_mbr_id ("/dev/sda", 1, $mbr_id) if defined $mbr_id;
|
||||
}
|
||||
|
||||
print STDERR "creating $type filesystem on $dev ...\n" if $debug;
|
||||
|
||||
# Create the filesystem.
|
||||
if ($type ne "btrfs") {
|
||||
eval {
|
||||
$g->mkfs ($type, $dev);
|
||||
};
|
||||
if ($@) {
|
||||
# Provide more guidance in the error message (RHBZ#823883).
|
||||
print STDERR "'mkfs' (create filesystem) operation failed.\n";
|
||||
if ($type eq "fat") {
|
||||
print STDERR "Instead of 'fat', try 'vfat' (long filenames) or 'msdos' (short filenames).\n";
|
||||
} else {
|
||||
print STDERR "Is '$type' a correct filesystem type?\n";
|
||||
}
|
||||
die
|
||||
}
|
||||
} else {
|
||||
$g->mkfs_btrfs ([$dev], datatype => "single", metadata => "single");
|
||||
}
|
||||
|
||||
# Set label.
|
||||
if (defined $label) {
|
||||
$g->set_label ($dev, $label);
|
||||
}
|
||||
|
||||
# Mount it.
|
||||
|
||||
# For vfat, add the utf8 mount option because we want to be able
|
||||
# to encode any non-ASCII characters into UCS2 which is what
|
||||
# modern vfat uses on disk (RHBZ#823885).
|
||||
my $options = "";
|
||||
$options = "utf8" if $type eq "vfat";
|
||||
|
||||
$g->mount_options ($options, $dev, "/");
|
||||
|
||||
# Copy the data in.
|
||||
my $ifile;
|
||||
|
||||
if ($ifmt eq "directory") {
|
||||
my $pfile = create_pipe ();
|
||||
my $cmd = sprintf ("tar -C %s -cf - . > $pfile &",
|
||||
shell_quote ($input));
|
||||
print STDERR "command: $cmd\n" if $debug;
|
||||
system ($cmd) == 0 or die __"tar: failed, see earlier messages\n";
|
||||
$ifile = $pfile;
|
||||
} else {
|
||||
if ($ifmt =~ /compress.d/) {
|
||||
my $pfile = create_pipe ();
|
||||
my $cmd;
|
||||
if ($ifmt =~ /compress'd/) {
|
||||
$cmd = sprintf ("uncompress -c %s > $pfile",
|
||||
shell_quote ($input));
|
||||
} elsif ($ifmt =~ /gzip compressed/) {
|
||||
$cmd = sprintf ("gzip -cd %s", shell_quote ($input));
|
||||
} elsif ($ifmt =~ /bzip2 compressed/) {
|
||||
$cmd = sprintf ("bzip2 -cd %s", shell_quote ($input));
|
||||
} elsif ($ifmt =~ /xz compressed/) {
|
||||
$cmd = sprintf ("xz -cd %s", shell_quote ($input));
|
||||
} else {
|
||||
die __x("{f}: unknown input format: {fmt}\n",
|
||||
f => $input, fmt => $ifmt);
|
||||
}
|
||||
$cmd .= " > $pfile &";
|
||||
print STDERR "command: $cmd\n" if $debug;
|
||||
system ($cmd) == 0 or
|
||||
die __"uncompress command failed, see earlier messages\n";
|
||||
$ifile = $pfile;
|
||||
} else {
|
||||
print STDERR "reading directly from $input\n" if $debug;
|
||||
$ifile = $input;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
# For debugging, print statvfs before and after doing
|
||||
# the tar-in.
|
||||
my %stat = $g->statvfs ("/");
|
||||
print STDERR "Before uploading ...\n";
|
||||
print STDERR Dumper(\%stat);
|
||||
}
|
||||
|
||||
print STDERR "Uploading from $ifile to / ...\n" if $debug;
|
||||
$g->tar_in ($ifile, "/");
|
||||
|
||||
if ($debug) {
|
||||
my %stat = $g->statvfs ("/");
|
||||
print STDERR "After uploading ...\n";
|
||||
print STDERR Dumper(\%stat);
|
||||
}
|
||||
|
||||
print STDERR "finishing off\n" if $debug;
|
||||
$g->shutdown ();
|
||||
$g->close ()
|
||||
};
|
||||
if ($@) {
|
||||
# Error: delete the output before exiting.
|
||||
my $err = $@;
|
||||
unlink $output;
|
||||
if ($err =~ /tar_in/) {
|
||||
print STDERR __"virt-make-fs: error copying contents into filesystem\nAn error here usually means that the program did not estimate the\nfilesystem size correctly. Please read the BUGS section of the manpage.\n";
|
||||
}
|
||||
print STDERR $err;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
sub sizebytes
|
||||
{
|
||||
local $_ = shift;
|
||||
my $unit = shift;
|
||||
|
||||
$_ *= 1024 if $unit =~ /[KMGTPE]/;
|
||||
$_ *= 1024 if $unit =~ /[MGTPE]/;
|
||||
$_ *= 1024 if $unit =~ /[GTPE]/;
|
||||
$_ *= 1024 if $unit =~ /[TPE]/;
|
||||
$_ *= 1024 if $unit =~ /[PE]/;
|
||||
$_ *= 1024 if $unit =~ /[E]/;
|
||||
|
||||
return floor($_);
|
||||
}
|
||||
|
||||
sub create_pipe
|
||||
{
|
||||
local $_;
|
||||
my $dir = tempdir (CLEANUP => 1);
|
||||
my $pipe = "$dir/pipe";
|
||||
mkfifo ($pipe, 0600) or
|
||||
die "mkfifo: $pipe: $!";
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
=head1 SHELL QUOTING
|
||||
|
||||
Libvirt guest names can contain arbitrary characters, some of which
|
||||
have meaning to the shell such as C<#> and space. You may need to
|
||||
quote or escape these characters on the command line. See the shell
|
||||
manual page L<sh(1)> for details.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<guestfish(1)>,
|
||||
L<virt-format(1)>,
|
||||
L<virt-resize(1)>,
|
||||
L<virt-tar-in(1)>,
|
||||
L<mkisofs(1)>,
|
||||
L<genisoimage(1)>,
|
||||
L<mksquashfs(1)>,
|
||||
L<mke2fs(8)>,
|
||||
L<resize2fs(8)>,
|
||||
L<guestfs(3)>,
|
||||
L<Sys::Guestfs(3)>,
|
||||
L<http://libguestfs.org/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2010-2012 Red Hat Inc.
|
||||
Reference in New Issue
Block a user