mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-01 17:35:17 +00:00
util: Introduce virFileInData
This function takes a FD and determines whether the current position is in data section or in a hole. In addition to that, it also determines how much bytes are there remaining till the current section ends. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
07c2399c01
commit
b928d140f4
@ -1628,6 +1628,7 @@ virFileGetHugepageSize;
|
|||||||
virFileGetMountReverseSubtree;
|
virFileGetMountReverseSubtree;
|
||||||
virFileGetMountSubtree;
|
virFileGetMountSubtree;
|
||||||
virFileHasSuffix;
|
virFileHasSuffix;
|
||||||
|
virFileInData;
|
||||||
virFileIsAbsPath;
|
virFileIsAbsPath;
|
||||||
virFileIsDir;
|
virFileIsDir;
|
||||||
virFileIsExecutable;
|
virFileIsExecutable;
|
||||||
|
@ -3793,6 +3793,114 @@ virFileComparePaths(const char *p1, const char *p2)
|
|||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(res1);
|
VIR_FREE(res1);
|
||||||
VIR_FREE(res2);
|
VIR_FREE(res2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virFileInData:
|
||||||
|
* @fd: file to check
|
||||||
|
* @inData: true if current position in the @fd is in data section
|
||||||
|
* @length: amount of bytes until the end of the current section
|
||||||
|
*
|
||||||
|
* With sparse files not every extent has to be physically stored on
|
||||||
|
* the disk. This results in so called data or hole sections. This
|
||||||
|
* function checks whether the current position in the file @fd is
|
||||||
|
* in a data section (@inData = 1) or in a hole (@inData = 0). Also,
|
||||||
|
* it sets @length to match the number of bytes remaining until the
|
||||||
|
* end of the current section.
|
||||||
|
*
|
||||||
|
* As a special case, there is an implicit hole at the end of any
|
||||||
|
* file. In this case, the function sets @inData = 0, @length = 0.
|
||||||
|
*
|
||||||
|
* Upon its return, the position in the @fd is left unchanged, i.e.
|
||||||
|
* despite this function lseek()-ing back and forth it always
|
||||||
|
* restores the original position in the file.
|
||||||
|
*
|
||||||
|
* NB, @length is type of long long because it corresponds to off_t
|
||||||
|
* the best.
|
||||||
|
*
|
||||||
|
* Returns 0 on success,
|
||||||
|
* -1 otherwise.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
virFileInData(int fd,
|
||||||
|
int *inData,
|
||||||
|
long long *length)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
off_t cur, data, hole, end;
|
||||||
|
|
||||||
|
/* Get current position */
|
||||||
|
cur = lseek(fd, 0, SEEK_CUR);
|
||||||
|
if (cur == (off_t) -1) {
|
||||||
|
virReportSystemError(errno, "%s",
|
||||||
|
_("Unable to get current position in file"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now try to get data and hole offsets */
|
||||||
|
data = lseek(fd, cur, SEEK_DATA);
|
||||||
|
|
||||||
|
/* There are four options:
|
||||||
|
* 1) data == cur; @cur is in data
|
||||||
|
* 2) data > cur; @cur is in a hole, next data at @data
|
||||||
|
* 3) data < 0, errno = ENXIO; either @cur is in trailing hole, or @cur is beyond EOF.
|
||||||
|
* 4) data < 0, errno != ENXIO; we learned nothing
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (data == (off_t) -1) {
|
||||||
|
/* cases 3 and 4 */
|
||||||
|
if (errno != ENXIO) {
|
||||||
|
virReportSystemError(errno, "%s",
|
||||||
|
_("Unable to seek to data"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inData = 0;
|
||||||
|
/* There are two situations now. There is always an
|
||||||
|
* implicit hole at EOF. However, there might be a
|
||||||
|
* trailing hole just before EOF too. If that's the case
|
||||||
|
* report it. */
|
||||||
|
if ((end = lseek(fd, 0, SEEK_END)) == (off_t) -1) {
|
||||||
|
virReportSystemError(errno, "%s",
|
||||||
|
_("Unable to seek to EOF"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*length = end - cur;
|
||||||
|
} else if (data > cur) {
|
||||||
|
/* case 2 */
|
||||||
|
*inData = 0;
|
||||||
|
*length = data - cur;
|
||||||
|
} else {
|
||||||
|
/* case 1 */
|
||||||
|
*inData = 1;
|
||||||
|
|
||||||
|
/* We don't know where does the next hole start. Let's
|
||||||
|
* find out. Here we get the same 4 possibilities as
|
||||||
|
* described above.*/
|
||||||
|
hole = lseek(fd, data, SEEK_HOLE);
|
||||||
|
if (hole == (off_t) -1 || hole == data) {
|
||||||
|
/* cases 1, 3 and 4 */
|
||||||
|
/* Wait a second. The reason why we are here is
|
||||||
|
* because we are in data. But at the same time we
|
||||||
|
* are in a trailing hole? Wut!? Do the best what we
|
||||||
|
* can do here. */
|
||||||
|
virReportSystemError(errno, "%s",
|
||||||
|
_("unable to seek to hole"));
|
||||||
|
goto cleanup;
|
||||||
|
} else {
|
||||||
|
/* case 2 */
|
||||||
|
*length = (hole - data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
cleanup:
|
||||||
|
/* At any rate, reposition back to where we started. */
|
||||||
|
if (cur != (off_t) -1)
|
||||||
|
ignore_value(lseek(fd, cur, SEEK_SET));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,4 +348,8 @@ int virFileReadValueString(char **value, const char *format, ...)
|
|||||||
ATTRIBUTE_FMT_PRINTF(2, 3);
|
ATTRIBUTE_FMT_PRINTF(2, 3);
|
||||||
|
|
||||||
|
|
||||||
|
int virFileInData(int fd,
|
||||||
|
int *inData,
|
||||||
|
long long *length);
|
||||||
|
|
||||||
#endif /* __VIR_FILE_H */
|
#endif /* __VIR_FILE_H */
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
#include "virfile.h"
|
#include "virfile.h"
|
||||||
@ -118,6 +119,190 @@ testFileSanitizePath(const void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
makeSparseFile(const off_t offsets[],
|
||||||
|
const bool startData);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/* Create a sparse file. @offsets in KiB. */
|
||||||
|
static int
|
||||||
|
makeSparseFile(const off_t offsets[],
|
||||||
|
const bool startData)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
char path[] = abs_builddir "fileInData.XXXXXX";
|
||||||
|
off_t len = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if ((fd = mkostemp(path, O_CLOEXEC|O_RDWR)) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (unlink(path) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
for (i = 0; offsets[i] != (off_t) -1; i++)
|
||||||
|
len += offsets[i] * 1024;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
const char buf[] = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
off_t toWrite = sizeof(buf);
|
||||||
|
|
||||||
|
if (toWrite > len)
|
||||||
|
toWrite = len;
|
||||||
|
|
||||||
|
if (safewrite(fd, buf, toWrite) < 0) {
|
||||||
|
fprintf(stderr, "unable to write to %s (errno=%d)\n", path, errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= toWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
for (i = 0; offsets[i] != (off_t) -1; i++) {
|
||||||
|
bool inData = startData;
|
||||||
|
|
||||||
|
if (i % 2)
|
||||||
|
inData = !inData;
|
||||||
|
|
||||||
|
if (!inData &&
|
||||||
|
fallocate(fd,
|
||||||
|
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||||
|
len, offsets[i] * 1024) < 0) {
|
||||||
|
fprintf(stderr, "unable to punch a hole at offset %lld length %lld\n",
|
||||||
|
(long long) len, (long long) offsets[i]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += offsets[i] * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
|
||||||
|
fprintf(stderr, "unable to lseek (errno=%d)\n", errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
error:
|
||||||
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !__linux__ */
|
||||||
|
|
||||||
|
static int
|
||||||
|
makeSparseFile(const off_t offsets[] ATTRIBUTE_UNUSED,
|
||||||
|
const bool startData ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__linux__ */
|
||||||
|
|
||||||
|
|
||||||
|
#define EXTENT 4
|
||||||
|
static bool
|
||||||
|
holesSupported(void)
|
||||||
|
{
|
||||||
|
off_t offsets[] = {EXTENT, EXTENT, EXTENT, -1};
|
||||||
|
off_t tmp;
|
||||||
|
int fd;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if ((fd = makeSparseFile(offsets, true)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* The way this works is: there are 4K of data followed by 4K hole followed
|
||||||
|
* by 4K hole again. Check if the filesystem we are running the test suite
|
||||||
|
* on supports holes. */
|
||||||
|
if ((tmp = lseek(fd, 0, SEEK_DATA)) == (off_t) -1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (tmp != 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if ((tmp = lseek(fd, tmp, SEEK_HOLE)) == (off_t) -1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (tmp != EXTENT * 1024)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if ((tmp = lseek(fd, tmp, SEEK_DATA)) == (off_t) -1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (tmp != 2 * EXTENT * 1024)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if ((tmp = lseek(fd, tmp, SEEK_HOLE)) == (off_t) -1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (tmp != 3 * EXTENT * 1024)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
cleanup:
|
||||||
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct testFileInData {
|
||||||
|
bool startData; /* whether the list of offsets starts with data section */
|
||||||
|
off_t *offsets;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
testFileInData(const void *opaque)
|
||||||
|
{
|
||||||
|
const struct testFileInData *data = opaque;
|
||||||
|
int fd = -1;
|
||||||
|
int ret = -1;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if ((fd = makeSparseFile(data->offsets, data->startData)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
for (i = 0; data->offsets[i] != (off_t) -1; i++) {
|
||||||
|
bool shouldInData = data->startData;
|
||||||
|
int realInData;
|
||||||
|
long long shouldLen;
|
||||||
|
long long realLen;
|
||||||
|
|
||||||
|
if (i % 2)
|
||||||
|
shouldInData = !shouldInData;
|
||||||
|
|
||||||
|
if (virFileInData(fd, &realInData, &realLen) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (realInData != shouldInData) {
|
||||||
|
fprintf(stderr, "Unexpected data/hole. Expected %s got %s\n",
|
||||||
|
shouldInData ? "data" : "hole",
|
||||||
|
realInData ? "data" : "hole");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldLen = data->offsets[i] * 1024;
|
||||||
|
if (realLen != shouldLen) {
|
||||||
|
fprintf(stderr, "Unexpected section length. Expected %lld got %lld\n",
|
||||||
|
shouldLen, realLen);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(fd, shouldLen, SEEK_CUR) < 0) {
|
||||||
|
fprintf(stderr, "Unable to seek\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mymain(void)
|
mymain(void)
|
||||||
{
|
{
|
||||||
@ -186,6 +371,24 @@ mymain(void)
|
|||||||
DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo//hoo");
|
DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo//hoo");
|
||||||
DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo///////hoo");
|
DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo///////hoo");
|
||||||
|
|
||||||
|
#define DO_TEST_IN_DATA(inData, ...) \
|
||||||
|
do { \
|
||||||
|
off_t offsets[] = {__VA_ARGS__, -1}; \
|
||||||
|
struct testFileInData data = { \
|
||||||
|
.startData = inData, .offsets = offsets, \
|
||||||
|
}; \
|
||||||
|
if (virTestRun(virTestCounterNext(), testFileInData, &data) < 0) \
|
||||||
|
ret = -1; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
if (holesSupported()) {
|
||||||
|
DO_TEST_IN_DATA(true, 4, 4, 4);
|
||||||
|
DO_TEST_IN_DATA(false, 4, 4, 4);
|
||||||
|
DO_TEST_IN_DATA(true, 8, 8, 8);
|
||||||
|
DO_TEST_IN_DATA(false, 8, 8, 8);
|
||||||
|
DO_TEST_IN_DATA(true, 8, 16, 32, 64, 128, 256, 512);
|
||||||
|
DO_TEST_IN_DATA(false, 8, 16, 32, 64, 128, 256, 512);
|
||||||
|
}
|
||||||
return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user