提交 b928d140 编写于 作者: M Michal Privoznik

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: NMichal Privoznik <mprivozn@redhat.com>
上级 07c2399c
......@@ -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;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册