Align buffer for read() with flag O_DIRECT

This commit is contained in:
Gerhard Rieger 2020-12-29 05:07:03 +01:00
parent de2f2c471b
commit 55518fa690
7 changed files with 124 additions and 34 deletions

View file

@ -21,6 +21,12 @@ Corrections:
Mitigated race condition of quickly terminating SYSTEM or EXEC child Mitigated race condition of quickly terminating SYSTEM or EXEC child
processes. processes.
Option o-direct might require alignment of read/write buffer to, e.g.,
512 bytes, Socat now takes care of this when allocating the buffer.
With this fix read() succeeds, however, write() still might fail when
not writing complete pages.
Test: O_DIRECT
Porting: Porting:
In gcc version 10 the default changed from -fcommon to -fno-common. In gcc version 10 the default changed from -fcommon to -fno-common.
Consequently, linking filan and procan failed with error Consequently, linking filan and procan failed with error

View file

@ -69,6 +69,9 @@
/* Define if you have the socket function. */ /* Define if you have the socket function. */
#undef HAVE_SOCKET #undef HAVE_SOCKET
/* Define if you have the posix_memalign function. */
#undef HAVE_PROTOTYPE_LIB_posix_memalign
/* Define if you have the strdup function. */ /* Define if you have the strdup function. */
#undef HAVE_PROTOTYPE_LIB_strdup #undef HAVE_PROTOTYPE_LIB_strdup

View file

@ -785,6 +785,7 @@ AC_CHECK_FUNCS(cfsetispeed cfgetispeed cfsetospeed cfgetospeed)
################################### ###################################
# check for prototype and existence of functions that return a pointer # check for prototype and existence of functions that return a pointer
# defines in config.h: HAVE_PROTOTYPE_LIB_$1 # defines in config.h: HAVE_PROTOTYPE_LIB_$1
AC_CHECK_PROTOTYPE_LIB(posix_memalign)
AC_CHECK_PROTOTYPE_LIB(strdup) AC_CHECK_PROTOTYPE_LIB(strdup)
AC_CHECK_PROTOTYPE_LIB(strerror) AC_CHECK_PROTOTYPE_LIB(strerror)
AC_CHECK_PROTOTYPE_LIB(strstr) AC_CHECK_PROTOTYPE_LIB(strstr)
@ -794,7 +795,6 @@ AC_CHECK_PROTOTYPE_LIB(memrchr)
AC_CHECK_PROTOTYPE_LIB(if_indextoname) AC_CHECK_PROTOTYPE_LIB(if_indextoname)
AC_CHECK_PROTOTYPE_LIB(ptsname) AC_CHECK_PROTOTYPE_LIB(ptsname)
AC_MSG_CHECKING(for long long) AC_MSG_CHECKING(for long long)
AC_CACHE_VAL(sc_cv_type_longlong, AC_CACHE_VAL(sc_cv_type_longlong,
[AC_TRY_COMPILE([],[long long s;], [AC_TRY_COMPILE([],[long long s;],

73
socat.c
View file

@ -57,7 +57,7 @@ void socat_opt_hint(FILE *fd, char a, char b);
void socat_version(FILE *fd); void socat_version(FILE *fd);
int socat(const char *address1, const char *address2); int socat(const char *address1, const char *address2);
int _socat(void); int _socat(void);
int cv_newline(unsigned char **buff, ssize_t *bytes, int lineterm1, int lineterm2); int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2);
void socat_signal(int sig); void socat_signal(int sig);
static int socat_sigchild(struct single *file); static int socat_sigchild(struct single *file);
@ -708,7 +708,7 @@ int childleftdata(xiofile_t *xfd) {
} }
int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
unsigned char **buff, size_t bufsiz, bool righttoleft); unsigned char *buff, size_t bufsiz, bool righttoleft);
bool mayrd1; /* sock1 has read data or eof, according to poll() */ bool mayrd1; /* sock1 has read data or eof, according to poll() */
bool mayrd2; /* sock2 has read data or eof, according to poll() */ bool mayrd2; /* sock2 has read data or eof, according to poll() */
@ -765,8 +765,21 @@ int _socat(void) {
Error2("buffer size option (-b) to big - "F_Zu" (max is "F_Zu")", socat_opts.bufsiz, (SIZE_MAX-1)/2); Error2("buffer size option (-b) to big - "F_Zu" (max is "F_Zu")", socat_opts.bufsiz, (SIZE_MAX-1)/2);
socat_opts.bufsiz = (SIZE_MAX-1)/2; socat_opts.bufsiz = (SIZE_MAX-1)/2;
} }
#if HAVE_PROTOTYPE_LIB_posix_memalign
/* Operations on files with flag O_DIRECT might need buffer alignment.
Without this, eg.read() fails with "Invalid argument" */
{
int _errno;
if ((_errno = Posix_memalign((void **)&buff, getpagesize(), 2*socat_opts.bufsiz+1)) != 0) {
Error1("posix_memalign(): %s", strerror(_errno));
return -1;
}
}
#else /* !HAVE_PROTOTYPE_LIB_posix_memalign */
buff = Malloc(2*socat_opts.bufsiz+1); buff = Malloc(2*socat_opts.bufsiz+1);
if (buff == NULL) return -1; if (buff == NULL) return -1;
#endif /* !HAVE_PROTOTYPE_LIB_posix_memalign */
if (socat_opts.logopt == 'm' && xioinqopt('l', NULL, 0) == 'm') { if (socat_opts.logopt == 'm' && xioinqopt('l', NULL, 0) == 'm') {
Info("switching to syslog"); Info("switching to syslog");
@ -988,7 +1001,7 @@ int _socat(void) {
if (mayrd1 && maywr2) { if (mayrd1 && maywr2) {
mayrd1 = false; mayrd1 = false;
if ((bytes1 = xiotransfer(sock1, sock2, &buff, socat_opts.bufsiz, false)) if ((bytes1 = xiotransfer(sock1, sock2, buff, socat_opts.bufsiz, false))
< 0) { < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
closing = MAX(closing, 1); closing = MAX(closing, 1);
@ -1020,7 +1033,7 @@ int _socat(void) {
if (mayrd2 && maywr1) { if (mayrd2 && maywr1) {
mayrd2 = false; mayrd2 = false;
if ((bytes2 = xiotransfer(sock2, sock1, &buff, socat_opts.bufsiz, true)) if ((bytes2 = xiotransfer(sock2, sock1, buff, socat_opts.bufsiz, true))
< 0) { < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
closing = MAX(closing, 1); closing = MAX(closing, 1);
@ -1194,10 +1207,10 @@ static int
*/ */
/* inpipe, outpipe must be single descriptors (not dual!) */ /* inpipe, outpipe must be single descriptors (not dual!) */
int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
unsigned char **buff, size_t bufsiz, bool righttoleft) { unsigned char *buff, size_t bufsiz, bool righttoleft) {
ssize_t bytes, writt = 0; ssize_t bytes, writt = 0;
bytes = xioread(inpipe, *buff, bufsiz); bytes = xioread(inpipe, buff, bufsiz);
if (bytes < 0) { if (bytes < 0) {
if (errno != EAGAIN) if (errno != EAGAIN)
XIO_RDSTREAM(inpipe)->eof = 2; XIO_RDSTREAM(inpipe)->eof = 2;
@ -1215,7 +1228,7 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
/* handle escape char */ /* handle escape char */
if (XIO_RDSTREAM(inpipe)->escape != -1) { if (XIO_RDSTREAM(inpipe)->escape != -1) {
/* check input data for escape char */ /* check input data for escape char */
unsigned char *ptr = *buff; unsigned char *ptr = buff;
size_t ctr = 0; size_t ctr = 0;
while (ctr < bytes) { while (ctr < bytes) {
if (*ptr == XIO_RDSTREAM(inpipe)->escape) { if (*ptr == XIO_RDSTREAM(inpipe)->escape) {
@ -1251,8 +1264,8 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
size_t j; size_t j;
size_t N = 16; size_t N = 16;
const unsigned char *end, *s, *t; const unsigned char *end, *s, *t;
s = *buff; s = buff;
end = (*buff)+bytes; end = buff+bytes;
xioprintblockheader(stderr, bytes, righttoleft); xioprintblockheader(stderr, bytes, righttoleft);
while (s < end) { while (s < end) {
/*! prefix? */ /*! prefix? */
@ -1298,8 +1311,8 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
size_t i = 0; size_t i = 0;
xioprintblockheader(stderr, bytes, righttoleft); xioprintblockheader(stderr, bytes, righttoleft);
while (i < (size_t)bytes) { while (i < (size_t)bytes) {
int c = (*buff)[i]; int c = buff[i];
if (i > 0 && (*buff)[i-1] == '\n') if (i > 0 && buff[i-1] == '\n')
/*! prefix? */; /*! prefix? */;
switch (c) { switch (c) {
case '\a' : fputs("\\a", stderr); break; case '\a' : fputs("\\a", stderr); break;
@ -1323,12 +1336,12 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
/* print prefix */ /* print prefix */
xioprintblockheader(stderr, bytes, righttoleft); xioprintblockheader(stderr, bytes, righttoleft);
for (i = 0; i < bytes; ++i) { for (i = 0; i < bytes; ++i) {
fprintf(stderr, " %02x", (*buff)[i]); fprintf(stderr, " %02x", buff[i]);
} }
fputc('\n', stderr); fputc('\n', stderr);
} }
writt = xiowrite(outpipe, *buff, bytes); writt = xiowrite(outpipe, buff, bytes);
if (writt < 0) { if (writt < 0) {
/* EAGAIN when nonblocking but a mandatory lock is on file. /* EAGAIN when nonblocking but a mandatory lock is on file.
the problem with EAGAIN is that the read cannot be repeated, the problem with EAGAIN is that the read cannot be repeated,
@ -1355,11 +1368,9 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
/* converts the newline characters (or character sequences) from the one /* converts the newline characters (or character sequences) from the one
specified in lineterm1 to that of lineterm2. Possible values are specified in lineterm1 to that of lineterm2. Possible values are
LINETERM_CR, LINETERM_CRNL, LINETERM_RAW. LINETERM_CR, LINETERM_CRNL, LINETERM_RAW.
buff points to the malloc()'ed data, input and output. It may be subject to bytes specifies the number of bytes input and output */
realloc(). bytes specifies the number of bytes input and output */ int cv_newline(unsigned char *buff, ssize_t *bytes,
int cv_newline(unsigned char **buff, ssize_t *bufsiz,
int lineterm1, int lineterm2) { int lineterm1, int lineterm2) {
ssize_t *bytes = bufsiz;
/* must perform newline changes */ /* must perform newline changes */
if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) { if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) {
/* no change in data length */ /* no change in data length */
@ -1369,23 +1380,23 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz,
} else { } else {
from = '\r'; to = '\n'; from = '\r'; to = '\n';
} }
z = *buff + *bytes; z = buff + *bytes;
p = *buff; p = buff;
while (p < z) { while (p < z) {
if (*p == from) *p = to; if (*p == from) *p = to;
++p; ++p;
} }
} else if (lineterm1 == LINETERM_CRNL) { } else if (lineterm1 == LINETERM_CRNL) {
/* buffer becomes shorter */ /* buffer might become shorter */
unsigned char to, *s, *t, *z; unsigned char to, *s, *t, *z;
if (lineterm2 == LINETERM_RAW) { if (lineterm2 == LINETERM_RAW) {
to = '\n'; to = '\n';
} else { } else {
to = '\r'; to = '\r';
} }
z = *buff + *bytes; z = buff + *bytes;
s = t = *buff; s = t = buff;
while (s < z) { while (s < z) {
if (*s == '\r') { if (*s == '\r') {
++s; ++s;
@ -1397,20 +1408,24 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz,
*t++ = *s++; *t++ = *s++;
} }
} }
*bufsiz = t - *buff; *bytes = t - buff;
} else { } else {
/* buffer becomes longer, must alloc another space */ /* buffer becomes longer (up to double length), must alloc another space */
unsigned char *buf2; static unsigned char *buf2; /*! not threadsafe */
unsigned char from; unsigned char *s, *t, *z; unsigned char from; unsigned char *s, *t, *z;
if (lineterm1 == LINETERM_RAW) { if (lineterm1 == LINETERM_RAW) {
from = '\n'; from = '\n';
} else { } else {
from = '\r'; from = '\r';
} }
if ((buf2 = Malloc(2*socat_opts.bufsiz/*sic!*/+1)) == NULL) { if (buf2 == NULL) {
if ((buf2 = Malloc(socat_opts.bufsiz)) == NULL) {
return -1; return -1;
} }
s = *buff; t = buf2; z = *buff + *bytes; }
memcpy(buf2, buff, *bytes);
s = buf2; t = buff; z = buf2 + *bytes;
while (s < z) { while (s < z) {
if (*s == from) { if (*s == from) {
*t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
@ -1420,9 +1435,7 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz,
*t++ = *s++; *t++ = *s++;
} }
} }
free(*buff); *bytes = t - buff;;
*buff = buf2;
*bufsiz = t - buf2;;
} }
return 0; return 0;
} }

10
sycls.c
View file

@ -23,6 +23,16 @@
#if WITH_SYCLS #if WITH_SYCLS
#if HAVE_PROTOTYPE_LIB_posix_memalign
int Posix_memalign(void **memptr, size_t alignment, size_t size) {
int result;
Debug3("posix_memalign(%p, "F_Zu", F_Zu)", memptr, alignment, size);
result = posix_memalign(memptr, alignment, size);
Debug1("posix_memalign(...) -> %d", result);
return result;
}
#endif /* HAVE_PROTOTYPE_LIB_posix_memalign */
mode_t Umask(mode_t mask) { mode_t Umask(mode_t mask) {
mode_t result; mode_t result;
int _errno; int _errno;

View file

@ -11,6 +11,7 @@ struct utsname;
struct flock; struct flock;
struct addrinfo; struct addrinfo;
int Posix_memalign(void **memptr, size_t alignment, size_t size);
mode_t Umask(mode_t mask); mode_t Umask(mode_t mask);
#endif /* WITH_SYCLS */ #endif /* WITH_SYCLS */
int Open(const char *pathname, int flags, mode_t mode); int Open(const char *pathname, int flags, mode_t mode);
@ -175,6 +176,7 @@ void Add_history(const char *string);
#else /* !WITH_SYCLS */ #else /* !WITH_SYCLS */
#define Posix_memalign(m,a,s) posix_memalign(m,a,s)
#define Umask(m) umask(m) #define Umask(m) umask(m)
#define Creat(p,m) creat(p,m) #define Creat(p,m) creat(p,m)
#define Lseek(f,o,w) lseek(f,o,w) #define Lseek(f,o,w) lseek(f,o,w)

58
test.sh
View file

@ -13614,7 +13614,7 @@ NAME=SCTP_SERVICENAME
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%sctp%*|*%$NAME%*) *%$N%*|*%functions%*|*%socket%*|*%sctp%*|*%$NAME%*)
TEST="$NAME: Service name resolution works with SCTP" TEST="$NAME: Service name resolution works with SCTP"
# invoke socat with address SCTP4-CONNECT:$LOCALHOST:http; when this does fails with # invoke socat with address SCTP4-CONNECT:$LOCALHOST:http; when this fails with
# "Connection refused", or does not fail at all, the test succeeded # "Connection refused", or does not fail at all, the test succeeded
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! runssctp4 "$((PORT))" >/dev/null; then elif ! runssctp4 "$((PORT))" >/dev/null; then
@ -13647,6 +13647,62 @@ esac
N=$((N+1)) N=$((N+1))
# Test the o-direct option on reading
NAME=O_DIRECT
case "$TESTS" in
*%$N%*|*%functions%*|*%engine%*|*%file%*|*%$NAME%*)
TEST="$NAME: echo via file with o-direct"
# Write data to a file and read it with options o-direct (and ignoreeof)
# When the data read is the same as the data written the test succeeded.
if ! eval $NUMCOND; then :;
elif ! testoptions o-direct >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}o-direct not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.file"
to="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
$PRINTF "test $F_n $TEST... " $N
CMD="$TRACE $SOCAT $opts - $tf,o-direct,ignoreeof!!$tf"
echo "$da" |$CMD >"$to" 2>"$te"
rc=$?
if [ $rc -ne 0 ] && grep -q "Invalid argument" "$te" && [ $UNAME = Linux ]; then
case $(stat -f /tmp/gerhard/ |grep -o "Type: [^[:space:]]*" |cut -c 7-) in
ext2/ext3|xfs|reiserfs)
$PRINTF "${FAILED}\n"
echo "$CMD" >&2
cat "$te" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" ;;
*) $PRINTF "${YELLOW}inable file system${NORMAL}\n"
numCANT=$((numCANT+1))
listCANT="$listCANT $N" ;;
esac
elif [ $rc -ne 0 ]; then
$PRINTF "${FAILED}:\n"
echo "$CMD" >&2
cat "$te" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
elif ! echo "$da" |diff - "$to" >$tdiff; then
$PRINTF "${FAILED}\n"
echo "$CMD" >&2
cat "$te" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
numOK=$((numOK+1))
fi # command ok
fi ;; # NUMCOND, feats
esac
N=$((N+1))
################################################################################## ##################################################################################
#================================================================================= #=================================================================================
# here come tests that might affect your systems integrity. Put normal tests # here come tests that might affect your systems integrity. Put normal tests