c401cc
From 6729ce720b3274d576524b14939017c8dc7a00bb Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <6729ce720b3274d576524b14939017c8dc7a00bb@dist-git>
c401cc
From: Eric Blake <eblake@redhat.com>
c401cc
Date: Wed, 26 Feb 2014 14:54:35 +0100
c401cc
Subject: [PATCH] storage: always probe type with buffer
c401cc
c401cc
https://bugzilla.redhat.com/show_bug.cgi?id=1032370
c401cc
c401cc
This gets rid of another stat() per volume, as well as cutting
c401cc
bytes read in half, when populating the volumes of a directory
c401cc
pool during a pool refresh.  Not to mention that it provides an
c401cc
interface that can let gluster pools also probe file types.
c401cc
c401cc
* src/util/virstoragefile.h (virStorageFileProbeFormatFromFD):
c401cc
Delete.
c401cc
(virStorageFileProbeFormatFromBuf): New prototype.
c401cc
(VIR_STORAGE_MAX_HEADER): New constant, based on...
c401cc
* src/util/virstoragefile.c (STORAGE_MAX_HEAD): ...old name.
c401cc
(vmdk4GetBackingStore, virStorageFileGetMetadataInternal)
c401cc
(virStorageFileProbeFormat): Adjust clients.
c401cc
(virStorageFileProbeFormatFromFD): Delete.
c401cc
(virStorageFileProbeFormatFromBuf): Export.
c401cc
* src/storage/storage_backend_fs.c (virStorageBackendProbeTarget):
c401cc
Adjust client.
c401cc
* src/libvirt_private.syms (virstoragefile.h): Adjust exports.
c401cc
c401cc
Signed-off-by: Eric Blake <eblake@redhat.com>
c401cc
(cherry picked from commit 348b4e254bea98c83107887c0cf64c6572063d64)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/libvirt_private.syms         |  2 +-
c401cc
 src/storage/storage_backend_fs.c | 52 +++++++++++++++++---------
c401cc
 src/util/virstoragefile.c        | 81 +++++++++++++---------------------------
c401cc
 src/util/virstoragefile.h        | 12 +++++-
c401cc
 4 files changed, 71 insertions(+), 76 deletions(-)
c401cc
c401cc
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
c401cc
index 4d74d46..4d80130 100644
c401cc
--- a/src/libvirt_private.syms
c401cc
+++ b/src/libvirt_private.syms
c401cc
@@ -1931,7 +1931,7 @@ virStorageFileIsClusterFS;
c401cc
 virStorageFileIsSharedFS;
c401cc
 virStorageFileIsSharedFSType;
c401cc
 virStorageFileProbeFormat;
c401cc
-virStorageFileProbeFormatFromFD;
c401cc
+virStorageFileProbeFormatFromBuf;
c401cc
 virStorageFileResize;
c401cc
 
c401cc
 
c401cc
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
c401cc
index 5d68232..11bb39b 100644
c401cc
--- a/src/storage/storage_backend_fs.c
c401cc
+++ b/src/storage/storage_backend_fs.c
c401cc
@@ -1,7 +1,7 @@
c401cc
 /*
c401cc
  * storage_backend_fs.c: storage backend for FS and directory handling
c401cc
  *
c401cc
- * Copyright (C) 2007-2012 Red Hat, Inc.
c401cc
+ * Copyright (C) 2007-2013 Red Hat, Inc.
c401cc
  * Copyright (C) 2007-2008 Daniel P. Berrange
c401cc
  *
c401cc
  * This library is free software; you can redistribute it and/or
c401cc
@@ -71,6 +71,8 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
c401cc
     int ret = -1;
c401cc
     virStorageFileMetadata *meta = NULL;
c401cc
     struct stat sb;
c401cc
+    char *header = NULL;
c401cc
+    ssize_t len = VIR_STORAGE_MAX_HEADER;
c401cc
 
c401cc
     *backingStore = NULL;
c401cc
     *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
c401cc
@@ -88,20 +90,33 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
c401cc
         goto error;
c401cc
     }
c401cc
 
c401cc
-    if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
c401cc
-        ret = -1;
c401cc
-        goto error;
c401cc
-    }
c401cc
+    if (S_ISDIR(sb.st_mode)) {
c401cc
+        target->format = VIR_STORAGE_FILE_DIR;
c401cc
+    } else {
c401cc
+        if ((len = virFileReadHeaderFD(fd, len, &header)) < 0) {
c401cc
+            virReportSystemError(errno, _("cannot read header '%s'"),
c401cc
+                                 target->path);
c401cc
+            goto error;
c401cc
+        }
c401cc
 
c401cc
-    if (!(meta = virStorageFileGetMetadataFromFD(target->path, fd,
c401cc
-                                                 target->format))) {
c401cc
-        ret = -1;
c401cc
-        goto error;
c401cc
+        target->format = virStorageFileProbeFormatFromBuf(target->path,
c401cc
+                                                          header, len);
c401cc
+        if (target->format < 0) {
c401cc
+            ret = -1;
c401cc
+            goto error;
c401cc
+        }
c401cc
+
c401cc
+        if (!(meta = virStorageFileGetMetadataFromBuf(target->path,
c401cc
+                                                      header, len,
c401cc
+                                                      target->format))) {
c401cc
+            ret = -1;
c401cc
+            goto error;
c401cc
+        }
c401cc
     }
c401cc
 
c401cc
     VIR_FORCE_CLOSE(fd);
c401cc
 
c401cc
-    if (meta->backingStore) {
c401cc
+    if (meta && meta->backingStore) {
c401cc
         *backingStore = meta->backingStore;
c401cc
         meta->backingStore = NULL;
c401cc
         if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO &&
c401cc
@@ -126,10 +141,10 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
c401cc
         ret = 0;
c401cc
     }
c401cc
 
c401cc
-    if (capacity && meta->capacity)
c401cc
+    if (capacity && meta && meta->capacity)
c401cc
         *capacity = meta->capacity;
c401cc
 
c401cc
-    if (encryption != NULL && meta->encrypted) {
c401cc
+    if (encryption && meta && meta->encrypted) {
c401cc
         if (VIR_ALLOC(*encryption) < 0)
c401cc
             goto cleanup;
c401cc
 
c401cc
@@ -150,24 +165,25 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
c401cc
     }
c401cc
 
c401cc
     virBitmapFree(target->features);
c401cc
-    target->features = meta->features;
c401cc
-    meta->features = NULL;
c401cc
+    if (meta) {
c401cc
+        target->features = meta->features;
c401cc
+        meta->features = NULL;
c401cc
+    }
c401cc
 
c401cc
-    if (meta->compat) {
c401cc
+    if (meta && meta->compat) {
c401cc
         VIR_FREE(target->compat);
c401cc
         target->compat = meta->compat;
c401cc
         meta->compat = NULL;
c401cc
     }
c401cc
 
c401cc
-    virStorageFileFreeMetadata(meta);
c401cc
-
c401cc
-    return ret;
c401cc
+    goto cleanup;
c401cc
 
c401cc
 error:
c401cc
     VIR_FORCE_CLOSE(fd);
c401cc
 
c401cc
 cleanup:
c401cc
     virStorageFileFreeMetadata(meta);
c401cc
+    VIR_FREE(header);
c401cc
     return ret;
c401cc
 
c401cc
 }
c401cc
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
c401cc
index 8b89db1..471287c 100644
c401cc
--- a/src/util/virstoragefile.c
c401cc
+++ b/src/util/virstoragefile.c
c401cc
@@ -148,11 +148,6 @@ qedGetBackingStore(char **, int *, const char *, size_t);
c401cc
 #define QED_F_BACKING_FILE 0x01
c401cc
 #define QED_F_BACKING_FORMAT_NO_PROBE 0x04
c401cc
 
c401cc
-/* VMDK needs at least 20*512 B to find backing store,
c401cc
- * ISO has 5 Byte magic on offset 32769,
c401cc
- * other formats need less */
c401cc
-#define STORAGE_MAX_HEAD 32769+5
c401cc
-
c401cc
 
c401cc
 static struct FileTypeInfo const fileTypeInfo[] = {
c401cc
     [VIR_STORAGE_FILE_NONE] = { 0, NULL, NULL, LV_LITTLE_ENDIAN,
c401cc
@@ -442,7 +437,7 @@ vmdk4GetBackingStore(char **res,
c401cc
     size_t len;
c401cc
     int ret = BACKING_STORE_ERROR;
c401cc
 
c401cc
-    if (VIR_ALLOC_N(desc, STORAGE_MAX_HEAD + 1) < 0)
c401cc
+    if (VIR_ALLOC_N(desc, VIR_STORAGE_MAX_HEADER) < 0)
c401cc
         goto cleanup;
c401cc
 
c401cc
     *res = NULL;
c401cc
@@ -460,8 +455,8 @@ vmdk4GetBackingStore(char **res,
c401cc
         goto cleanup;
c401cc
     }
c401cc
     len = buf_size - 0x200;
c401cc
-    if (len > STORAGE_MAX_HEAD)
c401cc
-        len = STORAGE_MAX_HEAD;
c401cc
+    if (len > VIR_STORAGE_MAX_HEADER)
c401cc
+        len = VIR_STORAGE_MAX_HEADER;
c401cc
     memcpy(desc, buf + 0x200, len);
c401cc
     desc[len] = '\0';
c401cc
     start = strstr(desc, prefix);
c401cc
@@ -677,7 +672,7 @@ virBackingStoreIsFile(const char *backing)
c401cc
     return true;
c401cc
 }
c401cc
 
c401cc
-static int
c401cc
+int
c401cc
 virStorageFileProbeFormatFromBuf(const char *path,
c401cc
                                  char *buf,
c401cc
                                  size_t buflen)
c401cc
@@ -685,7 +680,7 @@ virStorageFileProbeFormatFromBuf(const char *path,
c401cc
     int format = VIR_STORAGE_FILE_RAW;
c401cc
     size_t i;
c401cc
     int possibleFormat = VIR_STORAGE_FILE_RAW;
c401cc
-    VIR_DEBUG("path=%s", path);
c401cc
+    VIR_DEBUG("path=%s, buf=%p, buflen=%zu", path, buf, buflen);
c401cc
 
c401cc
     /* First check file magic */
c401cc
     for (i = 0; i < VIR_STORAGE_FILE_LAST; i++) {
c401cc
@@ -877,36 +872,41 @@ cleanup:
c401cc
 
c401cc
 
c401cc
 /**
c401cc
- * virStorageFileProbeFormatFromFD:
c401cc
+ * virStorageFileProbeFormat:
c401cc
  *
c401cc
- * Probe for the format of 'fd' (which is an open file descriptor
c401cc
- * pointing to 'path'), returning the detected disk format.
c401cc
+ * Probe for the format of 'path', returning the detected
c401cc
+ * disk format.
c401cc
  *
c401cc
  * Callers are advised never to trust the returned 'format'
c401cc
  * unless it is listed as VIR_STORAGE_FILE_RAW, since a
c401cc
- * malicious guest can turn a file into any other non-raw
c401cc
+ * malicious guest can turn a raw file into any other non-raw
c401cc
  * format at will.
c401cc
  *
c401cc
  * Best option: Don't use this function
c401cc
  */
c401cc
 int
c401cc
-virStorageFileProbeFormatFromFD(const char *path, int fd)
c401cc
+virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid)
c401cc
 {
c401cc
-    char *head = NULL;
c401cc
-    ssize_t len = STORAGE_MAX_HEAD;
c401cc
+    int fd;
c401cc
     int ret = -1;
c401cc
     struct stat sb;
c401cc
+    ssize_t len = VIR_STORAGE_MAX_HEADER;
c401cc
+    char *header = NULL;
c401cc
 
c401cc
-    if (fstat(fd, &sb) < 0) {
c401cc
-        virReportSystemError(errno,
c401cc
-                             _("cannot stat file '%s'"),
c401cc
-                             path);
c401cc
+    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
c401cc
+        virReportSystemError(-fd, _("Failed to open file '%s'"), path);
c401cc
         return -1;
c401cc
     }
c401cc
 
c401cc
+    if (fstat(fd, &sb) < 0) {
c401cc
+        virReportSystemError(errno, _("cannot stat file '%s'"), path);
c401cc
+        goto cleanup;
c401cc
+    }
c401cc
+
c401cc
     /* No header to probe for directories */
c401cc
     if (S_ISDIR(sb.st_mode)) {
c401cc
-        return VIR_STORAGE_FILE_DIR;
c401cc
+        ret = VIR_STORAGE_FILE_DIR;
c401cc
+        goto cleanup;
c401cc
     }
c401cc
 
c401cc
     if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
c401cc
@@ -914,44 +914,15 @@ virStorageFileProbeFormatFromFD(const char *path, int fd)
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
-    if ((len = virFileReadHeaderFD(fd, len, (char **)&head)) < 0) {
c401cc
+    if ((len = virFileReadHeaderFD(fd, len, &header)) < 0) {
c401cc
         virReportSystemError(errno, _("cannot read header '%s'"), path);
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
-    ret = virStorageFileProbeFormatFromBuf(path, head, len);
c401cc
+    ret = virStorageFileProbeFormatFromBuf(path, header, len);
c401cc
 
c401cc
 cleanup:
c401cc
-    VIR_FREE(head);
c401cc
-    return ret;
c401cc
-}
c401cc
-
c401cc
-
c401cc
-/**
c401cc
- * virStorageFileProbeFormat:
c401cc
- *
c401cc
- * Probe for the format of 'path', returning the detected
c401cc
- * disk format.
c401cc
- *
c401cc
- * Callers are advised never to trust the returned 'format'
c401cc
- * unless it is listed as VIR_STORAGE_FILE_RAW, since a
c401cc
- * malicious guest can turn a raw file into any other non-raw
c401cc
- * format at will.
c401cc
- *
c401cc
- * Best option: Don't use this function
c401cc
- */
c401cc
-int
c401cc
-virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid)
c401cc
-{
c401cc
-    int fd, ret;
c401cc
-
c401cc
-    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
c401cc
-        virReportSystemError(-fd, _("Failed to open file '%s'"), path);
c401cc
-        return -1;
c401cc
-    }
c401cc
-
c401cc
-    ret = virStorageFileProbeFormatFromFD(path, fd);
c401cc
-
c401cc
+    VIR_FREE(header);
c401cc
     VIR_FORCE_CLOSE(fd);
c401cc
 
c401cc
     return ret;
c401cc
@@ -998,7 +969,7 @@ virStorageFileGetMetadataFromFDInternal(const char *path,
c401cc
                                         int format)
c401cc
 {
c401cc
     char *buf = NULL;
c401cc
-    ssize_t len = STORAGE_MAX_HEAD;
c401cc
+    ssize_t len = VIR_STORAGE_MAX_HEADER;
c401cc
     struct stat sb;
c401cc
     virStorageFileMetadataPtr ret = NULL;
c401cc
 
c401cc
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
c401cc
index 5fc8832..e1ac91b 100644
c401cc
--- a/src/util/virstoragefile.h
c401cc
+++ b/src/util/virstoragefile.h
c401cc
@@ -27,6 +27,14 @@
c401cc
 # include "virbitmap.h"
c401cc
 # include "virutil.h"
c401cc
 
c401cc
+/* Minimum header size required to probe all known formats with
c401cc
+ * virStorageFileProbeFormat, or obtain metadata from a known format.
c401cc
+ * Rounded to multiple of 512 (ISO has a 5-byte magic at offset
c401cc
+ * 32769).  Some formats can be probed with fewer bytes.  Although
c401cc
+ * some formats theoretically permit metadata that can rely on offsets
c401cc
+ * beyond this size, in practice that doesn't matter.  */
c401cc
+# define VIR_STORAGE_MAX_HEADER 0x8200
c401cc
+
c401cc
 enum virStorageFileFormat {
c401cc
     VIR_STORAGE_FILE_AUTO_SAFE = -2,
c401cc
     VIR_STORAGE_FILE_AUTO = -1,
c401cc
@@ -80,8 +88,8 @@ struct _virStorageFileMetadata {
c401cc
 # endif
c401cc
 
c401cc
 int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid);
c401cc
-int virStorageFileProbeFormatFromFD(const char *path,
c401cc
-                                    int fd);
c401cc
+int virStorageFileProbeFormatFromBuf(const char *path, char *buf,
c401cc
+                                     size_t buflen);
c401cc
 
c401cc
 virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path,
c401cc
                                                     int format,
c401cc
-- 
c401cc
1.9.0
c401cc