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 <eblake@redhat.com>
This commit is contained in:
Eric Blake 2013-11-05 10:30:56 -07:00
parent 5717ee6ab8
commit 5327fad4f2
4 changed files with 30 additions and 11 deletions

View File

@ -1208,6 +1208,7 @@ virFileOpenAs;
virFileOpenTty; virFileOpenTty;
virFilePrintf; virFilePrintf;
virFileReadAll; virFileReadAll;
virFileReadHeaderFD;
virFileReadLimFD; virFileReadLimFD;
virFileResolveAllLinks; virFileResolveAllLinks;
virFileResolveLink; virFileResolveLink;

View File

@ -1225,6 +1225,27 @@ saferead_lim(int fd, size_t max_len, size_t *length)
return NULL; 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 /* A wrapper around saferead_lim that maps a failure due to
exceeding the maximum size limitation to EOVERFLOW. */ exceeding the maximum size limitation to EOVERFLOW. */
int int

View File

@ -122,9 +122,12 @@ int virFileNBDDeviceAssociate(const char *file,
int virFileDeleteTree(const char *dir); int virFileDeleteTree(const char *dir);
int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; int virFileReadHeaderFD(int fd, int maxlen, char **buf)
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(3);
int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; int virFileReadLimFD(int fd, int maxlen, char **buf)
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(3);
int virFileReadAll(const char *path, int maxlen, char **buf)
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
int virFileWriteStr(const char *path, const char *str, mode_t mode) int virFileWriteStr(const char *path, const char *str, mode_t mode)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;

View File

@ -793,10 +793,7 @@ virStorageFileGetMetadataInternal(const char *path,
goto cleanup; goto cleanup;
} }
if (VIR_ALLOC_N(buf, len) < 0) if ((len = virFileReadHeaderFD(fd, len, &buf)) < 0) {
goto cleanup;
if ((len = read(fd, buf, len)) < 0) {
virReportSystemError(errno, _("cannot read header '%s'"), path); virReportSystemError(errno, _("cannot read header '%s'"), path);
goto cleanup; goto cleanup;
} }
@ -939,15 +936,12 @@ virStorageFileProbeFormatFromFD(const char *path, int fd)
return VIR_STORAGE_FILE_DIR; return VIR_STORAGE_FILE_DIR;
} }
if (VIR_ALLOC_N(head, len) < 0)
return -1;
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
virReportSystemError(errno, _("cannot set to start of '%s'"), path); virReportSystemError(errno, _("cannot set to start of '%s'"), path);
goto cleanup; goto cleanup;
} }
if ((len = read(fd, head, len)) < 0) { if ((len = virFileReadHeaderFD(fd, len, &head)) < 0) {
virReportSystemError(errno, _("cannot read header '%s'"), path); virReportSystemError(errno, _("cannot read header '%s'"), path);
goto cleanup; goto cleanup;
} }