From: Todd C. Miller Date: Wed, 30 May 2012 14:46:02 +0000 (-0400) Subject: No need to loop over atomic_writev(), it guarantees to write all X-Git-Tag: SUDO_1_8_6^2~168 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b406b51da0684490de642b8fc189f696cf347b23;p=sudo No need to loop over atomic_writev(), it guarantees to write all data or return an error. Fix handling of stdout/stderr that contains "\r\n" and handle a "\r\n" pair that spans a buffer. --- diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index 52c60c294..bcd6304f9 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -247,7 +247,7 @@ main(int argc, char *argv[]) char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; double seconds, to_wait, speed = 1.0, max_wait = 0; sigaction_t sa; - size_t len, nbytes, nread, nwritten, off; + size_t len, nbytes, nread; struct log_info *li; struct iovec *iov = NULL; int iovcnt = 0, iovmax = 0; @@ -412,6 +412,8 @@ main(int argc, char *argv[]) #else while (fgets(buf, sizeof(buf), io_fds[IOFD_TIMING].f) != NULL) { #endif + char last_char = '\0'; + if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) errorx(1, _("invalid timing file line: %s"), buf); @@ -444,51 +446,59 @@ main(int argc, char *argv[]) nread = fread(buf, 1, len, io_fds[idx].f); #endif nbytes -= nread; - off = 0; - do { - /* Convert newline to carriage return + linefeed if needed. */ - if (need_nlcr) { - size_t linelen; - cp = buf + off; - iovcnt = 0; - while ((ep = memchr(cp, '\n', nread - off)) != NULL) { - /* Is there already a carriage return? */ - if (cp != ep && ep[-1] == '\r') - continue; - - /* Store the line in iov followed by \r\n pair. */ - if (iovcnt + 3 > iovmax) { - iovmax <<= 1; - iov = erealloc3(iov, iovmax, sizeof(*iov)); - } - linelen = (size_t)(ep - cp); - iov[iovcnt].iov_base = cp; - iov[iovcnt].iov_len = linelen; - iovcnt++; - iov[iovcnt].iov_base = "\r\n"; - iov[iovcnt].iov_len = 2; - iovcnt++; - off += linelen + 1; - cp = ep + 1; + + /* Convert newline to carriage return + linefeed if needed. */ + if (need_nlcr) { + size_t remainder = nread; + size_t linelen; + iovcnt = 0; + cp = buf; + ep = cp - 1; + /* Handle a "\r\n" pair that spans a buffer. */ + if (last_char == '\r' && buf[0] == '\n') { + ep++; + remainder--; + } + while ((ep = memchr(ep + 1, '\n', remainder)) != NULL) { + /* Is there already a carriage return? */ + if (cp != ep && ep[-1] == '\r') { + remainder = (size_t)(&buf[nread - 1] - ep); + continue; } - if (nread - off != 0) { - linelen = nread - off; - iov[iovcnt].iov_base = cp; - iov[iovcnt].iov_len = linelen; - iovcnt++; - off += linelen; + + /* Store the line in iov followed by \r\n pair. */ + if (iovcnt + 3 > iovmax) { + iovmax <<= 1; + iov = erealloc3(iov, iovmax, sizeof(*iov)); } - /* Note: off already adjusted above. */ - (void)atomic_writev(STDOUT_FILENO, iov, iovcnt); - } else { - /* No conversion needed. */ - iov[0].iov_base = buf + off; - iov[0].iov_len = nread - off; - iovcnt = 1; - nwritten = atomic_writev(STDOUT_FILENO, iov, iovcnt); - off += nwritten; + linelen = (size_t)(ep - cp) + 1; + iov[iovcnt].iov_base = cp; + iov[iovcnt].iov_len = linelen - 1; /* not including \n */ + iovcnt++; + iov[iovcnt].iov_base = "\r\n"; + iov[iovcnt].iov_len = 2; + iovcnt++; + cp = ep + 1; + remainder -= linelen; + } + if (cp - buf != nread) { + /* + * Partial line without a linefeed or multiple lines + * with \r\n pairs. + */ + iov[iovcnt].iov_base = cp; + iov[iovcnt].iov_len = nread - (cp - buf); + iovcnt++; } - } while (nread > off); + last_char = buf[nread - 1]; /* stash last char of old buffer */ + } else { + /* No conversion needed. */ + iov[0].iov_base = buf; + iov[0].iov_len = nread; + iovcnt = 1; + } + if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1) + error(1, _("writing to standard output")); } } term_restore(STDIN_FILENO, 1); @@ -594,7 +604,8 @@ atomic_writev(int fd, struct iovec *iov, int iovcnt) } if (errno == EINTR) continue; - error(1, "writing to standard output"); + nwritten = -1; + break; } debug_return_size_t(nwritten); }