[libvirt] [PATCH]: Export the 'label' on block devices
Jim Meyering
jim at meyering.net
Thu Oct 16 08:53:35 UTC 2008
Chris Lalancette <clalance at redhat.com> wrote:
> To support LVM partitioning in oVirt, one of the things we need is the ability
> to tell what kind of label is currently on a block device. Here, a 'label' is
> used in the same sense that it is used in parted; namely, it defines which kind
> of partition table is on the disk, whether it be DOS, LVM2, SUN, BSD, etc. Note
> that this is different than the partition type; those are things like Linux,
> FAT16, FAT32, etc.
>
> This actually turns out to be fairly easy to implement; there are really only a
> few labels that are in common use, and they all have an easy signature to
> recognize (but see comments in the code about pc98). This patch implements
> label detection on block devices in virStorageBackendUpdateVolInfoFD, and hooks
> it up to the iSCSI backend so it works there.
>
> To keep code duplication down, I moved some of the enum's from
> storage_backend_disk.c into a common place. Note, however, that there is a
> slight semantic change because of this. Previously, if no label was found on a
> disk in storage_backend_disk.c, it would always return "dos" as the label type.
> That's not actually true, though; if it's a completely zeroed disk, for
> instance, it really just has label type of 'unknown'. This patch changes to the
> new semantic of 'unknown' for label types we don't understand. I don't think
> this will be a huge issue for compatibility, but there could be something I'm
> missing.
Hi Chris,
I like the patch.
Some minor suggestions below.
> Otherwise, this patch has been tested by me to work, and now when you do:
>
> # virsh vol-dumpxml --pool iscsitest lun-1
>
> you'll get:
>
> <volume>
> ...
> <target>
> ...
> <format type='dos' />
>
> Which should be sufficient for oVirt to do it's detection.
>
> Signed-off-by: Chris Lalancette <clalance at redhat.com>
> Index: src/storage_backend.c
> ===================================================================
> RCS file: /data/cvs/libvirt/src/storage_backend.c,v
> retrieving revision 1.21
> diff -u -r1.21 storage_backend.c
> --- src/storage_backend.c 5 Sep 2008 12:03:45 -0000 1.21
> +++ src/storage_backend.c 16 Oct 2008 07:29:46 -0000
> @@ -192,6 +192,30 @@
> return ret;
> }
>
> +struct diskType disk_types[] = {
> + { "lvm2", VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CUL },
> + { "gpt", VIR_STORAGE_POOL_DISK_GPT, 0x200, 8, 0x5452415020494645UL },
> + { "dvh", VIR_STORAGE_POOL_DISK_DVH, 0x0, 4, 0x41A9E50BUL },
> + { "mac", VIR_STORAGE_POOL_DISK_MAC, 0x0, 2, 0x5245UL },
> + { "bsd", VIR_STORAGE_POOL_DISK_BSD, 0x40, 4, 0x82564557UL },
> + { "sun", VIR_STORAGE_POOL_DISK_SUN, 0x1fc, 2, 0xBEDAUL },
Some partition tables are deliberately created to be dual GPT/DOS-based.
Here, it gets the first match (as you've deliberately ordered them that
way, I see). We might need a mechanism to let the caller prefer one or
the other. Also, realize that this can be wrong if there was once a GPT
table, and it has since been replaced with a DOS one that still happens
to have the GPT signature bytes. Or maybe it was dual GPT/DOS long ago
but has since been maintained only as DOS (in which case all GPT-related
headers could be gone). This suggests that pronouncing a partition
table to have type GPT based solely on the first 1KB is a little risky.
But it's probably just fine for now.
> + /*
> + * NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so
> + * we can't use that. At the moment I'm relying on the "dummy" IPL
> + * bootloader data that comes from parted. Luckily, the chances of running
> + * into a pc98 machine running libvirt are approximately nil.
I agree.
Why not just exclude it?
> + */
> + /*{ "pc98", 0x1fe, 2, 0xAA55UL },*/
> + { "pc98", VIR_STORAGE_POOL_DISK_PC98, 0x0, 8, 0x314C5049000000CBUL },
> + /*
> + * NOTE: the order is important here; some other disk types (like GPT and
> + * and PC98) also have 0x55AA at this offset. For that reason, the DOS
> + * one must be the last one.
> + */
> + { "dos", VIR_STORAGE_POOL_DISK_DOS, 0x1fe, 2, 0xAA55UL },
> + { NULL, -1, 0x0, 0, 0x0UL },
> +};
> +
> int
> virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
> virStorageVolDefPtr vol,
> @@ -245,6 +269,40 @@
> if (withCapacity) vol->capacity = end;
> }
>
> + /* make sure to set the target format "unknown" to begin with */
> + vol->target.format = VIR_STORAGE_POOL_DISK_UNKNOWN;
> +
> + if (S_ISBLK(sb.st_mode)) {
> + off_t start;
> + int i;
> + unsigned char buffer[1024];
> + ssize_t bytes;
> +
> + start = lseek(fd, 0, SEEK_SET);
> + if (start < 0) {
> + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> + _("cannot seek to beginning of file '%s':%s"),
> + vol->target.path, strerror(errno));
> + return -1;
> + }
> + memset(buffer, 0, 1024);
You can remove the above memset, given one of the suggestions below.
> + bytes = saferead(fd, buffer, 1024);
Better not to repeat constants like that. use sizeof:
bytes = saferead(fd, buffer, sizeof buffer);
> + if (bytes < 0) {
> + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> + _("cannot read beginning of file '%s':%s"),
> + vol->target.path, strerror(errno));
> + return -1;
> + }
At worst, memset just the probably-empty portion of the buffer
that wasn't set by the read:
memset(buffer + bytes, 0, sizeof buffer - bytes);
However, I prefer to drop the memset altogether and check
offset+len against "bytes" below:
> + for (i = 0; disk_types[i].name != NULL; i++) {
It'd be good to check that offset+len is within range, regardless
of whether the buffer is zeroed. In case someone decides to shrink
buffer or to add a type with magic bytes past the 1KB mark.
if (disk_types[i].offset + disk_types[i].length > bytes)
continue;
> + if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
> + disk_types[i].length) == 0) {
> + vol->target.format = disk_types[i].label;
> + break;
> + }
> + }
> + }
> +
...
> Index: src/storage_backend.h
> ===================================================================
> RCS file: /data/cvs/libvirt/src/storage_backend.h,v
> retrieving revision 1.7
> diff -u -r1.7 storage_backend.h
> --- src/storage_backend.h 2 Sep 2008 14:15:42 -0000 1.7
> +++ src/storage_backend.h 16 Oct 2008 07:29:46 -0000
> @@ -56,6 +56,30 @@
> VIR_STORAGE_BACKEND_POOL_SOURCE_NAME = (1<<4),
> };
>
> +enum diskLabel {
> + VIR_STORAGE_POOL_DISK_DOS = 0,
I like to start with 1 or larger, so that using an uninitialized
variable (often 0) is more likely to trigger an error.
> + VIR_STORAGE_POOL_DISK_DVH,
> + VIR_STORAGE_POOL_DISK_GPT,
> + VIR_STORAGE_POOL_DISK_MAC,
> + VIR_STORAGE_POOL_DISK_BSD,
> + VIR_STORAGE_POOL_DISK_PC98,
> + VIR_STORAGE_POOL_DISK_SUN,
> + VIR_STORAGE_POOL_DISK_LVM2,
> + /* the "unknown" disk is 1 billion (and not, for instance, -1), to make
> + sure it doesn't run afoul of error checking */
> + VIR_STORAGE_POOL_DISK_UNKNOWN = 1 * 1024 * 1024 * 1024,
> +};
> +
> +struct diskType {
> + const char *name;
> + /* the 'label' terminology comes from parted */
i find "label" (meaning "partition table") terminology confusing, if only
because each partition can have a volume-label string. If it weren't so
ensconced in Parted's code, directory structure (libparted/labels/*.c)
and documentation, I would have excised it long ago. I much prefer
"partition table".
In a way, I don't mean to make you change names, but I'd hate
to see Parted's poorly chosen name propagate any more than it has.
> + enum diskLabel label;
> + unsigned short offset;
> + unsigned short length;
> + unsigned long long magic;
> +};
> +extern struct diskType disk_types[];
> +
> typedef struct _virStorageBackendPoolOptions virStorageBackendPoolOptions;
> typedef virStorageBackendPoolOptions *virStorageBackendPoolOptionsPtr;
> struct _virStorageBackendPoolOptions {
More information about the libvir-list
mailing list