cluster/ec: correctly handle end of file for seek

When a SEEK_HOLE was issued near to the end of file, sometimes an
offset beyond the end of file was returned. Another problem was that
using some offsets greater than the end of file returned successfully
instead of failing with ENXIO.

Change-Id: I238d2884ba02fd19a78116b0f8f8e8d6338fb3f5
BUG: 1449348
Signed-off-by: Xavier Hernandez <xhernandez@datalab.es>
Reviewed-on: https://review.gluster.org/17228
Smoke: Gluster Build System <jenkins@build.gluster.org>
NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org>
CentOS-regression: Gluster Build System <jenkins@build.gluster.org>
Reviewed-by: Amar Tumballi <amarts@redhat.com>
Reviewed-by: Pranith Kumar Karampuri <pkarampu@redhat.com>
This commit is contained in:
Xavier Hernandez 2017-05-09 19:40:21 +02:00 committed by Pranith Kumar Karampuri
parent b25bf64f3a
commit eb96dd45f8
3 changed files with 260 additions and 0 deletions

57
tests/basic/ec/ec-seek.t Normal file
View File

@ -0,0 +1,57 @@
#!/bin/bash
. $(dirname $0)/../../include.rc
. $(dirname $0)/../../volume.rc
cleanup
SEEK=$(dirname $0)/seek
build_tester $(dirname $0)/seek.c -o ${SEEK}
TEST glusterd
TEST pidof glusterd
TEST $CLI volume info
TEST mkdir -p $B0/${V0}{0..2}
TEST $CLI volume create $V0 disperse 3 redundancy 1 $H0:$B0/${V0}{0..2}
EXPECT "$V0" volinfo_field $V0 'Volume Name'
EXPECT 'Created' volinfo_field $V0 'Status'
EXPECT '3' brick_count $V0
TEST $CLI volume start $V0
EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'Started' volinfo_field $V0 'Status'
TEST $GFS -s $H0 --volfile-id $V0 $M0
EXPECT_WITHIN $CHILD_UP_TIMEOUT "3" ec_child_up_count $V0 0
TEST ${SEEK} create ${M0}/test 0 1 1048576 1
# Determine underlying filesystem allocation block size
BSIZE="$(($(${SEEK} scan ${M0}/test hole 0) * 2))"
TEST ${SEEK} create ${M0}/test 0 ${BSIZE} $((${BSIZE} * 4 + 512)) ${BSIZE}
EXPECT "^0$" ${SEEK} scan ${M0}/test data 0
EXPECT "^$((${BSIZE} / 2))$" ${SEEK} scan ${M0}/test data $((${BSIZE} / 2))
EXPECT "^$((${BSIZE} - 1))$" ${SEEK} scan ${M0}/test data $((${BSIZE} - 1))
EXPECT "^$((${BSIZE} * 4))$" ${SEEK} scan ${M0}/test data ${BSIZE}
EXPECT "^$((${BSIZE} * 4))$" ${SEEK} scan ${M0}/test data $((${BSIZE} * 4))
EXPECT "^$((${BSIZE} * 5))$" ${SEEK} scan ${M0}/test data $((${BSIZE} * 5))
EXPECT "^$((${BSIZE} * 5 + 511))$" ${SEEK} scan ${M0}/test data $((${BSIZE} * 5 + 511))
EXPECT "^ENXIO$" ${SEEK} scan ${M0}/test data $((${BSIZE} * 5 + 512))
EXPECT "^ENXIO$" ${SEEK} scan ${M0}/test data $((${BSIZE} * 6))
EXPECT "^${BSIZE}$" ${SEEK} scan ${M0}/test hole 0
EXPECT "^${BSIZE}$" ${SEEK} scan ${M0}/test hole $((${BSIZE} / 2))
EXPECT "^${BSIZE}$" ${SEEK} scan ${M0}/test hole $((${BSIZE} - 1))
EXPECT "^${BSIZE}$" ${SEEK} scan ${M0}/test hole ${BSIZE}
EXPECT "^$((${BSIZE} * 5 + 512))$" ${SEEK} scan ${M0}/test hole $((${BSIZE} * 4))
EXPECT "^$((${BSIZE} * 5 + 512))$" ${SEEK} scan ${M0}/test hole $((${BSIZE} * 5))
EXPECT "^$((${BSIZE} * 5 + 512))$" ${SEEK} scan ${M0}/test hole $((${BSIZE} * 5 + 511))
EXPECT "^ENXIO$" ${SEEK} scan ${M0}/test hole $((${BSIZE} * 5 + 512))
EXPECT "^ENXIO$" ${SEEK} scan ${M0}/test hole $((${BSIZE} * 6))
cleanup
# Centos6 regression slaves seem to not support SEEK_DATA/SEEK_HOLE
#G_TESTDEF_TEST_STATUS_CENTOS6=BAD_TEST,BUG=000000

185
tests/basic/ec/seek.c Normal file
View File

@ -0,0 +1,185 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
static char buffer[65536];
static int
parse_int(const char *text, size_t *value)
{
char *ptr;
size_t val;
val = strtoul(text, &ptr, 0);
if (*ptr != 0) {
return 0;
}
*value = val;
return 1;
}
static int
fill_area(int fd, off_t offset, size_t size)
{
size_t len;
ssize_t res;
while (size > 0) {
len = sizeof(buffer);
if (len > size) {
len = size;
}
res = pwrite(fd, buffer, len, offset);
if (res < 0) {
fprintf(stderr,
"pwrite(%d, %p, %lu, %lu) failed: %d\n",
fd, buffer, size, offset, errno);
return 0;
}
if (res != len) {
fprintf(stderr,
"pwrite(%d, %p, %lu, %lu) didn't wrote all "
"data: %lu/%lu\n",
fd, buffer, size, offset, res, len);
return 0;
}
offset += len;
size -= len;
}
return 1;
}
static void
syntax(void)
{
fprintf(stderr, "Syntax: seek create <path> <offset> <size> [...]\n");
fprintf(stderr, " seek scan <path> data|hole <offset>\n");
}
static int
seek_create(const char *path, int argc, char *argv[])
{
size_t off, size;
int fd;
int ret = 1;
fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (fd < 0) {
fprintf(stderr, "Failed to create the file\n");
goto out;
}
while (argc > 0) {
if (!parse_int(argv[0], &off) ||
!parse_int(argv[1], &size)) {
syntax();
goto out_close;
}
if (!fill_area(fd, off, size)) {
goto out_close;
}
argv += 2;
argc -= 2;
}
ret = 0;
out_close:
close(fd);
out:
return ret;
}
static int
seek_scan(const char *path, const char *type, const char *pos)
{
size_t off, res;
int fd, whence;
int ret = 1;
if (strcmp(type, "data") == 0) {
whence = SEEK_DATA;
} else if (strcmp(type, "hole") == 0) {
whence = SEEK_HOLE;
} else {
syntax();
goto out;
}
if (!parse_int(pos, &off)) {
syntax();
goto out;
}
fd = open(path, O_RDWR);
if (fd < 0) {
fprintf(stderr, "Failed to open the file\n");
goto out;
}
res = lseek(fd, off, whence);
if (res == (off_t)-1) {
if (errno != ENXIO) {
fprintf(stderr, "seek(%d, %lu, %d) failed: %d\n", fd,
off, whence, errno);
goto out_close;
}
fprintf(stdout, "ENXIO\n");
} else {
fprintf(stdout, "%lu\n", res);
}
ret = 0;
out_close:
close(fd);
out:
return ret;
}
int
main(int argc, char *argv[])
{
int ret = 1;
memset(buffer, 0x55, sizeof(buffer));
if (argc < 3) {
syntax();
goto out;
}
if (strcmp(argv[1], "create") == 0) {
if (((argc - 3) & 1) != 0) {
syntax();
goto out;
}
ret = seek_create(argv[2], argc - 3, argv + 3);
} else if (strcmp(argv[1], "scan") == 0) {
if (argc != 5) {
syntax();
goto out;
}
ret = seek_scan(argv[2], argv[3], argv[4]);
} else {
syntax();
goto out;
}
ret = 0;
out:
return ret;
}

View File

@ -1556,6 +1556,7 @@ void ec_wind_seek(ec_t *ec, ec_fop_data_t *fop, int32_t idx)
int32_t ec_manager_seek(ec_fop_data_t *fop, int32_t state)
{
ec_cbk_data_t *cbk;
size_t size;
switch (state) {
case EC_STATE_INIT:
@ -1571,6 +1572,16 @@ int32_t ec_manager_seek(ec_fop_data_t *fop, int32_t state)
return EC_STATE_DISPATCH;
case EC_STATE_DISPATCH:
/* This shouldn't fail because we have the inode locked. */
GF_ASSERT(ec_get_inode_size(fop, fop->locks[0].lock->loc.inode,
&size));
if (fop->user_size >= size) {
ec_fop_set_error(fop, ENXIO);
return EC_STATE_REPORT;
}
ec_dispatch_one(fop);
return EC_STATE_PREPARE_ANSWER;
@ -1582,10 +1593,17 @@ int32_t ec_manager_seek(ec_fop_data_t *fop, int32_t state)
if ((cbk != NULL) && (cbk->op_ret >= 0)) {
ec_t *ec = fop->xl->private;
/* This shouldn't fail because we have the inode locked. */
GF_ASSERT(ec_get_inode_size(fop, fop->locks[0].lock->loc.inode,
&size));
cbk->offset *= ec->fragments;
if (cbk->offset < fop->user_size) {
cbk->offset = fop->user_size;
}
if (cbk->offset > size) {
cbk->offset = size;
}
}
return EC_STATE_REPORT;