debian: Note that libconfig-dev ought to be required.
[libguestfs.git] / tools / virt-make-fs
1 #!/usr/bin/perl -w
2 # virt-make-fs
3 # Copyright (C) 2010 Red Hat Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 use warnings;
20 use strict;
21
22 use Sys::Guestfs;
23 use Sys::Guestfs::Lib qw(feature_available);
24
25 use Pod::Usage;
26 use Getopt::Long;
27 use File::Temp qw(tempdir);
28 use POSIX qw(mkfifo floor);
29 use Data::Dumper;
30 use String::ShellQuote qw(shell_quote);
31 use Locale::TextDomain 'libguestfs';
32
33 =encoding utf8
34
35 =head1 NAME
36
37 virt-make-fs - Make a filesystem from a tar archive or files
38
39 =head1 SYNOPSIS
40
41  virt-make-fs [--options] input.tar output.img
42
43  virt-make-fs [--options] input.tar.gz output.img
44
45  virt-make-fs [--options] directory output.img
46
47 =head1 DESCRIPTION
48
49 Virt-make-fs is a command line tool for creating a filesystem from a
50 tar archive or some files in a directory.  It is similar to tools like
51 L<mkisofs(1)>, L<genisoimage(1)> and L<mksquashfs(1)>.  Unlike those
52 tools, it can create common filesystem types like ext2/3 or NTFS,
53 which can be useful if you want to attach these filesystems to
54 existing virtual machines (eg. to import large amounts of read-only
55 data to a VM).
56
57 Basic usage is:
58
59  virt-make-fs input output
60
61 where C<input> is either a directory containing files that you want to
62 add, or a tar archive (either uncompressed tar or gzip-compressed
63 tar); and C<output> is a disk image.  The input type is detected
64 automatically.  The output disk image defaults to a raw ext2 image
65 unless you specify extra flags (see L</OPTIONS> below).
66
67 =head2 EXTRA SPACE
68
69 Unlike formats such as tar and squashfs, a filesystem does not "just
70 fit" the files that it contains, but might have extra space.
71 Depending on how you are going to use the output, you might think this
72 extra space is wasted and want to minimize it, or you might want to
73 leave space so that more files can be added later.  Virt-make-fs
74 defaults to minimizing the extra space, but you can use the I<--size>
75 flag to leave space in the filesystem if you want it.
76
77 An alternative way to leave extra space but not make the output image
78 any bigger is to use an alternative disk image format (instead of the
79 default "raw" format).  Using I<--format=qcow2> will use the native
80 QEmu/KVM qcow2 image format (check your hypervisor supports this
81 before using it).  This allows you to choose a large I<--size> but the
82 extra space won't actually be allocated in the image until you try to
83 store something in it.
84
85 Don't forget that you can also use local commands including
86 L<resize2fs(8)> and L<virt-resize(1)> to resize existing filesystems,
87 or rerun virt-make-fs to build another image from scratch.
88
89 =head3 EXAMPLE
90
91  virt-make-fs --format=qcow2 --size=+200M input output.img
92
93 =head2 FILESYSTEM TYPE
94
95 The default filesystem type is C<ext2>.  Just about any filesystem
96 type that libguestfs supports can be used (but I<not> read-only
97 formats like ISO9660).  Here are some of the more common choices:
98
99 =over 4
100
101 =item I<ext3>
102
103 Note that ext3 filesystems contain a journal, typically 1-32 MB in size.
104 If you are not going to use the filesystem in a way that requires the
105 journal, then this is just wasted overhead.
106
107 =item I<ntfs> or I<vfat>
108
109 Useful if exporting data to a Windows guest.
110
111 I<Note for vfat>: The tar archive or local directory must only contain
112 files which are owned by root (ie. UID:GID = 0:0).  The reason is that
113 the tar program running within libguestfs is unable to change the
114 ownership of non-root files, since vfat itself does not support this.
115
116 =item I<minix>
117
118 Lower overhead than C<ext2>, but certain limitations on filename
119 length and total filesystem size.
120
121 =back
122
123 =head3 EXAMPLE
124
125  virt-make-fs --type=minix input minixfs.img
126
127 =head2 TO PARTITION OR NOT TO PARTITION
128
129 Optionally virt-make-fs can add a partition table to the output disk.
130
131 Adding a partition can make the disk image more compatible with
132 certain virtualized operating systems which don't expect to see a
133 filesystem directly located on a block device (Linux doesn't care and
134 will happily handle both types).
135
136 On the other hand, if you have a partition table then the output image
137 is no longer a straight filesystem.  For example you cannot run
138 L<fsck(8)> directly on a partitioned disk image.  (However libguestfs
139 tools such as L<guestfish(1)> and L<virt-resize(1)> can still be
140 used).
141
142 =head3 EXAMPLE
143
144 Add an MBR partition:
145
146  virt-make-fs --partition -- input disk.img
147
148 If the output disk image could be terabyte-sized or larger, it's
149 better to use an EFI/GPT-compatible partition table:
150
151  virt-make-fs --partition=gpt --size=+4T --format=qcow2 input disk.img
152
153 =head1 OPTIONS
154
155 =over 4
156
157 =cut
158
159 my $help;
160
161 =item B<--help>
162
163 Display brief help.
164
165 =cut
166
167 my $version;
168
169 =item B<--version>
170
171 Display version number and exit.
172
173 =cut
174
175 my $debug;
176
177 =item B<--debug>
178
179 Enable debugging information.
180
181 =cut
182
183 my $size;
184
185 =item B<--size=E<lt>NE<gt>>
186
187 =item B<--size=+E<lt>NE<gt>>
188
189 =item B<-s E<lt>NE<gt>>
190
191 =item B<-s +E<lt>NE<gt>>
192
193 Use the I<--size> (or I<-s>) option to choose the size of the output
194 image.
195
196 If this option is I<not> given, then the output image will be just
197 large enough to contain all the files, with not much wasted space.
198
199 To choose a fixed size output disk, specify an absolute number
200 followed by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes,
201 Gigabytes, Terabytes, Petabytes or Exabytes.  This must be large
202 enough to contain all the input files, else you will get an error.
203
204 To leave extra space, specify C<+> (plus sign) and a number followed
205 by b/K/M/G/T/P/E to mean bytes, Kilobytes, Megabytes, Gigabytes,
206 Terabytes, Petabytes or Exabytes.  For example: I<--size=+200M> means
207 enough space for the input files, and (approximately) an extra 200 MB
208 free space.
209
210 Note that virt-make-fs estimates free space, and therefore will not
211 produce filesystems containing precisely the free space requested.
212 (It is much more expensive and time-consuming to produce a filesystem
213 which has precisely the desired free space).
214
215 =cut
216
217 my $format = "raw";
218
219 =item B<--format=E<lt>fmtE<gt>>
220
221 =item B<-F E<lt>fmtE<gt>>
222
223 Choose the output disk image format.
224
225 The default is C<raw> (raw disk image).
226
227 For other choices, see the L<qemu-img(1)> manpage.  The only other
228 choice that would really make sense here is C<qcow2>.
229
230 =cut
231
232 my $type = "ext2";
233
234 =item B<--type=E<lt>fsE<gt>>
235
236 =item B<-t E<lt>fsE<gt>>
237
238 Choose the output filesystem type.
239
240 The default is C<ext2>.
241
242 Any filesystem which is supported read-write by libguestfs can be used
243 here.
244
245 =cut
246
247 my $partition;
248
249 =item B<--partition>
250
251 =item B<--partition=E<lt>parttypeE<gt>>
252
253 If specified, this flag adds an MBR partition table to the output disk
254 image.
255
256 You can change the partition table type, eg. I<--partition=gpt> for
257 large disks.
258
259 Note that if you just use a lonesome I<--partition>, the Perl option
260 parser might consider the next parameter to be the partition type.
261 For example:
262
263  virt-make-fs --partition input.tar ...
264
265 would cause virt-make-fs to think you wanted to use a partition type
266 of C<input.tar> which is completely wrong.  To avoid this, use I<-->
267 (a double dash) between options and the input file argument:
268
269  virt-make-fs --partition -- input.tar ...
270
271 =back
272
273 =cut
274
275 GetOptions ("help|?" => \$help,
276             "version" => \$version,
277             "debug" => \$debug,
278             "s|size=s" => \$size,
279             "F|format=s" => \$format,
280             "t|type=s" => \$type,
281             "partition:s" => \$partition,
282     ) or pod2usage (2);
283 pod2usage (1) if $help;
284 if ($version) {
285     my $g = Sys::Guestfs->new ();
286     my %h = $g->version ();
287     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
288     exit
289 }
290
291 die __"virt-make-fs input output\n" if @ARGV != 2;
292
293 my $input = $ARGV[0];
294 my $output = $ARGV[1];
295
296 # Input.  What is it?  Estimate how much space it will need.
297 #
298 # Estimation is a Hard Problem.  Some factors which make it hard:
299 #
300 #   - Superblocks, block free bitmaps, FAT and other fixed overhead
301 #   - Indirect blocks (ext2, ext3), and extents
302 #   - Journal size
303 #   - Internal fragmentation of files
304 #
305 # What we could also do is try shrinking the filesystem after creating
306 # and populating it, but that is complex given partitions.
307
308 my $estimate;     # Estimated size required (in bytes).
309 my $ifmt;         # Input format.
310
311 if (-d $input) {
312     $ifmt = "directory";
313
314     my @cmd = ("du", "--apparent-size", "-b", "-s", $input);
315     open PIPE, "-|", @cmd or die "du $input: $!";
316
317     $_ = <PIPE>;
318     if (/^(\d+)/) {
319         $estimate = $1;
320     } else {
321         die __"unexpected output from 'du' command";
322     }
323 } else {
324     local $ENV{LANG} = "C";
325     my @cmd = ("file", "-bsLz", $input);
326     open PIPE, "-|", @cmd or die "file $input: $!";
327
328     $ifmt = <PIPE>;
329     chomp $ifmt;
330     close PIPE;
331
332     if ($ifmt !~ /tar archive/) {
333         die __x("{f}: unknown input format: {fmt}\n",
334                 f => $input, fmt => $ifmt);
335     }
336
337     if ($ifmt =~ /compress.d/) {
338         if ($ifmt =~ /compress'd/) {
339             @cmd = ("uncompress", "-c", $input);
340         } elsif ($ifmt =~ /gzip compressed/) {
341             @cmd = ("gzip", "-cd", $input);
342         } elsif ($ifmt =~ /bzip2 compressed/) {
343             @cmd = ("bzip2", "-cd", $input);
344         } elsif ($ifmt =~ /xz compressed/) {
345             @cmd = ("xz", "-cd", $input);
346         } else {
347             die __x("{f}: unknown input format: {fmt}\n",
348                     f => $input, fmt => $ifmt);
349         }
350
351         open PIPE, "-|", @cmd or die "uncompress $input: $!";
352         $estimate = 0;
353         $estimate += length while <PIPE>;
354         close PIPE or die "close: $!";
355     } else {
356         # Plain tar file, just get the size directly.  Tar files have
357         # a 512 byte block size (compared with typically 1K or 4K for
358         # filesystems) so this isn't very accurate.
359         $estimate = -s $input;
360     }
361 }
362
363 if ($debug) {
364     printf STDERR "input format = %s\n", $ifmt;
365     printf STDERR "estimate = %s bytes (%s 1K blocks, %s 4K blocks)\n",
366       $estimate, $estimate / 1024, $estimate / 4096;
367 }
368
369 $estimate += 256 * 1024;        # For superblocks &c.
370
371 if ($type =~ /^ext[3-9]/) {
372     $estimate += 1024 * 1024;   # For ext3/4, add some more for the journal.
373 }
374
375 if ($type =~ /^ntfs/) {
376     $estimate += 4 * 1024 * 1024; # NTFS journal.
377 }
378
379 $estimate *= 1.10;              # Add 10%, see above.
380
381 # Calculate the output size.
382
383 if (!defined $size) {
384     $size = $estimate;
385 } else {
386     if ($size =~ /^\+([.\d]+)([bKMGTPE])$/) {
387         $size = $estimate + sizebytes ($1, $2);
388     } elsif ($size =~ /^([.\d]+)([bKMGTPE])$/) {
389         $size = sizebytes ($1, $2);
390     } else {
391         die __x("virt-make-fs: cannot parse size parameter: {sz}\n",
392                 sz => $size);
393     }
394 }
395
396 $size = int ($size);
397
398 # Create the output disk.
399 # Take the unusual step of invoking qemu-img here.
400
401 my @cmd = ("qemu-img", "create", "-f", $format, $output, $size);
402 if ($debug) {
403     print STDERR ("running: ", join (" ", @cmd), "\n");
404 }
405 system (@cmd) == 0 or
406     die __"qemu-img create: failed to create disk image, see earlier error messages\n";
407
408 eval {
409     print STDERR "starting libguestfs ...\n" if $debug;
410
411     # Run libguestfs.
412     my $g = Sys::Guestfs->new ();
413     $g->add_drive_opts ($output, format => $format);
414     $g->launch ();
415
416     if ($type eq "ntfs" && !feature_available ($g, "ntfs3g", "ntfsprogs")) {
417         die __"virt-make-fs: NTFS support was disabled when libguestfs was compiled\n"
418     }
419
420     # Partition the disk.
421     my $dev = "/dev/sda";
422     if (defined $partition) {
423         $partition = "mbr" if $partition eq "";
424         $g->part_disk ($dev, $partition);
425         $dev = "/dev/sda1";
426     }
427
428     print STDERR "creating $type filesystem on $dev ...\n" if $debug;
429
430     # Create the filesystem.
431     $g->mkfs ($type, $dev);
432     $g->mount_options ("", $dev, "/");
433
434     # Copy the data in.
435     my $ifile;
436
437     if ($ifmt eq "directory") {
438         my $pfile = create_pipe ();
439         my $cmd = sprintf ("tar -C %s -cf - . > $pfile &",
440                            shell_quote ($input));
441         print STDERR "command: $cmd\n" if $debug;
442         system ($cmd) == 0 or die __"tar: failed, see earlier messages\n";
443         $ifile = $pfile;
444     } else {
445         if ($ifmt =~ /compress.d/) {
446             my $pfile = create_pipe ();
447             my $cmd;
448             if ($ifmt =~ /compress'd/) {
449                 $cmd = sprintf ("uncompress -c %s > $pfile",
450                                 shell_quote ($input));
451             } elsif ($ifmt =~ /gzip compressed/) {
452                 $cmd = sprintf ("gzip -cd %s", shell_quote ($input));
453             } elsif ($ifmt =~ /bzip2 compressed/) {
454                 $cmd = sprintf ("bzip2 -cd %s", shell_quote ($input));
455             } elsif ($ifmt =~ /xz compressed/) {
456                 $cmd = sprintf ("xz -cd %s", shell_quote ($input));
457             } else {
458                 die __x("{f}: unknown input format: {fmt}\n",
459                         f => $input, fmt => $ifmt);
460             }
461             $cmd .= " > $pfile &";
462             print STDERR "command: $cmd\n" if $debug;
463             system ($cmd) == 0 or
464                 die __"uncompress command failed, see earlier messages\n";
465             $ifile = $pfile;
466         } else {
467             print STDERR "reading directly from $input\n" if $debug;
468             $ifile = $input;
469         }
470     }
471
472     if ($debug) {
473         # For debugging, print statvfs before and after doing
474         # the tar-in.
475         my %stat = $g->statvfs ("/");
476         print STDERR "Before uploading ...\n";
477         print STDERR Dumper(\%stat);
478     }
479
480     print STDERR "Uploading from $ifile to / ...\n" if $debug;
481     $g->tar_in ($ifile, "/");
482
483     if ($debug) {
484         my %stat = $g->statvfs ("/");
485         print STDERR "After uploading ...\n";
486         print STDERR Dumper(\%stat);
487     }
488
489     print STDERR "finishing off\n" if $debug;
490     $g->umount_all ();
491     $g->sync ();
492     undef $g;
493 };
494 if ($@) {
495     # Error: delete the output before exiting.
496     my $err = $@;
497     unlink $output;
498     if ($err =~ /tar_in/) {
499         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";
500     }
501     print STDERR $err;
502     exit 1;
503 }
504
505 exit 0;
506
507 sub sizebytes
508 {
509     local $_ = shift;
510     my $unit = shift;
511
512     $_ *= 1024 if $unit =~ /[KMGTPE]/;
513     $_ *= 1024 if $unit =~ /[MGTPE]/;
514     $_ *= 1024 if $unit =~ /[GTPE]/;
515     $_ *= 1024 if $unit =~ /[TPE]/;
516     $_ *= 1024 if $unit =~ /[PE]/;
517     $_ *= 1024 if $unit =~ /[E]/;
518
519     return floor($_);
520 }
521
522 sub create_pipe
523 {
524     local $_;
525     my $dir = tempdir (CLEANUP => 1);
526     my $pipe = "$dir/pipe";
527     mkfifo ($pipe, 0600) or
528         die "mkfifo: $pipe: $!";
529     return $pipe;
530 }
531
532 =head1 SHELL QUOTING
533
534 Libvirt guest names can contain arbitrary characters, some of which
535 have meaning to the shell such as C<#> and space.  You may need to
536 quote or escape these characters on the command line.  See the shell
537 manual page L<sh(1)> for details.
538
539 =head1 SEE ALSO
540
541 L<guestfish(1)>,
542 L<virt-resize(1)>,
543 L<virt-tar-in(1)>,
544 L<mkisofs(1)>,
545 L<genisoimage(1)>,
546 L<mksquashfs(1)>,
547 L<mke2fs(8)>,
548 L<resize2fs(8)>,
549 L<guestfs(3)>,
550 L<Sys::Guestfs(3)>,
551 L<http://libguestfs.org/>.
552
553 =head1 BUGS
554
555 When reporting bugs, please enable debugging and capture the
556 I<complete> output:
557
558  export LIBGUESTFS_DEBUG=1
559  virt-make-fs --debug [...] > /tmp/virt-make-fs.log 2>&1
560
561 Attach /tmp/virt-make-fs.log to a new bug report at
562 L<https://bugzilla.redhat.com/>
563
564 =head1 AUTHOR
565
566 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
567
568 =head1 COPYRIGHT
569
570 Copyright (C) 2010 Red Hat Inc.
571
572 This program is free software; you can redistribute it and/or modify
573 it under the terms of the GNU General Public License as published by
574 the Free Software Foundation; either version 2 of the License, or
575 (at your option) any later version.
576
577 This program is distributed in the hope that it will be useful,
578 but WITHOUT ANY WARRANTY; without even the implied warranty of
579 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
580 GNU General Public License for more details.
581
582 You should have received a copy of the GNU General Public License
583 along with this program; if not, write to the Free Software
584 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.