From 5967039eb6a1be5f15a2cc313c8c72bae0c5f413 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 5 Nov 2013 10:30:56 -0700 Subject: [PATCH] storage: avoid short reads while chasing backing chain Our backing file chain code was not very robust to an ill-timed EINTR, which could lead to a short read causing us to randomly treat metadata differently than usual. But the existing virFileReadLimFD forces an error if we don't read the entire file, even though we only care about the header of the file. So add a new virFile function that does what we want. * src/util/virfile.h (virFileReadHeaderFD): New prototype. * src/util/virfile.c (virFileReadHeaderFD): New function. * src/libvirt_private.syms (virfile.h): Export it. * src/util/virstoragefile.c (virStorageFileGetMetadataInternal) (virStorageFileProbeFormatFromFD): Use it. Signed-off-by: Eric Blake (cherry picked from commit 5327fad4f292e4f3f84884ffe158c492bf00519c) Conflicts: src/util/virstoragefile.c: OOM error reporting & buffer signedness Conflicts: src/libvirt_private.syms, src/util/virfile.c, src/util/virfile.h: Moved code to virutil.{c,h} --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 14 ++------------ src/util/virutil.c | 19 +++++++++++++++++++ src/util/virutil.h | 3 +++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4c95334a17..a728fed383 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1880,6 +1880,7 @@ virFileMatchesNameSuffix; virFileOpenAs; virFileOpenTty; virFileReadAll; +virFileReadHeaderFD; virFileReadLimFD; virFileResolveAllLinks; virFileResolveLink; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 60fdcf3ab2..12f842025b 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -707,12 +707,7 @@ virStorageFileGetMetadataInternal(const char *path, goto cleanup; } - if (VIR_ALLOC_N(buf, len) < 0) { - virReportOOMError(); - goto cleanup; - } - - if ((len = read(fd, buf, len)) < 0) { + if ((len = virFileReadHeaderFD(fd, len, (char **)&buf)) < 0) { virReportSystemError(errno, _("cannot read header '%s'"), path); goto cleanup; } @@ -849,17 +844,12 @@ virStorageFileProbeFormatFromFD(const char *path, int fd) return VIR_STORAGE_FILE_DIR; } - 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) { + if ((len = virFileReadHeaderFD(fd, len, (char **)&head)) < 0) { virReportSystemError(errno, _("cannot read header '%s'"), path); goto cleanup; } diff --git a/src/util/virutil.c b/src/util/virutil.c index e93885ace0..1dcc5dfc12 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -439,6 +439,25 @@ saferead_lim(int fd, size_t max_len, size_t *length) return NULL; } +/* A wrapper around saferead_lim that merely stops reading at the + * specified maximum size. */ +int +virFileReadHeaderFD(int fd, int maxlen, char **buf) +{ + size_t len; + char *s; + + if (maxlen <= 0) { + errno = EINVAL; + return -1; + } + s = saferead_lim(fd, maxlen, &len); + if (s == NULL) + return -1; + *buf = s; + return len; +} + /* A wrapper around saferead_lim that maps a failure due to exceeding the maximum size limitation to EOVERFLOW. */ int diff --git a/src/util/virutil.h b/src/util/virutil.h index e498d95059..750006ac49 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -58,6 +58,9 @@ int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups, unsigned long long capBits, bool clearExistingCaps); +int virFileReadHeaderFD(int fd, int maxlen, char **buf) + ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(3); + int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; -- 2.47.2