mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-21 20:15: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;
|
||||
virFileGetMountSubtree;
|
||||
virFileHasSuffix;
|
||||
virFileInData;
|
||||
virFileIsAbsPath;
|
||||
virFileIsDir;
|
||||
virFileIsExecutable;
|
||||
|
@ -3793,6 +3793,114 @@ virFileComparePaths(const char *p1, const char *p2)
|
||||
cleanup:
|
||||
VIR_FREE(res1);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -348,4 +348,8 @@ int virFileReadValueString(char **value, const char *format, ...)
|
||||
ATTRIBUTE_FMT_PRINTF(2, 3);
|
||||
|
||||
|
||||
int virFileInData(int fd,
|
||||
int *inData,
|
||||
long long *length);
|
||||
|
||||
#endif /* __VIR_FILE_H */
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "testutils.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
|
||||
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");
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user