Richard W.M. Jones
2019-Feb-22 09:07 UTC
[Libguestfs] [PATCH nbdkit v3 0/4] Add linuxdisk plugin.
For v3 I reimplemented this using mke2fs -d. This obviously makes the implementation a whole lot simpler, but cannot support multiple directory merging. Patches 1-3 are the same as before. I've also reproduced the notes from v2 below. v2: - Fix inconsistent tab/space. - All 3 plugins now contain a block of text pointing to the other 2 plugins. - TMDIR -> TMPDIR - Unlink the temporary file and other cleanups along error paths. - fclose -> pclose, and check the return value for errors. I considered, but didn't yet fix, the latent bug in shell_quote. Instead I added a comment. Note there's a case where we probably (arguably) should be using shell_quote on an argv[0] here: https://github.com/libguestfs/nbdkit/blob/72e0afe2e280d895f68941677fafa559ddc3bb0d/plugins/iso/iso.c#L100 'du -c -k -s' works on FreeBSD which doesn't have GNU coreutils. (However I agree it's not very natural to use this plugin with FreeBSD at all.) Rich.
Richard W.M. Jones
2019-Feb-22 09:07 UTC
[Libguestfs] [PATCH nbdkit v3 1/4] common: Move some GPT functionality to a common directory.
From: "Richard W.M. Jones" <rjones@redhat.com> Headers and code related to GUID Partition Tables are moved to a common directory to allow us to share that code across multiple plugins and filters in future. This change is pure refactoring. --- Makefile.am | 1 + common/gpt/Makefile.am | 42 +++++++++++ common/gpt/efi-crc32.c | 140 +++++++++++++++++++++++++++++++++++ common/gpt/efi-crc32.h | 42 +++++++++++ common/gpt/gpt.h | 80 ++++++++++++++++++++ configure.ac | 1 + plugins/partitioning/Makefile.am | 4 +- plugins/partitioning/efi-crc32.c | 140 ----------------------------------- plugins/partitioning/efi-crc32.h | 42 ----------- plugins/partitioning/partition-gpt.c | 31 ++------ plugins/partitioning/virtual-disk.h | 13 +--- 11 files changed, 314 insertions(+), 222 deletions(-) create mode 100644 common/gpt/Makefile.am create mode 100644 common/gpt/efi-crc32.c create mode 100644 common/gpt/efi-crc32.h create mode 100644 common/gpt/gpt.h delete mode 100644 plugins/partitioning/efi-crc32.c delete mode 100644 plugins/partitioning/efi-crc32.h diff --git a/Makefile.am b/Makefile.am index 273accb..653956f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ SUBDIRS = \ if HAVE_PLUGINS SUBDIRS += \ common/bitmap \ + common/gpt \ common/regions \ common/sparse \ plugins \ diff --git a/common/gpt/Makefile.am b/common/gpt/Makefile.am new file mode 100644 index 0000000..66d2caa --- /dev/null +++ b/common/gpt/Makefile.am @@ -0,0 +1,42 @@ +# nbdkit +# Copyright (C) 2019 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +include $(top_srcdir)/common-rules.mk + +noinst_LTLIBRARIES = libgpt.la + +libgpt_la_SOURCES = \ + efi-crc32.c \ + efi-crc32.h \ + gpt.h +libgpt_la_CFLAGS = \ + $(WARNINGS_CFLAGS) diff --git a/common/gpt/efi-crc32.c b/common/gpt/efi-crc32.c new file mode 100644 index 0000000..2b080d3 --- /dev/null +++ b/common/gpt/efi-crc32.c @@ -0,0 +1,140 @@ +/* This code was taken from parted and indirectly from other sources + * as you can see from the messages below. The license is compatible + * with the permissive license used in nbdkit. - RWMJ 2018-09-16 + */ + +/* + * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com> + * - Copied crc32.c from the linux/drivers/net/cipe directory. + * - Now pass seed as an arg + * - changed unsigned long to uint32_t, added #include<stdint.h> + * - changed len to be an unsigned long + * - changed crc32val to be a register + * - License remains unchanged! It's still GPL-compatable! + */ + + /* ============================================================= */ + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ + /* code or tables extracted from it, as desired without restriction. */ + /* */ + /* First, the polynomial itself and its table of feedback terms. The */ + /* polynomial is */ + /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ + /* */ + /* Note that we take it "backwards" and put the highest-order term in */ + /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ + /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ + /* the MSB being 1. */ + /* */ + /* Note that the usual hardware shift register implementation, which */ + /* is what we're using (we're merely optimizing it by doing eight-bit */ + /* chunks at a time) shifts bits into the lowest-order term. In our */ + /* implementation, that means shifting towards the right. Why do we */ + /* do it this way? Because the calculated CRC must be transmitted in */ + /* order from highest-order term to lowest-order term. UARTs transmit */ + /* characters in order from LSB to MSB. By storing the CRC this way, */ + /* we hand it to the UART in the order low-byte to high-byte; the UART */ + /* sends each low-bit to hight-bit; and the result is transmission bit */ + /* by bit from highest- to lowest-order term without requiring any bit */ + /* shuffling on our part. Reception works similarly. */ + /* */ + /* The feedback terms table consists of 256, 32-bit entries. Notes: */ + /* */ + /* The table can be generated at runtime if desired; code to do so */ + /* is shown later. It might not be obvious, but the feedback */ + /* terms simply represent the results of eight shift/xor opera- */ + /* tions for all combinations of data and CRC register values. */ + /* */ + /* The values must be right-shifted by eight bits by the "updcrc" */ + /* logic; the shift must be unsigned (bring in zeroes). On some */ + /* hardware you could probably optimize the shift in assembler by */ + /* using byte-swap instructions. */ + /* polynomial $edb88320 */ + /* */ + /* -------------------------------------------------------------------- */ + +#include <config.h> + +#include <stdio.h> +#include <stdint.h> + +#include "efi-crc32.h" + +static const uint32_t crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +static uint32_t +_efi_crc32 (const void *buf, size_t len, uint32_t seed) +{ + size_t i; + uint32_t crc32val; + const unsigned char *s = buf; + + crc32val = seed; + for (i = 0; i < len; i++) { + crc32val + crc32_tab[(crc32val ^ s[i]) & 0xff] ^ + (crc32val >> 8); + } + return crc32val; +} + +uint32_t +efi_crc32 (const void *buf, size_t len) +{ + return _efi_crc32 (buf, len, ~0L) ^ ~0L; +} diff --git a/common/gpt/efi-crc32.h b/common/gpt/efi-crc32.h new file mode 100644 index 0000000..21e28d7 --- /dev/null +++ b/common/gpt/efi-crc32.h @@ -0,0 +1,42 @@ +/* nbdkit + * Copyright (C) 2018 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NBDKIT_EFI_CRC32_H +#define NBDKIT_EFI_CRC32_H + +#include <stdint.h> + +extern uint32_t efi_crc32 (const void *buf, size_t len) + __attribute__((__nonnull__ (1))); + +#endif /* NBDKIT_EFI_CRC32_H */ diff --git a/common/gpt/gpt.h b/common/gpt/gpt.h new file mode 100644 index 0000000..7e2a2ec --- /dev/null +++ b/common/gpt/gpt.h @@ -0,0 +1,80 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NBDKIT_GPT_H +#define NBDKIT_GPT_H + +struct gpt_header { + char signature[8]; + char revision[4]; + uint32_t header_size; + uint32_t crc; + uint32_t reserved; + uint64_t current_lba; + uint64_t backup_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + char guid[16]; + uint64_t partition_entries_lba; + uint32_t nr_partition_entries; + uint32_t size_partition_entry; + uint32_t crc_partitions; +}; + +#define GPT_SIGNATURE "EFI PART" +#define GPT_REVISION "\0\0\1\0" /* revision 1.0 */ + +struct gpt_entry { + char partition_type_guid[16]; + char unique_guid[16]; + uint64_t first_lba; + uint64_t last_lba; + uint64_t attributes; + char name[72]; /* UTF-16LE */ +}; + +/* GPT_MIN_PARTITIONS is the minimum number of partitions and is + * defined by the UEFI standard (assuming 512 byte sector size). + * + * In plugins such as the partitioning plugin, if we are requested to + * allocate more than GPT_MIN_PARTITIONS then we increase the + * partition table in chunks of this size. Note that clients may not + * support > GPT_MIN_PARTITIONS. + * + * GPT_PT_ENTRY_SIZE is the minimum specified by the UEFI spec, but + * increasing it is not useful. + */ +#define GPT_MIN_PARTITIONS 128 +#define GPT_PT_ENTRY_SIZE 128 + +#endif /* NBDKIT_GPT_H */ diff --git a/configure.ac b/configure.ac index a0f45e8..bca6134 100644 --- a/configure.ac +++ b/configure.ac @@ -833,6 +833,7 @@ AC_CONFIG_FILES([podwrapper.pl], AC_CONFIG_FILES([Makefile bash/Makefile common/bitmap/Makefile + common/gpt/Makefile common/include/Makefile common/regions/Makefile common/sparse/Makefile diff --git a/plugins/partitioning/Makefile.am b/plugins/partitioning/Makefile.am index 42e0c45..fa0266e 100644 --- a/plugins/partitioning/Makefile.am +++ b/plugins/partitioning/Makefile.am @@ -37,8 +37,6 @@ EXTRA_DIST = nbdkit-partitioning-plugin.pod plugin_LTLIBRARIES = nbdkit-partitioning-plugin.la nbdkit_partitioning_plugin_la_SOURCES = \ - efi-crc32.c \ - efi-crc32.h \ partitioning.c \ partition-gpt.c \ partition-mbr.c \ @@ -48,6 +46,7 @@ nbdkit_partitioning_plugin_la_SOURCES = \ nbdkit_partitioning_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/include \ + -I$(top_srcdir)/common/gpt \ -I$(top_srcdir)/common/include \ -I$(top_srcdir)/common/regions \ -I. @@ -57,6 +56,7 @@ nbdkit_partitioning_plugin_la_LDFLAGS = \ -module -avoid-version -shared \ -Wl,--version-script=$(top_srcdir)/plugins/plugins.syms nbdkit_partitioning_plugin_la_LIBADD = \ + $(top_builddir)/common/gpt/libgpt.la \ $(top_builddir)/common/regions/libregions.la if HAVE_POD diff --git a/plugins/partitioning/efi-crc32.c b/plugins/partitioning/efi-crc32.c deleted file mode 100644 index 2b080d3..0000000 --- a/plugins/partitioning/efi-crc32.c +++ /dev/null @@ -1,140 +0,0 @@ -/* This code was taken from parted and indirectly from other sources - * as you can see from the messages below. The license is compatible - * with the permissive license used in nbdkit. - RWMJ 2018-09-16 - */ - -/* - * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com> - * - Copied crc32.c from the linux/drivers/net/cipe directory. - * - Now pass seed as an arg - * - changed unsigned long to uint32_t, added #include<stdint.h> - * - changed len to be an unsigned long - * - changed crc32val to be a register - * - License remains unchanged! It's still GPL-compatable! - */ - - /* ============================================================= */ - /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ - /* code or tables extracted from it, as desired without restriction. */ - /* */ - /* First, the polynomial itself and its table of feedback terms. The */ - /* polynomial is */ - /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ - /* */ - /* Note that we take it "backwards" and put the highest-order term in */ - /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ - /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ - /* the MSB being 1. */ - /* */ - /* Note that the usual hardware shift register implementation, which */ - /* is what we're using (we're merely optimizing it by doing eight-bit */ - /* chunks at a time) shifts bits into the lowest-order term. In our */ - /* implementation, that means shifting towards the right. Why do we */ - /* do it this way? Because the calculated CRC must be transmitted in */ - /* order from highest-order term to lowest-order term. UARTs transmit */ - /* characters in order from LSB to MSB. By storing the CRC this way, */ - /* we hand it to the UART in the order low-byte to high-byte; the UART */ - /* sends each low-bit to hight-bit; and the result is transmission bit */ - /* by bit from highest- to lowest-order term without requiring any bit */ - /* shuffling on our part. Reception works similarly. */ - /* */ - /* The feedback terms table consists of 256, 32-bit entries. Notes: */ - /* */ - /* The table can be generated at runtime if desired; code to do so */ - /* is shown later. It might not be obvious, but the feedback */ - /* terms simply represent the results of eight shift/xor opera- */ - /* tions for all combinations of data and CRC register values. */ - /* */ - /* The values must be right-shifted by eight bits by the "updcrc" */ - /* logic; the shift must be unsigned (bring in zeroes). On some */ - /* hardware you could probably optimize the shift in assembler by */ - /* using byte-swap instructions. */ - /* polynomial $edb88320 */ - /* */ - /* -------------------------------------------------------------------- */ - -#include <config.h> - -#include <stdio.h> -#include <stdint.h> - -#include "efi-crc32.h" - -static const uint32_t crc32_tab[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL - }; - -/* Return a 32-bit CRC of the contents of the buffer. */ - -static uint32_t -_efi_crc32 (const void *buf, size_t len, uint32_t seed) -{ - size_t i; - uint32_t crc32val; - const unsigned char *s = buf; - - crc32val = seed; - for (i = 0; i < len; i++) { - crc32val - crc32_tab[(crc32val ^ s[i]) & 0xff] ^ - (crc32val >> 8); - } - return crc32val; -} - -uint32_t -efi_crc32 (const void *buf, size_t len) -{ - return _efi_crc32 (buf, len, ~0L) ^ ~0L; -} diff --git a/plugins/partitioning/efi-crc32.h b/plugins/partitioning/efi-crc32.h deleted file mode 100644 index 21e28d7..0000000 --- a/plugins/partitioning/efi-crc32.h +++ /dev/null @@ -1,42 +0,0 @@ -/* nbdkit - * Copyright (C) 2018 Red Hat Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Red Hat nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef NBDKIT_EFI_CRC32_H -#define NBDKIT_EFI_CRC32_H - -#include <stdint.h> - -extern uint32_t efi_crc32 (const void *buf, size_t len) - __attribute__((__nonnull__ (1))); - -#endif /* NBDKIT_EFI_CRC32_H */ diff --git a/plugins/partitioning/partition-gpt.c b/plugins/partitioning/partition-gpt.c index c3d1d03..7a9bd80 100644 --- a/plugins/partitioning/partition-gpt.c +++ b/plugins/partitioning/partition-gpt.c @@ -46,6 +46,7 @@ #include "byte-swapping.h" #include "efi-crc32.h" +#include "gpt.h" #include "regions.h" #include "virtual-disk.h" @@ -86,28 +87,13 @@ create_gpt_partition_header (const void *pt, bool is_primary, unsigned char *out) { uint64_t nr_lbas; - struct gpt_header { - char signature[8]; - char revision[4]; - uint32_t header_size; - uint32_t crc; - uint32_t reserved; - uint64_t current_lba; - uint64_t backup_lba; - uint64_t first_usable_lba; - uint64_t last_usable_lba; - char guid[16]; - uint64_t partition_entries_lba; - uint32_t nr_partition_entries; - uint32_t size_partition_entry; - uint32_t crc_partitions; - } *header = (struct gpt_header *) out; + struct gpt_header *header = (struct gpt_header *) out; nr_lbas = virtual_size (®ions) / SECTOR_SIZE; memset (header, 0, sizeof *header); - memcpy (header->signature, "EFI PART", 8); - memcpy (header->revision, "\0\0\1\0", 4); /* revision 1.0 */ + memcpy (header->signature, GPT_SIGNATURE, sizeof (header->signature)); + memcpy (header->revision, GPT_REVISION, sizeof (header->revision)); header->header_size = htole32 (sizeof *header); if (is_primary) { header->current_lba = htole64 (1); @@ -159,14 +145,7 @@ create_gpt_partition_table_entry (const struct region *region, { size_t i, len; const char *filename; - struct gpt_entry { - char partition_type_guid[16]; - char unique_guid[16]; - uint64_t first_lba; - uint64_t last_lba; - uint64_t attributes; - char name[72]; /* UTF-16LE */ - } *entry = (struct gpt_entry *) out; + struct gpt_entry *entry = (struct gpt_entry *) out; assert (sizeof (struct gpt_entry) == GPT_PT_ENTRY_SIZE); diff --git a/plugins/partitioning/virtual-disk.h b/plugins/partitioning/virtual-disk.h index f59df70..49e125d 100644 --- a/plugins/partitioning/virtual-disk.h +++ b/plugins/partitioning/virtual-disk.h @@ -40,6 +40,7 @@ #include "rounding.h" +#include "gpt.h" #include "regions.h" #define SECTOR_SIZE UINT64_C(512) @@ -50,18 +51,6 @@ */ #define MAX_MBR_DISK_SIZE (UINT32_MAX * SECTOR_SIZE - 5 * MAX_ALIGNMENT) -/* GPT_MIN_PARTITIONS is the minimum number of partitions and is - * defined by the UEFI standard (assuming 512 byte sector size). If - * we are requested to allocate more than GPT_MIN_PARTITIONS then we - * increase the partition table in chunks of this size. Note that - * clients may not support > GPT_MIN_PARTITIONS. - * - * GPT_PT_ENTRY_SIZE is the minimum specified by the UEFI spec, but - * increasing it is not useful. - */ -#define GPT_MIN_PARTITIONS 128 -#define GPT_PT_ENTRY_SIZE 128 - /* For GPT, the number of entries in the partition table array (PTA), * and the number of LBAs which the PTA occupies. The latter will be * 32 if the number of files is <= GPT_MIN_PARTITIONS, which is the -- 1.8.3.1
Richard W.M. Jones
2019-Feb-22 09:07 UTC
[Libguestfs] [PATCH nbdkit v3 2/4] partition: Update to use new common/gpt/gpt.h header file.
From: "Richard W.M. Jones" <rjones@redhat.com> --- filters/partition/Makefile.am | 1 + filters/partition/partition-gpt.c | 62 ++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/filters/partition/Makefile.am b/filters/partition/Makefile.am index 0932793..7814f09 100644 --- a/filters/partition/Makefile.am +++ b/filters/partition/Makefile.am @@ -45,6 +45,7 @@ nbdkit_partition_filter_la_SOURCES = \ nbdkit_partition_filter_la_CPPFLAGS = \ -I$(top_srcdir)/include \ + -I$(top_srcdir)/common/gpt \ -I$(top_srcdir)/common/include nbdkit_partition_filter_la_CFLAGS = \ $(WARNINGS_CFLAGS) diff --git a/filters/partition/partition-gpt.c b/filters/partition/partition-gpt.c index ddfaa75..007799a 100644 --- a/filters/partition/partition-gpt.c +++ b/filters/partition/partition-gpt.c @@ -41,37 +41,29 @@ #include <nbdkit-filter.h> #include "byte-swapping.h" +#include "gpt.h" #include "partition.h" -struct gpt_header { - uint32_t nr_partitions; - uint32_t partition_entry_size; -}; - static void -get_gpt_header (uint8_t *sector, struct gpt_header *header) +get_gpt_header (uint8_t *sector, + uint32_t *nr_partition_entries, + uint32_t *size_partition_entry) { - memcpy (&header->nr_partitions, §or[0x50], 4); - header->nr_partitions = le32toh (header->nr_partitions); - memcpy (&header->partition_entry_size, §or[0x54], 4); - header->partition_entry_size = le32toh (header->partition_entry_size); + struct gpt_header *header = (struct gpt_header *) sector; + *nr_partition_entries = le32toh (header->nr_partition_entries); + *size_partition_entry = le32toh (header->size_partition_entry); } -struct gpt_partition { - uint8_t partition_type_guid[16]; - uint64_t first_lba; - uint64_t last_lba; -}; - static void -get_gpt_partition (uint8_t *bytes, struct gpt_partition *part) +get_gpt_partition (uint8_t *bytes, + uint8_t *partition_type_guid, + uint64_t *first_lba, uint64_t *last_lba) { - memcpy (&part->partition_type_guid, &bytes[0], 16); - memcpy (&part->first_lba, &bytes[0x20], 8); - part->first_lba = le64toh (part->first_lba); - memcpy (&part->last_lba, &bytes[0x28], 8); - part->last_lba = le64toh (part->last_lba); + struct gpt_entry *entry = (struct gpt_entry *) bytes; + memcpy (partition_type_guid, entry->partition_type_guid, 16); + *first_lba = le64toh (entry->first_lba); + *last_lba = le64toh (entry->last_lba); } int @@ -80,18 +72,19 @@ find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata, int64_t *offset_r, int64_t *range_r) { uint8_t partition_bytes[128]; - struct gpt_header header; - struct gpt_partition partition; + uint32_t nr_partition_entries, size_partition_entry; + uint8_t partition_type_guid[16]; + uint64_t first_lba, last_lba; int i; int err; - get_gpt_header (header_bytes, &header); - if (partnum > header.nr_partitions) { + get_gpt_header (header_bytes, &nr_partition_entries, &size_partition_entry); + if (partnum > nr_partition_entries) { nbdkit_error ("GPT partition number out of range"); return -1; } - if (header.partition_entry_size < 128) { + if (size_partition_entry < 128) { nbdkit_error ("GPT partition entry size is < 128 bytes"); return -1; } @@ -101,23 +94,24 @@ find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata, * that the GPT header is bogus. */ if (size < INT64_C(3)*SECTOR_SIZE + - INT64_C(2) * header.nr_partitions * header.partition_entry_size) { + INT64_C(2) * nr_partition_entries * size_partition_entry) { nbdkit_error ("GPT partition table is too large for this disk"); return -1; } - for (i = 0; i < header.nr_partitions; ++i) { + for (i = 0; i < nr_partition_entries; ++i) { /* We already checked these are within bounds above. */ if (next_ops->pread (nxdata, partition_bytes, sizeof partition_bytes, - 2*SECTOR_SIZE + i*header.partition_entry_size, 0, + 2*SECTOR_SIZE + i*size_partition_entry, 0, &err) == -1) return -1; - get_gpt_partition (partition_bytes, &partition); - if (memcmp (partition.partition_type_guid, + get_gpt_partition (partition_bytes, + partition_type_guid, &first_lba, &last_lba); + if (memcmp (partition_type_guid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0 && partnum == i+1) { - *offset_r = partition.first_lba * SECTOR_SIZE; - *range_r = (1 + partition.last_lba - partition.first_lba) * SECTOR_SIZE; + *offset_r = first_lba * SECTOR_SIZE; + *range_r = (1 + last_lba - first_lba) * SECTOR_SIZE; return 0; } } -- 1.8.3.1
Richard W.M. Jones
2019-Feb-22 09:07 UTC
[Libguestfs] [PATCH nbdkit v3 3/4] common: Move a utility function to a common directory.
From: "Richard W.M. Jones" <rjones@redhat.com> The shell_quote function is moved to a new common/utils directory. Eventually more utility functions can be created here. This change is pure refactoring. --- Makefile.am | 1 + common/utils/Makefile.am | 41 +++++++++++++++++++++++++++ common/utils/utils.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ common/utils/utils.h | 39 ++++++++++++++++++++++++++ configure.ac | 1 + plugins/iso/Makefile.am | 3 ++ plugins/iso/iso.c | 36 ++---------------------- 7 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 common/utils/Makefile.am create mode 100644 common/utils/utils.c create mode 100644 common/utils/utils.h diff --git a/Makefile.am b/Makefile.am index 653956f..99f0852 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,6 +74,7 @@ SUBDIRS += \ common/gpt \ common/regions \ common/sparse \ + common/utils \ plugins \ filters endif diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am new file mode 100644 index 0000000..e002586 --- /dev/null +++ b/common/utils/Makefile.am @@ -0,0 +1,41 @@ +# nbdkit +# Copyright (C) 2019 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +include $(top_srcdir)/common-rules.mk + +noinst_LTLIBRARIES = libutils.la + +libutils_la_SOURCES = \ + utils.c \ + utils.h +libutils_la_CFLAGS = \ + $(WARNINGS_CFLAGS) diff --git a/common/utils/utils.c b/common/utils/utils.c new file mode 100644 index 0000000..1f2b57d --- /dev/null +++ b/common/utils/utils.c @@ -0,0 +1,73 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Print str to fp, shell quoting if necessary. This comes from + * libguestfs, but was written by me so I'm relicensing it to a BSD + * license for nbdkit. + */ +void +shell_quote (const char *str, FILE *fp) +{ + /* Note possible bug in this list (XXX): + * https://www.redhat.com/archives/libguestfs/2019-February/msg00036.html + */ + const char *safe_chars + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_=,:/"; + size_t i, len; + + /* If the string consists only of safe characters, output it as-is. */ + len = strlen (str); + if (len == strspn (str, safe_chars)) { + fputs (str, fp); + return; + } + + /* Double-quote the string. */ + fputc ('"', fp); + for (i = 0; i < len; ++i) { + switch (str[i]) { + case '$': case '`': case '\\': case '"': + fputc ('\\', fp); + /*FALLTHROUGH*/ + default: + fputc (str[i], fp); + } + } + fputc ('"', fp); +} diff --git a/common/utils/utils.h b/common/utils/utils.h new file mode 100644 index 0000000..a012183 --- /dev/null +++ b/common/utils/utils.h @@ -0,0 +1,39 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NBDKIT_UTILS_H +#define NBDKIT_UTILS_H + +extern void shell_quote (const char *str, FILE *fp); + +#endif /* NBDKIT_UTILS_H */ diff --git a/configure.ac b/configure.ac index bca6134..229e38c 100644 --- a/configure.ac +++ b/configure.ac @@ -837,6 +837,7 @@ AC_CONFIG_FILES([Makefile common/include/Makefile common/regions/Makefile common/sparse/Makefile + common/utils/Makefile docs/Makefile include/Makefile plugins/Makefile diff --git a/plugins/iso/Makefile.am b/plugins/iso/Makefile.am index 061c161..673d1ee 100644 --- a/plugins/iso/Makefile.am +++ b/plugins/iso/Makefile.am @@ -43,6 +43,7 @@ nbdkit_iso_plugin_la_SOURCES = \ $(top_srcdir)/include/nbdkit-plugin.h nbdkit_iso_plugin_la_CPPFLAGS = \ + -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/include \ -I. nbdkit_iso_plugin_la_CFLAGS = \ @@ -50,6 +51,8 @@ nbdkit_iso_plugin_la_CFLAGS = \ nbdkit_iso_plugin_la_LDFLAGS = \ -module -avoid-version -shared \ -Wl,--version-script=$(top_srcdir)/plugins/plugins.syms +nbdkit_iso_plugin_la_LIBADD = \ + $(top_builddir)/common/utils/libutils.la if HAVE_POD diff --git a/plugins/iso/iso.c b/plugins/iso/iso.c index 7431b48..53fccf0 100644 --- a/plugins/iso/iso.c +++ b/plugins/iso/iso.c @@ -43,6 +43,8 @@ #include <nbdkit-plugin.h> +#include "utils.h" + /* List of directories parsed from the command line. */ static char **dirs = NULL; static size_t nr_dirs = 0; @@ -59,8 +61,6 @@ static const char *params = NULL; static int fd = -1; /* Construct the temporary ISO. */ -static void shell_quote (const char *str, FILE *fp); - static int make_iso (void) { @@ -136,38 +136,6 @@ make_iso (void) return 0; } -/* Print str to fp, shell quoting if necessary. This comes from - * libguestfs, but was written by me so I'm relicensing it to a BSD - * license for nbdkit. - */ -static void -shell_quote (const char *str, FILE *fp) -{ - const char *safe_chars - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_=,:/"; - size_t i, len; - - /* If the string consists only of safe characters, output it as-is. */ - len = strlen (str); - if (len == strspn (str, safe_chars)) { - fputs (str, fp); - return; - } - - /* Double-quote the string. */ - fputc ('"', fp); - for (i = 0; i < len; ++i) { - switch (str[i]) { - case '$': case '`': case '\\': case '"': - fputc ('\\', fp); - /*FALLTHROUGH*/ - default: - fputc (str[i], fp); - } - } - fputc ('"', fp); -} - static void iso_unload (void) { -- 1.8.3.1
Richard W.M. Jones
2019-Feb-22 09:07 UTC
[Libguestfs] [PATCH nbdkit v3 4/4] Add linuxdisk plugin.
From: "Richard W.M. Jones" <rjones@redhat.com> This plugin allows you to create a complete ext2, ext3 or ext4 filesystem in a GPT partitioned disk image. This can be attached as a disk to a Linux virtual machine. It is implemented using e2fsprogs mke2fs ‘-d’ option thus allowing the implementation to be very small and simple, with all the hard work done by mke2fs. Although there is some overlap with nbdkit-iso-plugin and nbdkit-floppy-plugin, the implementations and use cases of all three plugins are sufficiently different that it seems to make sense to add another plugin rather than attempting to extend one of the existing plugins. Largely to avoid user error this plugin is read-only. This is a major difference from the floppy plugin: that plugin allows files to be modified (but not resized or created) and writes those changes through to the backing filesystem. While this plugin could easily be made writable, this would cause almost certain disk corruption when someone connected two clients at the same time. In any case it doesn't make much sense for it to be writable by default since the expectation that writes would somehow modify the original directory on the host filesystem cannot be satisfied by this or any reasonable implementation. Users can add the cow filter on top if they really want writes and know what they are doing: instructions plus disclaimer about this are included in the man page. Eventually we could replace the supermin libext2fs code with this plugin, but there are some missing features that would need to be implemented first. --- README | 4 + TODO | 11 +- configure.ac | 2 + plugins/floppy/nbdkit-floppy-plugin.pod | 4 + plugins/iso/nbdkit-iso-plugin.pod | 6 +- plugins/linuxdisk/Makefile.am | 73 ++++++ plugins/linuxdisk/filesystem.c | 267 +++++++++++++++++++++ plugins/linuxdisk/linuxdisk.c | 229 ++++++++++++++++++ plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod | 180 ++++++++++++++ plugins/linuxdisk/partition-gpt.c | 211 ++++++++++++++++ plugins/linuxdisk/virtual-disk.c | 161 +++++++++++++ plugins/linuxdisk/virtual-disk.h | 92 +++++++ .../partitioning/nbdkit-partitioning-plugin.pod | 5 +- tests/Makefile.am | 9 + tests/test-linuxdisk-copy-out.sh | 76 ++++++ tests/test-linuxdisk.sh | 93 +++++++ 16 files changed, 1412 insertions(+), 11 deletions(-) create mode 100644 plugins/linuxdisk/Makefile.am create mode 100644 plugins/linuxdisk/filesystem.c create mode 100644 plugins/linuxdisk/linuxdisk.c create mode 100644 plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod create mode 100644 plugins/linuxdisk/partition-gpt.c create mode 100644 plugins/linuxdisk/virtual-disk.c create mode 100644 plugins/linuxdisk/virtual-disk.h create mode 100755 tests/test-linuxdisk-copy-out.sh create mode 100755 tests/test-linuxdisk.sh diff --git a/README b/README index e394a1f..9c4d844 100644 --- a/README +++ b/README @@ -102,6 +102,10 @@ For the ext2 plugin: - com_err +For the linuxdisk plugin: + + - mke2fs >= 1.42.10 (from e2fsprogs) + For the Perl, example4 and tar plugins: - perl interpreter diff --git a/TODO b/TODO index 94de9c9..59590a1 100644 --- a/TODO +++ b/TODO @@ -85,13 +85,6 @@ directed to qemu-nbd for these use cases. https://lists.gnu.org/archive/html/qemu-devel/2017-11/msg02971.html is a partial solution but it needs cleaning up. -* Create ext2 filesystems - - Similar to nbdkit-floppy-plugin which creates FAT32 filesystems, we - could also create ext2 filesystems. This is relatively - straightforward using libext2fs (part of e2fsprogs). In fact we - already use this library for the unrelated nbdkit-ext2-plugin. - nbdkit-nbd-plugin could use enhancements: * FUA passthrough, rather than extra FLUSH calls. For this, .can_fua @@ -114,6 +107,10 @@ nbdkit-floppy-plugin: * Add multiple dir merging. +nbdkit-linuxdisk-plugin: + +* Add multiple dir merging (in e2fsprogs mke2fs). + Suggestions for filters ----------------------- diff --git a/configure.ac b/configure.ac index 229e38c..4487b5e 100644 --- a/configure.ac +++ b/configure.ac @@ -793,6 +793,7 @@ non_lang_plugins="\ gzip \ iso \ libvirt \ + linuxdisk \ memory \ nbd \ null \ @@ -855,6 +856,7 @@ AC_CONFIG_FILES([Makefile plugins/gzip/Makefile plugins/iso/Makefile plugins/libvirt/Makefile + plugins/linuxdisk/Makefile plugins/lua/Makefile plugins/memory/Makefile plugins/nbd/Makefile diff --git a/plugins/floppy/nbdkit-floppy-plugin.pod b/plugins/floppy/nbdkit-floppy-plugin.pod index ae14946..9087b86 100644 --- a/plugins/floppy/nbdkit-floppy-plugin.pod +++ b/plugins/floppy/nbdkit-floppy-plugin.pod @@ -18,6 +18,9 @@ The virtual floppy disk will have a single partition (using an MBR partition table). In that partition will be a virtual FAT32 filesystem containing the files. Long filenames are supported. +To create a CD/ISO, see L<nbdkit-iso-plugin(1)>. To create a Linux +compatible virtual disk, see L<nbdkit-linuxdisk-plugin(1)>. + =head1 EXAMPLE Create a virtual floppy disk: @@ -74,6 +77,7 @@ important, and it simplifies the implementation greatly. L<nbdkit(1)>, L<nbdkit-plugin(3)>, L<nbdkit-file-plugin(1)>, +L<nbdkit-linuxdisk-plugin(1)>, L<nbdkit-iso-plugin(1)>. =head1 AUTHORS diff --git a/plugins/iso/nbdkit-iso-plugin.pod b/plugins/iso/nbdkit-iso-plugin.pod index 4d9cf41..90e26f0 100644 --- a/plugins/iso/nbdkit-iso-plugin.pod +++ b/plugins/iso/nbdkit-iso-plugin.pod @@ -17,8 +17,9 @@ read-only over the NBD protocol. This plugin uses L<genisoimage(1)> or L<mkisofs(1)> to create the ISO content. -To create a virtual floppy disk instead of a CD, see -L<nbdkit-floppy-plugin(1)>. +To create a FAT-formatted virtual floppy disk instead of a CD, see +L<nbdkit-floppy-plugin(1)>. To create a Linux compatible virtual +disk, see L<nbdkit-linuxdisk-plugin(1)>. =head1 EXAMPLE @@ -96,6 +97,7 @@ L<nbdkit(1)>, L<nbdkit-plugin(3)>, L<nbdkit-file-plugin(1)>, L<nbdkit-floppy-plugin(1)>, +L<nbdkit-linuxdisk-plugin(1)>, L<genisoimage(1)>, L<mkisofs(1)>. diff --git a/plugins/linuxdisk/Makefile.am b/plugins/linuxdisk/Makefile.am new file mode 100644 index 0000000..1e94567 --- /dev/null +++ b/plugins/linuxdisk/Makefile.am @@ -0,0 +1,73 @@ +# nbdkit +# Copyright (C) 2019 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +include $(top_srcdir)/common-rules.mk + +EXTRA_DIST = nbdkit-linuxdisk-plugin.pod + +plugin_LTLIBRARIES = nbdkit-linuxdisk-plugin.la + +nbdkit_linuxdisk_plugin_la_SOURCES = \ + filesystem.c \ + linuxdisk.c \ + partition-gpt.c \ + virtual-disk.c \ + virtual-disk.h \ + $(top_srcdir)/include/nbdkit-plugin.h + +nbdkit_linuxdisk_plugin_la_CPPFLAGS = \ + -I$(top_srcdir)/common/gpt \ + -I$(top_srcdir)/common/include \ + -I$(top_srcdir)/common/regions \ + -I$(top_srcdir)/common/utils \ + -I$(top_srcdir)/include +nbdkit_linuxdisk_plugin_la_CFLAGS = \ + $(WARNINGS_CFLAGS) +nbdkit_linuxdisk_plugin_la_LIBADD = \ + $(top_builddir)/common/gpt/libgpt.la \ + $(top_builddir)/common/regions/libregions.la \ + $(top_builddir)/common/utils/libutils.la +nbdkit_linuxdisk_plugin_la_LDFLAGS = \ + -module -avoid-version -shared \ + -Wl,--version-script=$(top_srcdir)/plugins/plugins.syms + +if HAVE_POD + +man_MANS = nbdkit-linuxdisk-plugin.1 +CLEANFILES += $(man_MANS) + +nbdkit-linuxdisk-plugin.1: nbdkit-linuxdisk-plugin.pod + $(PODWRAPPER) --section=1 --man $@ \ + --html $(top_builddir)/html/$@.html \ + $< + +endif HAVE_POD diff --git a/plugins/linuxdisk/filesystem.c b/plugins/linuxdisk/filesystem.c new file mode 100644 index 0000000..a1d431a --- /dev/null +++ b/plugins/linuxdisk/filesystem.c @@ -0,0 +1,267 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <nbdkit-plugin.h> + +#include "minmax.h" +#include "rounding.h" +#include "utils.h" + +#include "virtual-disk.h" + +static int64_t estimate_size (void); +static int mke2fs (const char *filename); + +int +create_filesystem (struct virtual_disk *disk) +{ + const char *tmpdir; + char *filename = NULL; + int fd = -1; + + /* Estimate the filesystem size and compute the final virtual size + * of the disk. We only need to do this if the user didn't specify + * the exact size on the command line. + */ + if (size == 0 || size_add_estimate) { + int64_t estimate; + + estimate = estimate_size (); + if (estimate == -1) + goto error; + + nbdkit_debug ("filesystem size estimate: %" PRIi64, estimate); + + /* Add 20% to the estimate to account for the overhead of + * filesystem metadata. Also set a minimum size. Note we are + * only wasting virtual space (since this will be stored sparsely + * under $TMPDIR) so we can be generous here. + */ + estimate = estimate * 6 / 5; + estimate = MAX (estimate, 1024*1024); + + /* For ext3 and ext4, add something for the journal. */ + if (strncmp (type, "ext", 3) == 0 && type[3] > '2') + estimate += 32*1024*1024; + + if (size_add_estimate) + size += estimate; + else + size = estimate; + } + + /* Round the final size up to a whole number of sectors. */ + size = ROUND_UP (size, SECTOR_SIZE); + + nbdkit_debug ("filesystem virtual size: %" PRIi64, size); + + /* Create the filesystem file. */ + tmpdir = getenv ("TMPDIR"); + if (tmpdir == NULL) + tmpdir = LARGE_TMPDIR; + if (asprintf (&filename, "%s/linuxdiskXXXXXX", tmpdir) == -1) { + nbdkit_error ("asprintf: %m"); + goto error; + } + + fd = mkstemp (filename); + if (fd == -1) { + nbdkit_error ("mkstemp: %s: %m", filename); + goto error; + } + if (ftruncate (fd, size) == -1) { + nbdkit_error ("ftruncate: %s: %m", filename); + goto error; + } + + /* Create the filesystem. */ + if (mke2fs (filename) == -1) + goto error; + + unlink (filename); + free (filename); + disk->filesystem_size = size; + disk->fd = fd; + return 0; + + error: + if (fd >= 0) + close (fd); + if (filename) { + unlink (filename); + free (filename); + } + return -1; +} + +/* Use ‘du’ to estimate the size of the filesystem quickly. We use + * the -c option to allow the possibility of supporting multiple + * directories in future. + * + * Typical output from ‘du -cs dir1 dir2’ is: + * + * 12345 dir1 + * 34567 dir2 + * 46912 total + * + * We ignore everything except the first number on the last line. + */ +static int64_t +estimate_size (void) +{ + char *command = NULL, *line = NULL; + size_t len = 0; + FILE *fp; + int64_t ret; + int r; + + /* Create the du command. */ + fp = open_memstream (&command, &len); + if (fp == NULL) { + nbdkit_error ("open_memstream: %m"); + return -1; + } + fprintf (fp, "du -c -k -s "); + shell_quote (dir, fp); + if (fclose (fp) == EOF) { + nbdkit_error ("memstream failed: %m"); + return -1; + } + + /* Run the command. */ + nbdkit_debug ("%s", command); + fp = popen (command, "r"); + free (command); + if (fp == NULL) { + nbdkit_error ("du command failed: %m"); + return -1; + } + + /* Ignore everything up to the last line. */ + len = 0; + while (getline (&line, &len, fp) != -1) + /* empty */; + if (ferror (fp)) { + nbdkit_error ("getline failed: %m"); + free (line); + pclose (fp); + return -1; + } + + r = pclose (fp); + if (r == -1) { + nbdkit_error ("pclose: %m"); + free (line); + return -1; + } + if (WIFEXITED (r) && WEXITSTATUS (r) != 0) { + nbdkit_error ("du command failed with exit code %d", WEXITSTATUS (r)); + return -1; + } + + /* Parse the last line. */ + if (sscanf (line, "%" SCNi64, &ret) != 1 || ret < 0) { + nbdkit_error ("could not parse last line of output: %s", line); + free (line); + return -1; + } + free (line); + + /* Result is in 1K blocks, convert it to bytes. */ + ret *= 1024; + return ret; +} + +static int +mke2fs (const char *filename) +{ + char *command = NULL; + size_t len = 0; + FILE *fp; + int r; + + /* Create the mke2fs command. */ + fp = open_memstream (&command, &len); + if (fp == NULL) { + nbdkit_error ("open_memstream: %m"); + return -1; + } + + fprintf (fp, "mke2fs -F -t %s ", type); + if (label) { + fprintf (fp, "-L "); + shell_quote (label, fp); + fprintf (fp, " "); + } + fprintf (fp, "-d "); + shell_quote (dir, fp); + fprintf (fp, " "); + shell_quote (filename, fp); + + if (fclose (fp) == EOF) { + nbdkit_error ("memstream failed: %m"); + return -1; + } + + /* Run the command. */ + nbdkit_debug ("%s", command); + r = system (command); + free (command); + + if (WIFEXITED (r) && WEXITSTATUS (r) != 0) { + nbdkit_error ("mke2fs command failed with exit code %d", WEXITSTATUS (r)); + return -1; + } + else if (WIFSIGNALED (r)) { + nbdkit_error ("mke2fs command was killed by signal %d", WTERMSIG (r)); + return -1; + } + else if (WIFSTOPPED (r)) { + nbdkit_error ("mke2fs command was stopped by signal %d", WSTOPSIG (r)); + return -1; + } + + return 0; +} diff --git a/plugins/linuxdisk/linuxdisk.c b/plugins/linuxdisk/linuxdisk.c new file mode 100644 index 0000000..8f50dd3 --- /dev/null +++ b/plugins/linuxdisk/linuxdisk.c @@ -0,0 +1,229 @@ +/* nbdkit + * Copyright (C) 2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#define NBDKIT_API_VERSION 2 + +#include <nbdkit-plugin.h> + +#include "random.h" +#include "regions.h" + +#include "virtual-disk.h" + +/* Directory, label, type, size parameters. */ +char *dir; +const char *label; +const char *type = "ext2"; +int64_t size; +bool size_add_estimate; /* if size=+SIZE was used */ + +/* Virtual disk. */ +static struct virtual_disk disk; + +/* Used to create a random GUID for the partition. */ +struct random_state random_state; + +static void +linuxdisk_load (void) +{ + init_virtual_disk (&disk); + xsrandom (time (NULL), &random_state); +} + +static void +linuxdisk_unload (void) +{ + free_virtual_disk (&disk); + free (dir); +} + +static int +linuxdisk_config (const char *key, const char *value) +{ + if (strcmp (key, "dir") == 0) { + if (dir != NULL) { + /* TODO: Support merging of multiple directories, like iso plugin. */ + nbdkit_error ("dir=<DIRECTORY> must only be set once"); + return -1; + } + dir = nbdkit_realpath (value); + if (dir == NULL) + return -1; + } + else if (strcmp (key, "label") == 0) { + label = value; + } + else if (strcmp (key, "type") == 0) { + if (strncmp (value, "ext", 3) != 0) { + nbdkit_error ("type=<TYPE> must be an filesystem type " + "supported by e2fsprogs"); + return -1; + } + type = value; + } + else if (strcmp (key, "size") == 0) { + if (value[0] == '+') { + size_add_estimate = true; + value++; + } + else + size_add_estimate = false; + size = nbdkit_parse_size (value); + if (size == -1) + return -1; + } + else { + nbdkit_error ("unknown parameter '%s'", key); + return -1; + } + + return 0; +} + +static int +linuxdisk_config_complete (void) +{ + if (dir == NULL) { + nbdkit_error ("you must supply the dir=<DIRECTORY> parameter " + "after the plugin name on the command line"); + return -1; + } + + return create_virtual_disk (&disk); +} + +#define linuxdisk_config_help \ + "dir=<DIRECTORY> (required) The directory to serve.\n" \ + "label=<LABEL> The filesystem label.\n" \ + "type=ext2|ext3|ext4 The filesystem type.\n" \ + "size=[+]<SIZE> The virtual filesystem size." + +static void * +linuxdisk_open (int readonly) +{ + return NBDKIT_HANDLE_NOT_NEEDED; +} + +#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL + +/* Get the file size. */ +static int64_t +linuxdisk_get_size (void *handle) +{ + return virtual_size (&disk.regions); +} + +/* Serves the same data over multiple connections. */ +static int +linuxdisk_can_multi_conn (void *handle) +{ + return 1; +} + +/* Read data from the virtual disk. */ +static int +linuxdisk_pread (void *handle, void *buf, uint32_t count, uint64_t offset, + uint32_t flags) +{ + while (count > 0) { + const struct region *region = find_region (&disk.regions, offset); + size_t len; + ssize_t r; + + /* Length to end of region. */ + len = region->end - offset + 1; + if (len > count) + len = count; + + switch (region->type) { + case region_file: + /* We don't use region->u.i since there is only one backing + * file, and we have that open already (in ‘disk.fd’). + */ + r = pread (disk.fd, buf, len, offset - region->start); + if (r == -1) { + nbdkit_error ("pread: %m"); + return -1; + } + if (r == 0) { + nbdkit_error ("pread: unexpected end of file"); + return -1; + } + len = r; + break; + + case region_data: + memcpy (buf, ®ion->u.data[offset - region->start], len); + break; + + case region_zero: + memset (buf, 0, len); + break; + } + + count -= len; + buf += len; + offset += len; + } + + return 0; +} + +static struct nbdkit_plugin plugin = { + .name = "linuxdisk", + .longname = "nbdkit Linux virtual disk plugin", + .version = PACKAGE_VERSION, + .load = linuxdisk_load, + .unload = linuxdisk_unload, + .config = linuxdisk_config, + .config_complete = linuxdisk_config_complete, + .config_help = linuxdisk_config_help, + .magic_config_key = "dir", + .open = linuxdisk_open, + .get_size = linuxdisk_get_size, + .can_multi_conn = linuxdisk_can_multi_conn, + .pread = linuxdisk_pread, + .errno_is_preserved = 1, +}; + +NBDKIT_REGISTER_PLUGIN(plugin) diff --git a/plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod b/plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod new file mode 100644 index 0000000..2483837 --- /dev/null +++ b/plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod @@ -0,0 +1,180 @@ +=head1 NAME + +nbdkit-linuxdisk-plugin - create virtual Linux disk from directory + +=head1 SYNOPSIS + + nbdkit linuxdisk [dir=]DIRECTORY + [label=LABEL] [type=ext2|ext3|ext4] + [size=[+]SIZE] + +=head1 DESCRIPTION + +C<nbdkit-linuxdisk-plugin> is a plugin for L<nbdkit(1)> which creates +an ext2-, ext3- or ext4-formatted disk image from a directory on the +fly. The files in the specified directory (and subdirectories) appear +in the virtual disk, which is served read-only over the NBD protocol. + +The virtual disk is partitioned with a single GPT partition containing +the filesystem. + +The virtual disk can be used as a Linux root (or other) filesystem. +Most features of Linux filesystems are supported, such as hard links, +symbolic links, block special devices etc. + +To create a FAT-formatted virtual floppy disk, see +L<nbdkit-floppy-plugin(1)>. To create a CD/ISO, see +L<nbdkit-iso-plugin(1)>. + +=head1 EXAMPLES + +=over 4 + +=item nbdkit linuxdisk /path/to/directory label=ROOTFS + +Create a virtual disk, giving it a filesystem label. Note that +clients will not be able to modify the filesystem, so it is safe to +share it with multiple clients. + +=item nbdkit --filter=cow linuxdisk /path/to/directory + +Add a writable overlay (see L<nbdkit-cow-filter(1)>, allowing the disk +to be written by the client. B<Multiple clients must not be allowed +to connect at the same time> (even if they all mount it read-only) as +this will cause disk corruption. + +=item nbdkit --filter=cow linuxdisk /path/to/directory size=+1G + +The same but specifying that at least 1G of free space should be +available in the filesystem (not including the space taken by the +initial filesystem). + +=item nbdkit --filter=partition linuxdisk /path/to/directory partition=1 + +Instead of serving a partitioned disk image, serve just the "naked" +filesystem (ie. the first partition, see +L<nbdkit-partition-filter(1)>). + +=item nbdkit -U - linuxdisk /path/to/directory +--run 'qemu-img convert $nbd ext2fs.img' + +This serves nothing. Instead it turns a directory into a disk image, +writing it to F<ext2fs.img> (see L<nbdkit-captive(1)>). The resulting +image is a partitioned disk. + +=item Create a minimal virtual appliance + +This creates and boots a minimal L<busybox(1)>-based virtual +appliance. This assumes that your kernel (F</boot/vmlinuz>) contains +the ext2 or ext4 driver compiled in, but most Linux distro kernels +have that. + + mkdir root root/bin root/dev root/proc root/sbin root/sys + mkdir root/usr root/usr/bin root/usr/sbin + sudo mknod root/dev/console c 5 1 + cp /sbin/busybox root/sbin/ + ln root/sbin/busybox root/sbin/init + ln root/sbin/busybox root/bin/ls + ln root/sbin/busybox root/bin/sh + nbdkit -U - linuxdisk root --run ' + qemu-kvm -display none -kernel /boot/vmlinuz -drive file=nbd:unix:$unixsocket,snapshot=on -append "console=ttyS0 root=/dev/sda1 rw" -serial stdio + ' + +You can drop any extra files you need into the F<root/> directory and +they will be copied into the appliance before boot. After booting +type these commands to complete the environmental setup: + + /sbin/busybox --install + mount -t proc proc /proc + mount -t sysfs sys /sys + +=back + +=head1 PARAMETERS + +=over 4 + +=item [B<dir=>]DIRECTORY + +Specify the directory containing files and subdirectories which will +be added to the virtual disk. Files inside this directory will appear +in the root directory of the virtual disk. + +This parameter is required. + +C<dir=> is a magic config key and may be omitted in most cases. +See L<nbdkit(1)/Magic parameters>. + +=item B<label=>LABEL + +The optional label for the filesystem. + +=item B<size=>SIZE + +=item B<size=+>SIZE + +The total (virtual) size of the filesystem. + +If the C<size> parameter is omitted the plugin will try to size the +filesystem with just enough space to contain the files and directories +that are initially loaded, and there will not be much extra space. + +Using C<size=SIZE> specifies the required virtual size of the whole +filesystem (including initial files and extra space). If this is set +too small for the initial filesystem then the plugin will fail to +start. + +Using C<size=+SIZE> specifies the minimum free space required after +the initial filesystem has been loaded. (The actual free space might +be slightly larger). + +=item B<type=ext2> + +=item B<type=ext3> + +=item B<type=ext4> + +Select the filesystem type. The default is C<ext2>. + +=back + +=head1 NOTES + +=head2 Users and groups. + +The original file UIDs and GIDs are recreated as far as possible. +Note that UIDs/GIDs will likely map to different users and groups when +read by a virtual machine or other NBD client machine. + +=head1 ENVIRONMENT VARIABLES + +=over 4 + +=item C<TMPDIR> + +The filesystem image is stored in a temporary file located in +F</var/tmp> by default. You can override this location by setting the +C<TMPDIR> environment variable before starting nbdkit. + +=back + +=head1 SEE ALSO + +L<mke2fs(8)>, +L<nbdkit(1)>, +L<nbdkit-plugin(3)>, +L<nbdkit-captive(1)>, +L<nbdkit-cow-filter(1)>, +L<nbdkit-file-plugin(1)>, +L<nbdkit-floppy-plugin(1)>, +L<nbdkit-iso-plugin(1)>, +L<nbdkit-partition-filter(1)>, +L<nbdkit-partitioning-plugin(1)>. + +=head1 AUTHORS + +Richard W.M. Jones + +=head1 COPYRIGHT + +Copyright (C) 2019 Red Hat Inc. diff --git a/plugins/linuxdisk/partition-gpt.c b/plugins/linuxdisk/partition-gpt.c new file mode 100644 index 0000000..3278229 --- /dev/null +++ b/plugins/linuxdisk/partition-gpt.c @@ -0,0 +1,211 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +#include <nbdkit-plugin.h> + +#include "efi-crc32.h" +#include "gpt.h" +#include "isaligned.h" +#include "rounding.h" +#include "regions.h" + +#include "virtual-disk.h" + +#define PARTITION_TYPE_GUID "0FC63DAF-8483-4772-8E79-3D69D8477DE4" + +static void create_gpt_protective_mbr (struct virtual_disk *disk, + unsigned char *out); +static void create_gpt_partition_header (struct virtual_disk *disk, + const void *pt, bool is_primary, + unsigned char *out); +static void create_gpt_partition_table (struct virtual_disk *disk, + unsigned char *out); + +/* Initialize the partition table structures. */ +int +create_partition_table (struct virtual_disk *disk) +{ + create_gpt_protective_mbr (disk, disk->protective_mbr); + + create_gpt_partition_table (disk, disk->pt); + + create_gpt_partition_header (disk, disk->pt, true, disk->primary_header); + create_gpt_partition_header (disk, disk->pt, false, disk->secondary_header); + + return 0; +} + +static void +chs_too_large (unsigned char *out) +{ + const int c = 1023, h = 254, s = 63; + + out[0] = h; + out[1] = (c & 0x300) >> 2 | s; + out[2] = c & 0xff; +} + +static void +create_mbr_partition_table_entry (const struct region *region, + bool bootable, int partition_id, + unsigned char *out) +{ + uint64_t start_sector, nr_sectors; + uint32_t u32; + + assert (IS_ALIGNED (region->start, SECTOR_SIZE)); + + start_sector = region->start / SECTOR_SIZE; + nr_sectors = DIV_ROUND_UP (region->len, SECTOR_SIZE); + + assert (start_sector <= UINT32_MAX); + assert (nr_sectors <= UINT32_MAX); + + out[0] = bootable ? 0x80 : 0; + chs_too_large (&out[1]); + out[4] = partition_id; + chs_too_large (&out[5]); + u32 = htole32 (start_sector); + memcpy (&out[8], &u32, 4); + u32 = htole32 (nr_sectors); + memcpy (&out[12], &u32, 4); +} + +static void +create_gpt_protective_mbr (struct virtual_disk *disk, unsigned char *out) +{ + struct region region; + uint64_t end; + + /* Protective MBR creates an MBR partition with partition ID 0xee + * which covers the whole of the disk, or as much of the disk as + * expressible with MBR. + */ + region.start = 512; + end = virtual_size (&disk->regions) - 1; + if (end > UINT32_MAX * SECTOR_SIZE) + end = UINT32_MAX * SECTOR_SIZE; + region.end = end; + region.len = region.end - region.start + 1; + + create_mbr_partition_table_entry (®ion, false, 0xee, &out[0x1be]); + + /* Boot sector signature. */ + out[0x1fe] = 0x55; + out[0x1ff] = 0xaa; +} + +static void +create_gpt_partition_header (struct virtual_disk *disk, + const void *pt, bool is_primary, + unsigned char *out) +{ + uint64_t nr_lbas; + struct gpt_header *header = (struct gpt_header *) out; + + nr_lbas = virtual_size (&disk->regions) / SECTOR_SIZE; + + memset (header, 0, sizeof *header); + memcpy (header->signature, GPT_SIGNATURE, sizeof (header->signature)); + memcpy (header->revision, GPT_REVISION, sizeof (header->revision)); + header->header_size = htole32 (sizeof *header); + if (is_primary) { + header->current_lba = htole64 (1); + header->backup_lba = htole64 (nr_lbas - 1); + } + else { + header->current_lba = htole64 (nr_lbas - 1); + header->backup_lba = htole64 (1); + } + header->first_usable_lba = htole64 (34); + header->last_usable_lba = htole64 (nr_lbas - 34); + if (is_primary) + header->partition_entries_lba = htole64 (2); + else + header->partition_entries_lba = htole64 (nr_lbas - 33); + header->nr_partition_entries = htole32 (GPT_MIN_PARTITIONS); + header->size_partition_entry = htole32 (GPT_PT_ENTRY_SIZE); + header->crc_partitions + htole32 (efi_crc32 (pt, GPT_PT_ENTRY_SIZE * GPT_MIN_PARTITIONS)); + + /* Must be computed last. */ + header->crc = htole32 (efi_crc32 (header, sizeof *header)); +} + +static void +create_gpt_partition_table_entry (const struct region *region, + bool bootable, + char partition_type_guid[16], + char guid[16], + unsigned char *out) +{ + struct gpt_entry *entry = (struct gpt_entry *) out; + + assert (sizeof (struct gpt_entry) == GPT_PT_ENTRY_SIZE); + + memcpy (entry->partition_type_guid, partition_type_guid, 16); + memcpy (entry->unique_guid, guid, 16); + + entry->first_lba = htole64 (region->start / SECTOR_SIZE); + entry->last_lba = htole64 (region->end / SECTOR_SIZE); + entry->attributes = htole64 (bootable ? 4 : 0); +} + +static void +create_gpt_partition_table (struct virtual_disk *disk, unsigned char *out) +{ + size_t j; + + for (j = 0; j < nr_regions (&disk->regions); ++j) { + const struct region *region = get_region (&disk->regions, j); + + /* Find the (only) partition region, which has type region_file. */ + if (region->type == region_file) { + create_gpt_partition_table_entry (region, true, + PARTITION_TYPE_GUID, + disk->guid, + out); + out += GPT_PT_ENTRY_SIZE; + } + } +} diff --git a/plugins/linuxdisk/virtual-disk.c b/plugins/linuxdisk/virtual-disk.c new file mode 100644 index 0000000..2f24ce7 --- /dev/null +++ b/plugins/linuxdisk/virtual-disk.c @@ -0,0 +1,161 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +#include <nbdkit-plugin.h> + +#include "random.h" +#include "regions.h" + +#include "virtual-disk.h" + +static int create_regions (struct virtual_disk *disk); + +void +init_virtual_disk (struct virtual_disk *disk) +{ + memset (disk, 0, sizeof *disk); + disk->fd = -1; + + init_regions (&disk->regions); +} + +int +create_virtual_disk (struct virtual_disk *disk) +{ + size_t i; + + /* Allocate the partition table structures. We can't fill them in + * until we have created the disk layout. + */ + disk->protective_mbr = calloc (1, SECTOR_SIZE); + disk->primary_header = calloc (1, SECTOR_SIZE); + disk->pt = calloc (1, 32*SECTOR_SIZE); + disk->secondary_header = calloc (1, SECTOR_SIZE); + if (disk->protective_mbr == NULL || + disk->primary_header == NULL || + disk->pt == NULL || + disk->secondary_header == NULL) { + nbdkit_error ("calloc: %m"); + return -1; + } + + /* Create the filesystem. This fills in disk->filesystem_size and + * disk->id. + */ + if (create_filesystem (disk) == -1) + return -1; + + /* Create a random GUID used as "Unique partition GUID". However + * this doesn't follow GUID conventions so in theory could make an + * invalid value. + */ + for (i = 0; i < 16; ++i) + disk->guid[i] = xrandom (&random_state) & 0xff; + + /* Create the virtual disk regions. */ + if (create_regions (disk) == -1) + return -1; + + /* Initialize partition table structures. This depends on + * disk->regions so must be done last. + */ + if (create_partition_table (disk) == -1) + return -1; + + return 0; +} + +void +free_virtual_disk (struct virtual_disk *disk) +{ + free_regions (&disk->regions); + free (disk->protective_mbr); + free (disk->primary_header); + free (disk->pt); + free (disk->secondary_header); + if (disk->fd >= 0) + close (disk->fd); +} + +/* Lay out the final disk. */ +static int +create_regions (struct virtual_disk *disk) +{ + /* Protective MBR. */ + if (append_region_len (&disk->regions, "Protective MBR", + SECTOR_SIZE, 0, 0, + region_data, (void *) disk->protective_mbr) == -1) + return -1; + + /* GPT primary partition table header (LBA 1). */ + if (append_region_len (&disk->regions, "GPT primary header", + SECTOR_SIZE, 0, 0, + region_data, (void *) disk->primary_header) == -1) + return -1; + + /* GPT primary PT (LBA 2..33). */ + if (append_region_len (&disk->regions, "GPT primary PT", + 32*SECTOR_SIZE, 0, 0, + region_data, (void *) disk->pt) == -1) + return -1; + + /* Partition containing the filesystem. Align it to 2048 sectors. */ + if (append_region_len (&disk->regions, "Filesystem", + disk->filesystem_size, 2048*SECTOR_SIZE, 0, + region_file, 0 /* unused */) == -1) + return -1; + + /* GPT secondary PT (LBA -33..-2). */ + if (append_region_len (&disk->regions, "GPT secondary PT", + 32*SECTOR_SIZE, SECTOR_SIZE, 0, + region_data, (void *) disk->pt) == -1) + return -1; + + /* GPT secondary PT header (LBA -1). */ + if (append_region_len (&disk->regions, "GPT secondary header", + SECTOR_SIZE, 0, 0, + region_data, (void *) disk->secondary_header) == -1) + return -1; + + return 0; +} diff --git a/plugins/linuxdisk/virtual-disk.h b/plugins/linuxdisk/virtual-disk.h new file mode 100644 index 0000000..2eee177 --- /dev/null +++ b/plugins/linuxdisk/virtual-disk.h @@ -0,0 +1,92 @@ +/* nbdkit + * Copyright (C) 2018-2019 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NBDKIT_VIRTUAL_DISK_H +#define NBDKIT_VIRTUAL_DISK_H + +#include <stdbool.h> +#include <stdint.h> + +#include "regions.h" + +extern char *dir; +extern const char *label; +extern const char *type; +extern int64_t size; +extern bool size_add_estimate; + +extern struct random_state random_state; + +#define SECTOR_SIZE 512 + +struct virtual_disk { + /* Virtual disk layout. */ + struct regions regions; + + /* Disk protective MBR. */ + uint8_t *protective_mbr; + + /* GPT primary partition table header. */ + uint8_t *primary_header; + + /* GPT primary and secondary (backup) PTs. These are the same. */ + uint8_t *pt; + + /* GPT secondary (backup) PT header. */ + uint8_t *secondary_header; + + /* Size of the filesystem in bytes. */ + uint64_t filesystem_size; + + /* Unique partition GUID. */ + char guid[16]; + + /* File descriptor of the temporary file containing the filesystem. */ + int fd; +}; + +/* virtual-disk.c */ +extern void init_virtual_disk (struct virtual_disk *disk) + __attribute__((__nonnull__ (1))); +extern int create_virtual_disk (struct virtual_disk *disk) + __attribute__((__nonnull__ (1))); +extern void free_virtual_disk (struct virtual_disk *disk) + __attribute__((__nonnull__ (1))); + +/* partition-gpt.c */ +extern int create_partition_table (struct virtual_disk *disk); + +/* filesystem.c */ +extern int create_filesystem (struct virtual_disk *disk); + +#endif /* NBDKIT_VIRTUAL_DISK_H */ diff --git a/plugins/partitioning/nbdkit-partitioning-plugin.pod b/plugins/partitioning/nbdkit-partitioning-plugin.pod index f3d6996..d045e4c 100644 --- a/plugins/partitioning/nbdkit-partitioning-plugin.pod +++ b/plugins/partitioning/nbdkit-partitioning-plugin.pod @@ -19,8 +19,8 @@ If you just want to concatenate files together (without adding a partition table) use L<nbdkit-split-plugin(1)>. If you want to select a single partition from an existing disk, use L<nbdkit-partition-filter(1)>. If you want to create a complete disk -with a filesystem, look at L<nbdkit-floppy-plugin(1)> or -L<nbdkit-iso-plugin(1)>. +with a filesystem, look at L<nbdkit-floppy-plugin(1)>, +L<nbdkit-iso-plugin(1)> or L<nbdkit-linuxdisk-plugin(1)>. The plugin supports read/write access. To limit clients to read-only access use the I<-r> flag. @@ -172,6 +172,7 @@ L<nbdkit(1)>, L<nbdkit-file-plugin(1)>, L<nbdkit-floppy-plugin(1)>, L<nbdkit-iso-plugin(1)>, +L<nbdkit-linuxdisk-plugin(1)>, L<nbdkit-partition-filter(1)>, L<nbdkit-split-plugin(1)>, L<nbdkit-plugin(3)>. diff --git a/tests/Makefile.am b/tests/Makefile.am index c75a9de..3992d9b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -71,6 +71,8 @@ EXTRA_DIST = \ test-ip.sh \ test-iso.sh \ test-layers.sh \ + test-linuxdisk.sh \ + test-linuxdisk-copy-out.sh \ test-log.sh \ test.lua \ test-memory-largest.sh \ @@ -447,6 +449,13 @@ TESTS += test-iso.sh endif HAVE_GUESTFISH endif HAVE_ISO +# linuxdisk plugin test. +if HAVE_GUESTFISH +TESTS += \ + test-linuxdisk.sh \ + test-linuxdisk-copy-out.sh +endif HAVE_GUESTFISH + # memory plugin test. LIBGUESTFS_TESTS += test-memory TESTS += test-memory-largest.sh test-memory-largest-for-qemu.sh diff --git a/tests/test-linuxdisk-copy-out.sh b/tests/test-linuxdisk-copy-out.sh new file mode 100755 index 0000000..55062d0 --- /dev/null +++ b/tests/test-linuxdisk-copy-out.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright (C) 2018-2019 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# Test the linuxdisk plugin with captive nbdkit, as described +# in the man page. + +source ./functions.sh +set -e +set -x + +requires qemu-img --version + +files="linuxdisk-copy-out.img + linuxdisk-copy-out.test1 linuxdisk-copy-out.test2 + linuxdisk-copy-out.test3 linuxdisk-copy-out.test4" +rm -f $files +cleanup_fn rm -f $files + +nbdkit -f -v -U - \ + --filter=partition \ + linuxdisk $srcdir/../plugins partition=1 label=ROOT \ + --run 'qemu-img convert $nbd linuxdisk-copy-out.img' + +# Check the disk content. +guestfish --ro -a linuxdisk-copy-out.img -m /dev/sda <<EOF +# Check some known files and directories exist. + ll / + ll /linuxdisk + is-dir /linuxdisk + is-file /linuxdisk/Makefile.am + +# This reads out all the directory entries and all file contents. + tar-out / - | cat >/dev/null + +# Download some files and compare to local copies. + download /linuxdisk/Makefile linuxdisk-copy-out.test1 + download /linuxdisk/Makefile.am linuxdisk-copy-out.test2 + download /linuxdisk/nbdkit-linuxdisk-plugin.pod linuxdisk-copy-out.test3 + download /linuxdisk/filesystem.c linuxdisk-copy-out.test4 +EOF + +# Compare downloaded files to local versions. +cmp linuxdisk-copy-out.test1 $srcdir/../plugins/linuxdisk/Makefile +cmp linuxdisk-copy-out.test2 $srcdir/../plugins/linuxdisk/Makefile.am +cmp linuxdisk-copy-out.test3 $srcdir/../plugins/linuxdisk/nbdkit-linuxdisk-plugin.pod +cmp linuxdisk-copy-out.test4 $srcdir/../plugins/linuxdisk/filesystem.c diff --git a/tests/test-linuxdisk.sh b/tests/test-linuxdisk.sh new file mode 100755 index 0000000..6105c96 --- /dev/null +++ b/tests/test-linuxdisk.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright (C) 2018-2019 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# Test the linuxdisk plugin. + +source ./functions.sh +set -e +set -x + +requires mkfifo --version + +d=linuxdisk.d +rm -rf $d +cleanup_fn rm -rf $d + +# Create a test directory with some regular files, subdirectories and +# special files. +mkdir $d +mkfifo $d/fifo +mkdir $d/sub +cp $srcdir/Makefile.am $d/sub/Makefile.am +ln $d/sub/Makefile.am $d/sub/hardlink +ln -s $d/sub/Makefile.am $d/sub/symlink + +# It would be nice to use the Unix domain socket to test that the +# socket gets created, but in fact that won't work because this socket +# isn't created until after the plugin creates the virtual disk. +start_nbdkit -P $d/linuxdisk.pid \ + -U $d/linuxdisk.sock \ + linuxdisk $d + +# Check the disk content. +guestfish --ro --format=raw -a "nbd://?socket=$PWD/$d/linuxdisk.sock" -m /dev/sda1 <<EOF + ll / + ll /sub + +# Check regular files exist. + is-file /sub/Makefile.am + is-file /sub/hardlink +# XXX Test sparse files in future. + +# Check the specials exist. + is-fifo /fifo + is-symlink /sub/symlink +# XXX Test sockets, etc. in future. + +# Check hard linked files. + lstatns /sub/Makefile.am | cat > $d/nlink.1 + lstatns /sub/hardlink | cat > $d/nlink.2 + +# This reads out all the directory entries and all file contents. + tar-out / - | cat >/dev/null + +# Download file and compare to local copy. + download /sub/Makefile.am $d/Makefile.am +EOF + +# Check the two hard linked files have st_nlink == 2. +grep "st_nlink: 2" $d/nlink.1 +grep "st_nlink: 2" $d/nlink.2 + +# Compare downloaded file to local version. +cmp $d/Makefile.am $srcdir/Makefile.am -- 1.8.3.1
Eric Blake
2019-Feb-22 15:42 UTC
Re: [Libguestfs] [PATCH nbdkit v3 4/4] Add linuxdisk plugin.
On 2/22/19 3:07 AM, Richard W.M. Jones wrote:> From: "Richard W.M. Jones" <rjones@redhat.com> > > This plugin allows you to create a complete ext2, ext3 or ext4 > filesystem in a GPT partitioned disk image. This can be attached as a > disk to a Linux virtual machine. It is implemented using e2fsprogs > mke2fs ‘-d’ option thus allowing the implementation to be very small > and simple, with all the hard work done by mke2fs. > > Although there is some overlap with nbdkit-iso-plugin and > nbdkit-floppy-plugin, the implementations and use cases of all three> +static int64_t > +estimate_size (void) > +{> > + r = pclose (fp); > + if (r == -1) { > + nbdkit_error ("pclose: %m"); > + free (line); > + return -1; > + } > + if (WIFEXITED (r) && WEXITSTATUS (r) != 0) { > + nbdkit_error ("du command failed with exit code %d", WEXITSTATUS (r)); > + return -1; > + } > +Doesn't cover if the process died due to signal.> +static int > +mke2fs (const char *filename) > +{> + /* Run the command. */ > + nbdkit_debug ("%s", command); > + r = system (command); > + free (command); > + > + if (WIFEXITED (r) && WEXITSTATUS (r) != 0) { > + nbdkit_error ("mke2fs command failed with exit code %d", WEXITSTATUS (r)); > + return -1; > + } > + else if (WIFSIGNALED (r)) { > + nbdkit_error ("mke2fs command was killed by signal %d", WTERMSIG (r)); > + return -1; > + } > + else if (WIFSTOPPED (r)) { > + nbdkit_error ("mke2fs command was stopped by signal %d", WSTOPSIG (r)); > + return -1; > + } > +WIFSTOPPED() branch is unreachable.> +=item nbdkit --filter=cow linuxdisk /path/to/directory > + > +Add a writable overlay (see L<nbdkit-cow-filter(1)>, allowing the disk > +to be written by the client. B<Multiple clients must not be allowed > +to connect at the same time> (even if they all mount it read-only) as > +this will cause disk corruption.Not a cause for changing any of the wording, but just to make sure I understand: even when a filesystem is mounted read-only, the kernel may still write to the underlying block device (such as fsck or journal updates), and THESE writes must only be done by a single writer, hence allowing multiple writers to a cow wrapper around the read-only file system indeed risks races where one writer corrupts another.> +=item Create a minimal virtual appliance > + > +This creates and boots a minimal L<busybox(1)>-based virtual > +appliance. This assumes that your kernel (F</boot/vmlinuz>) contains > +the ext2 or ext4 driver compiled in, but most Linux distro kernels > +have that. > + > + mkdir root root/bin root/dev root/proc root/sbin root/sys > + mkdir root/usr root/usr/bin root/usr/sbinWorth 'mkdir -p' in this example?> + sudo mknod root/dev/console c 5 1 > + cp /sbin/busybox root/sbin/ > + ln root/sbin/busybox root/sbin/init > + ln root/sbin/busybox root/bin/ls > + ln root/sbin/busybox root/bin/sh > + nbdkit -U - linuxdisk root --run ' > + qemu-kvm -display none -kernel /boot/vmlinuz -drive file=nbd:unix:$unixsocket,snapshot=on -append "console=ttyS0 root=/dev/sda1 rw" -serial stdio > + ' > + > +You can drop any extra files you need into the F<root/> directory and > +they will be copied into the appliance before boot. After booting > +type these commands to complete the environmental setup: > + > + /sbin/busybox --install > + mount -t proc proc /proc > + mount -t sysfs sys /sysPretty cool! I think you're ready to go, with minor tweaks to child process status handling. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org