diff --git a/CHANGES b/CHANGES index aa9dcb1..ee7f55b 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,12 @@ Corrections: Mitigated race condition of quickly terminating SYSTEM or EXEC child 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: In gcc version 10 the default changed from -fcommon to -fno-common. Consequently, linking filan and procan failed with error diff --git a/config.h.in b/config.h.in index 57de248..ecb8112 100644 --- a/config.h.in +++ b/config.h.in @@ -69,6 +69,9 @@ /* Define if you have the socket function. */ #undef HAVE_SOCKET +/* Define if you have the posix_memalign function. */ +#undef HAVE_PROTOTYPE_LIB_posix_memalign + /* Define if you have the strdup function. */ #undef HAVE_PROTOTYPE_LIB_strdup diff --git a/configure.ac b/configure.ac index f39081b..3d04cbb 100644 --- a/configure.ac +++ b/configure.ac @@ -785,6 +785,7 @@ AC_CHECK_FUNCS(cfsetispeed cfgetispeed cfsetospeed cfgetospeed) ################################### # check for prototype and existence of functions that return a pointer # defines in config.h: HAVE_PROTOTYPE_LIB_$1 +AC_CHECK_PROTOTYPE_LIB(posix_memalign) AC_CHECK_PROTOTYPE_LIB(strdup) AC_CHECK_PROTOTYPE_LIB(strerror) AC_CHECK_PROTOTYPE_LIB(strstr) @@ -794,7 +795,6 @@ AC_CHECK_PROTOTYPE_LIB(memrchr) AC_CHECK_PROTOTYPE_LIB(if_indextoname) AC_CHECK_PROTOTYPE_LIB(ptsname) - AC_MSG_CHECKING(for long long) AC_CACHE_VAL(sc_cv_type_longlong, [AC_TRY_COMPILE([],[long long s;], diff --git a/socat.c b/socat.c index f6fe0fc..3e94c78 100644 --- a/socat.c +++ b/socat.c @@ -57,7 +57,7 @@ void socat_opt_hint(FILE *fd, char a, char b); void socat_version(FILE *fd); int socat(const char *address1, const char *address2); 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); static int socat_sigchild(struct single *file); @@ -708,7 +708,7 @@ int childleftdata(xiofile_t *xfd) { } 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 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); 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); if (buff == NULL) return -1; +#endif /* !HAVE_PROTOTYPE_LIB_posix_memalign */ if (socat_opts.logopt == 'm' && xioinqopt('l', NULL, 0) == 'm') { Info("switching to syslog"); @@ -988,7 +1001,7 @@ int _socat(void) { if (mayrd1 && maywr2) { mayrd1 = false; - if ((bytes1 = xiotransfer(sock1, sock2, &buff, socat_opts.bufsiz, false)) + if ((bytes1 = xiotransfer(sock1, sock2, buff, socat_opts.bufsiz, false)) < 0) { if (errno != EAGAIN) { closing = MAX(closing, 1); @@ -1020,7 +1033,7 @@ int _socat(void) { if (mayrd2 && maywr1) { mayrd2 = false; - if ((bytes2 = xiotransfer(sock2, sock1, &buff, socat_opts.bufsiz, true)) + if ((bytes2 = xiotransfer(sock2, sock1, buff, socat_opts.bufsiz, true)) < 0) { if (errno != EAGAIN) { closing = MAX(closing, 1); @@ -1106,7 +1119,7 @@ int _socat(void) { xioclose(sock1); xioclose(sock2); - free(buff); + free(buff); return 0; } @@ -1194,10 +1207,10 @@ static int */ /* inpipe, outpipe must be single descriptors (not dual!) */ 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; - bytes = xioread(inpipe, *buff, bufsiz); + bytes = xioread(inpipe, buff, bufsiz); if (bytes < 0) { if (errno != EAGAIN) XIO_RDSTREAM(inpipe)->eof = 2; @@ -1215,7 +1228,7 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, /* handle escape char */ if (XIO_RDSTREAM(inpipe)->escape != -1) { /* check input data for escape char */ - unsigned char *ptr = *buff; + unsigned char *ptr = buff; size_t ctr = 0; while (ctr < bytes) { if (*ptr == XIO_RDSTREAM(inpipe)->escape) { @@ -1251,8 +1264,8 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, size_t j; size_t N = 16; const unsigned char *end, *s, *t; - s = *buff; - end = (*buff)+bytes; + s = buff; + end = buff+bytes; xioprintblockheader(stderr, bytes, righttoleft); while (s < end) { /*! prefix? */ @@ -1298,8 +1311,8 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, size_t i = 0; xioprintblockheader(stderr, bytes, righttoleft); while (i < (size_t)bytes) { - int c = (*buff)[i]; - if (i > 0 && (*buff)[i-1] == '\n') + int c = buff[i]; + if (i > 0 && buff[i-1] == '\n') /*! prefix? */; switch (c) { case '\a' : fputs("\\a", stderr); break; @@ -1323,12 +1336,12 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, /* print prefix */ xioprintblockheader(stderr, bytes, righttoleft); for (i = 0; i < bytes; ++i) { - fprintf(stderr, " %02x", (*buff)[i]); + fprintf(stderr, " %02x", buff[i]); } fputc('\n', stderr); } - writt = xiowrite(outpipe, *buff, bytes); + writt = xiowrite(outpipe, buff, bytes); if (writt < 0) { /* EAGAIN when nonblocking but a mandatory lock is on file. 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 specified in lineterm1 to that of lineterm2. Possible values are LINETERM_CR, LINETERM_CRNL, LINETERM_RAW. - buff points to the malloc()'ed data, input and output. It may be subject to - realloc(). bytes specifies the number of bytes input and output */ -int cv_newline(unsigned char **buff, ssize_t *bufsiz, + bytes specifies the number of bytes input and output */ +int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2) { - ssize_t *bytes = bufsiz; /* must perform newline changes */ if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) { /* no change in data length */ @@ -1369,23 +1380,23 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz, } else { from = '\r'; to = '\n'; } - z = *buff + *bytes; - p = *buff; + z = buff + *bytes; + p = buff; while (p < z) { if (*p == from) *p = to; ++p; } } else if (lineterm1 == LINETERM_CRNL) { - /* buffer becomes shorter */ + /* buffer might become shorter */ unsigned char to, *s, *t, *z; if (lineterm2 == LINETERM_RAW) { to = '\n'; } else { to = '\r'; } - z = *buff + *bytes; - s = t = *buff; + z = buff + *bytes; + s = t = buff; while (s < z) { if (*s == '\r') { ++s; @@ -1397,20 +1408,24 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz, *t++ = *s++; } } - *bufsiz = t - *buff; + *bytes = t - buff; } else { - /* buffer becomes longer, must alloc another space */ - unsigned char *buf2; + /* buffer becomes longer (up to double length), must alloc another space */ + static unsigned char *buf2; /*! not threadsafe */ unsigned char from; unsigned char *s, *t, *z; + if (lineterm1 == LINETERM_RAW) { from = '\n'; } else { from = '\r'; } - if ((buf2 = Malloc(2*socat_opts.bufsiz/*sic!*/+1)) == NULL) { - return -1; + if (buf2 == NULL) { + if ((buf2 = Malloc(socat_opts.bufsiz)) == NULL) { + return -1; + } } - s = *buff; t = buf2; z = *buff + *bytes; + memcpy(buf2, buff, *bytes); + s = buf2; t = buff; z = buf2 + *bytes; while (s < z) { if (*s == from) { *t++ = '\r'; *t++ = '\n'; @@ -1420,9 +1435,7 @@ int cv_newline(unsigned char **buff, ssize_t *bufsiz, *t++ = *s++; } } - free(*buff); - *buff = buf2; - *bufsiz = t - buf2;; + *bytes = t - buff;; } return 0; } diff --git a/sycls.c b/sycls.c index 0767f57..60d3682 100644 --- a/sycls.c +++ b/sycls.c @@ -23,6 +23,16 @@ #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 result; int _errno; diff --git a/sycls.h b/sycls.h index 2d5e706..8bbcc45 100644 --- a/sycls.h +++ b/sycls.h @@ -11,6 +11,7 @@ struct utsname; struct flock; struct addrinfo; +int Posix_memalign(void **memptr, size_t alignment, size_t size); mode_t Umask(mode_t mask); #endif /* WITH_SYCLS */ int Open(const char *pathname, int flags, mode_t mode); @@ -175,6 +176,7 @@ void Add_history(const char *string); #else /* !WITH_SYCLS */ +#define Posix_memalign(m,a,s) posix_memalign(m,a,s) #define Umask(m) umask(m) #define Creat(p,m) creat(p,m) #define Lseek(f,o,w) lseek(f,o,w) diff --git a/test.sh b/test.sh index a775122..25cbae0 100755 --- a/test.sh +++ b/test.sh @@ -13614,7 +13614,7 @@ NAME=SCTP_SERVICENAME case "$TESTS" in *%$N%*|*%functions%*|*%socket%*|*%sctp%*|*%$NAME%*) 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 if ! eval $NUMCOND; then :; elif ! runssctp4 "$((PORT))" >/dev/null; then @@ -13647,6 +13647,62 @@ esac 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