Refactor virStorageFileGetMetadataFromFD to separate functionality

The virStorageFileGetMetadataFromFD did two jobs in one. First
it probed for storage type, then it extracted metadata for the
type. It is desirable to be able to separate these jobs, allowing
probing without querying metadata, and querying metadata without
probing.

To prepare for this, split out probing code into a new pair of
methods

  virStorageFileProbeFormatFromFD
  virStorageFileProbeFormat

* src/util/storage_file.c, src/util/storage_file.h,
  src/libvirt_private.syms: Introduce virStorageFileProbeFormat
  and virStorageFileProbeFormatFromFD
This commit is contained in:
Daniel P. Berrange 2010-06-15 14:58:10 +01:00
parent 779b6ea7c4
commit c70cb0f49b
3 changed files with 342 additions and 138 deletions

View File

@ -628,6 +628,8 @@ virStorageGenerateQcowPassphrase;
# storage_file.h
virStorageFileFormatTypeToString;
virStorageFileFormatTypeFromString;
virStorageFileProbeFormat;
virStorageFileProbeFormatFromFD;
virStorageFileGetMetadata;
virStorageFileGetMetadataFromFD;
virStorageFileIsSharedFS;

View File

@ -104,6 +104,9 @@ static int vmdk4GetBackingStore(char **, int *,
#define QCOW2_HDR_EXTENSION_END 0
#define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
/* VMDK needs at least this to find backing store,
* other formats need less */
#define STORAGE_MAX_HEAD (20*512)
static struct FileTypeInfo const fileTypeInfo[] = {
@ -349,9 +352,14 @@ vmdk4GetBackingStore(char **res,
size_t buf_size)
{
static const char prefix[] = "parentFileNameHint=\"";
char desc[20*512 + 1], *start, *end;
char *desc, *start, *end;
size_t len;
int ret = BACKING_STORE_ERROR;
if (VIR_ALLOC_N(desc, STORAGE_MAX_HEAD + 1) < 0) {
virReportOOMError();
goto cleanup;
}
*res = NULL;
/*
@ -363,29 +371,42 @@ vmdk4GetBackingStore(char **res,
*/
*format = VIR_STORAGE_FILE_AUTO;
if (buf_size <= 0x200)
return BACKING_STORE_INVALID;
if (buf_size <= 0x200) {
ret = BACKING_STORE_INVALID;
goto cleanup;
}
len = buf_size - 0x200;
if (len > sizeof(desc) - 1)
len = sizeof(desc) - 1;
if (len > STORAGE_MAX_HEAD)
len = STORAGE_MAX_HEAD;
memcpy(desc, buf + 0x200, len);
desc[len] = '\0';
start = strstr(desc, prefix);
if (start == NULL)
return BACKING_STORE_OK;
if (start == NULL) {
ret = BACKING_STORE_OK;
goto cleanup;
}
start += strlen(prefix);
end = strchr(start, '"');
if (end == NULL)
return BACKING_STORE_INVALID;
if (end == start)
return BACKING_STORE_OK;
if (end == NULL) {
ret = BACKING_STORE_INVALID;
goto cleanup;
}
if (end == start) {
ret = BACKING_STORE_OK;
goto cleanup;
}
*end = '\0';
*res = strdup(start);
if (*res == NULL) {
virReportOOMError();
return BACKING_STORE_ERROR;
goto cleanup;
}
return BACKING_STORE_OK;
ret = BACKING_STORE_OK;
cleanup:
VIR_FREE(desc);
return ret;
}
/**
@ -411,148 +432,325 @@ absolutePathFromBaseFile(const char *base_file, const char *path)
return res;
}
static bool
virStorageFileMatchesMagic(int format,
unsigned char *buf,
size_t buflen)
{
int mlen;
if (fileTypeInfo[format].magic == NULL)
return false;
/* Validate magic data */
mlen = strlen(fileTypeInfo[format].magic);
if (mlen > buflen)
return false;
if (memcmp(buf, fileTypeInfo[format].magic, mlen) != 0)
return false;
return true;
}
static bool
virStorageFileMatchesExtension(int format,
const char *path)
{
if (fileTypeInfo[format].extension == NULL)
return false;
if (virFileHasSuffix(path, fileTypeInfo[format].extension))
return true;
return false;
}
static bool
virStorageFileMatchesVersion(int format,
unsigned char *buf,
size_t buflen)
{
int version;
/* Validate version number info */
if (fileTypeInfo[format].versionOffset == -1)
return false;
if ((fileTypeInfo[format].versionOffset + 4) > buflen)
return false;
if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
version =
(buf[fileTypeInfo[format].versionOffset+3] << 24) |
(buf[fileTypeInfo[format].versionOffset+2] << 16) |
(buf[fileTypeInfo[format].versionOffset+1] << 8) |
(buf[fileTypeInfo[format].versionOffset]);
} else {
version =
(buf[fileTypeInfo[format].versionOffset] << 24) |
(buf[fileTypeInfo[format].versionOffset+1] << 16) |
(buf[fileTypeInfo[format].versionOffset+2] << 8) |
(buf[fileTypeInfo[format].versionOffset+3]);
}
if (version != fileTypeInfo[format].versionNumber)
return false;
return true;
}
static int
virStorageFileGetMetadataFromBuf(int format,
const char *path,
unsigned char *buf,
size_t buflen,
virStorageFileMetadata *meta)
{
/* XXX we should consider moving virStorageBackendUpdateVolInfo
* code into this method, for non-magic files
*/
if (!fileTypeInfo[format].magic) {
return 0;
}
/* Optionally extract capacity from file */
if (fileTypeInfo[format].sizeOffset != -1) {
if ((fileTypeInfo[format].sizeOffset + 8) > buflen)
return 1;
if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
meta->capacity =
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7] << 56) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 48) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 40) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 32) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 24) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 16) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 8) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset]);
} else {
meta->capacity =
((unsigned long long)buf[fileTypeInfo[format].sizeOffset] << 56) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 48) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 40) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 32) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 24) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 16) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 8) |
((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7]);
}
/* Avoid unlikely, but theoretically possible overflow */
if (meta->capacity > (ULLONG_MAX / fileTypeInfo[format].sizeMultiplier))
return 1;
meta->capacity *= fileTypeInfo[format].sizeMultiplier;
}
if (fileTypeInfo[format].qcowCryptOffset != -1) {
int crypt_format;
crypt_format =
(buf[fileTypeInfo[format].qcowCryptOffset] << 24) |
(buf[fileTypeInfo[format].qcowCryptOffset+1] << 16) |
(buf[fileTypeInfo[format].qcowCryptOffset+2] << 8) |
(buf[fileTypeInfo[format].qcowCryptOffset+3]);
meta->encrypted = crypt_format != 0;
}
if (fileTypeInfo[format].getBackingStore != NULL) {
char *backing;
int backingFormat;
int ret = fileTypeInfo[format].getBackingStore(&backing,
&backingFormat,
buf, buflen);
if (ret == BACKING_STORE_INVALID)
return 1;
if (ret == BACKING_STORE_ERROR)
return -1;
if (backing != NULL) {
meta->backingStore = absolutePathFromBaseFile(path, backing);
VIR_FREE(backing);
if (meta->backingStore == NULL) {
virReportOOMError();
return -1;
}
meta->backingStoreFormat = backingFormat;
} else {
meta->backingStore = NULL;
meta->backingStoreFormat = VIR_STORAGE_FILE_AUTO;
}
}
return 0;
}
static int
virStorageFileProbeFormatFromBuf(const char *path,
unsigned char *buf,
size_t buflen)
{
int format = VIR_STORAGE_FILE_RAW;
int i;
/* First check file magic */
for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
if (virStorageFileMatchesMagic(i, buf, buflen) &&
virStorageFileMatchesVersion(i, buf, buflen)) {
format = i;
goto cleanup;
}
}
/* No magic, so check file extension */
for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
if (virStorageFileMatchesExtension(i, path)) {
format = i;
goto cleanup;
}
}
cleanup:
return format;
}
/**
* Probe the header of a file to determine what type of disk image
* it is, and info about its capacity if available.
* virStorageFileProbeFormatFromFD:
*
* Probe for the format of 'fd' (which is an open file descriptor
* pointing to 'path'), returning the detected disk format.
*
* Callers are advised never to trust the returned 'format'
* unless it is listed as VIR_STORAGE_FILE_RAW, since a
* malicious guest can turn a file into any other non-raw
* format at will.
*
* Best option: Don't use this function
*/
int
virStorageFileProbeFormatFromFD(const char *path, int fd)
{
unsigned char *head;
ssize_t len = STORAGE_MAX_HEAD;
int ret = -1;
if (VIR_ALLOC_N(head, len) < 0) {
virReportOOMError();
return -1;
}
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
virReportSystemError(errno, _("cannot set to start of '%s'"), path);
goto cleanup;
}
if ((len = read(fd, head, len)) < 0) {
virReportSystemError(errno, _("cannot read header '%s'"), path);
goto cleanup;
}
ret = virStorageFileProbeFormatFromBuf(path, head, len);
cleanup:
VIR_FREE(head);
return ret;
}
/**
* virStorageFileProbeFormat:
*
* Probe for the format of 'path', returning the detected
* disk format.
*
* Callers are advised never to trust the returned 'format'
* unless it is listed as VIR_STORAGE_FILE_RAW, since a
* malicious guest can turn a raw file into any other non-raw
* format at will.
*
* Best option: Don't use this function
*/
int
virStorageFileProbeFormat(const char *path)
{
int fd, ret;
if ((fd = open(path, O_RDONLY)) < 0) {
virReportSystemError(errno, _("cannot open file '%s'"), path);
return -1;
}
ret = virStorageFileProbeFormatFromFD(path, fd);
close(fd);
return ret;
}
/**
* virStorageFileGetMetadataFromFD:
*
* Probe for the format of 'fd' (which is an open file descriptor
* for the file 'path'), filling 'meta' with the detected
* format and other associated metadata.
*
* Callers are advised never to trust the returned 'meta->format'
* unless it is listed as VIR_STORAGE_FILE_RAW, since a
* malicious guest can turn a raw file into any other non-raw
* format at will.
*/
int
virStorageFileGetMetadataFromFD(const char *path,
int fd,
virStorageFileMetadata *meta)
{
unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
int len, i;
unsigned char *head;
ssize_t len = STORAGE_MAX_HEAD;
int ret = -1;
memset(meta, 0, sizeof (*meta));
/* If all else fails, call it a raw file */
meta->format = VIR_STORAGE_FILE_RAW;
if ((len = read(fd, head, sizeof(head))) < 0) {
virReportSystemError(errno, _("cannot read header '%s'"), path);
if (VIR_ALLOC_N(head, len) < 0) {
virReportOOMError();
return -1;
}
/* First check file magic */
for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
int mlen;
memset(meta, 0, sizeof (*meta));
if (fileTypeInfo[i].magic == NULL)
continue;
/* Validate magic data */
mlen = strlen(fileTypeInfo[i].magic);
if (mlen > len)
continue;
if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0)
continue;
/* Validate version number info */
if (fileTypeInfo[i].versionNumber != -1) {
int version;
if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
version = (head[fileTypeInfo[i].versionOffset+3] << 24) |
(head[fileTypeInfo[i].versionOffset+2] << 16) |
(head[fileTypeInfo[i].versionOffset+1] << 8) |
head[fileTypeInfo[i].versionOffset];
} else {
version = (head[fileTypeInfo[i].versionOffset] << 24) |
(head[fileTypeInfo[i].versionOffset+1] << 16) |
(head[fileTypeInfo[i].versionOffset+2] << 8) |
head[fileTypeInfo[i].versionOffset+3];
}
if (version != fileTypeInfo[i].versionNumber)
continue;
}
/* Optionally extract capacity from file */
if (fileTypeInfo[i].sizeOffset != -1) {
if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
meta->capacity =
((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
} else {
meta->capacity =
((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
}
/* Avoid unlikely, but theoretically possible overflow */
if (meta->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
continue;
meta->capacity *= fileTypeInfo[i].sizeMultiplier;
}
if (fileTypeInfo[i].qcowCryptOffset != -1) {
int crypt_format;
crypt_format = (head[fileTypeInfo[i].qcowCryptOffset] << 24) |
(head[fileTypeInfo[i].qcowCryptOffset+1] << 16) |
(head[fileTypeInfo[i].qcowCryptOffset+2] << 8) |
head[fileTypeInfo[i].qcowCryptOffset+3];
meta->encrypted = crypt_format != 0;
}
/* Validation passed, we know the file format now */
meta->format = i;
if (fileTypeInfo[i].getBackingStore != NULL) {
char *backing;
int backingFormat;
switch (fileTypeInfo[i].getBackingStore(&backing,
&backingFormat,
head, len)) {
case BACKING_STORE_OK:
break;
case BACKING_STORE_INVALID:
continue;
case BACKING_STORE_ERROR:
return -1;
}
if (backing != NULL) {
meta->backingStore = absolutePathFromBaseFile(path, backing);
VIR_FREE(backing);
if (meta->backingStore == NULL) {
virReportOOMError();
return -1;
}
meta->backingStoreFormat = backingFormat;
} else {
meta->backingStoreFormat = VIR_STORAGE_FILE_AUTO;
}
}
return 0;
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
virReportSystemError(errno, _("cannot set to start of '%s'"), path);
goto cleanup;
}
/* No magic, so check file extension */
for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
if (fileTypeInfo[i].extension == NULL)
continue;
if (!virFileHasSuffix(path, fileTypeInfo[i].extension))
continue;
meta->format = i;
return 0;
if ((len = read(fd, head, len)) < 0) {
virReportSystemError(errno, _("cannot read header '%s'"), path);
goto cleanup;
}
return 0;
meta->format = virStorageFileProbeFormatFromBuf(path, head, len);
ret = virStorageFileGetMetadataFromBuf(meta->format, path, head, len, meta);
cleanup:
VIR_FREE(head);
return ret;
}
/**
* virStorageFileGetMetadata:
*
* Probe for the format of 'path', filling 'meta' with the detected
* format and other associated metadata.
*
* Callers are advised never to trust the returned 'meta->format'
* unless it is listed as VIR_STORAGE_FILE_RAW, since a
* malicious guest can turn a raw file into any other non-raw
* format at will.
*/
int
virStorageFileGetMetadata(const char *path,
virStorageFileMetadata *meta)

View File

@ -57,6 +57,10 @@ typedef struct _virStorageFileMetadata {
# define DEV_BSIZE 512
# endif
int virStorageFileProbeFormat(const char *path);
int virStorageFileProbeFormatFromFD(const char *path,
int fd);
int virStorageFileGetMetadata(const char *path,
virStorageFileMetadata *meta);
int virStorageFileGetMetadataFromFD(const char *path,