* 'vipw-shortwrite' of https://github.com/DankRank/util-linux:
  ul_copy_file: make defines for return values
  read_all: return 0 when EOF occurs after 0 bytes
  ul_copy_file: add test program
  ul_copy_file: handle EAGAIN and EINTR
  ul_copy_file: use all_read/all_write
  ul_copy_file: use BUFSSIZ for buffer size
  nologin: use ul_copy_file
  login: use ul_copy_file
  configure.ac: check for sendfile
  ul_copy_file: use sendfile
  vipw: move copyfile to the lib
  vipw: fix short write handling in copyfile
This commit is contained in:
Karel Zak 2020-11-09 11:06:27 +01:00
commit bb123ad503
8 changed files with 101 additions and 32 deletions

View File

@ -530,6 +530,7 @@ AC_CHECK_FUNCS([ \
qsort_r \
rpmatch \
scandirat \
sendfile \
setprogname \
setresgid \
setresuid \

View File

@ -12,6 +12,10 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#ifdef HAVE_SENDFILE
#include <sys/sendfile.h>
#endif
#include "c.h"
@ -63,13 +67,15 @@ static inline ssize_t read_all(int fd, char *buf, size_t count)
memset(buf, 0, count);
while (count > 0) {
ret = read(fd, buf, count);
if (ret <= 0) {
if (ret < 0 && (errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
if (ret < 0) {
if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
xusleep(250000);
continue;
}
return c ? c : -1;
}
if (ret == 0)
return c;
tries = 0;
count -= ret;
buf += ret;
@ -78,4 +84,32 @@ static inline ssize_t read_all(int fd, char *buf, size_t count)
return c;
}
static inline ssize_t sendfile_all(int out, int in, off_t *off, size_t count)
{
#ifdef HAVE_SENDFILE
ssize_t ret;
ssize_t c = 0;
int tries = 0;
while (count) {
ret = sendfile(out, in, off, count);
if (ret < 0) {
if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
xusleep(250000);
continue;
}
return c ? c : -1;
}
if (ret == 0)
return c;
tries = 0;
count -= ret;
c += ret;
}
return c;
#else
errno = ENOSYS;
return -1;
#endif
}
#endif /* UTIL_LINUX_ALL_IO_H */

View File

@ -74,4 +74,8 @@ static inline struct dirent *xreaddir(DIR *dp)
extern void close_all_fds(const int exclude[], size_t exsz);
#define UL_COPY_READ_ERROR (-1)
#define UL_COPY_WRITE_ERROR (-2)
int ul_copy_file(int from, int to);
#endif /* UTIL_LINUX_FILEUTILS */

View File

@ -6,12 +6,15 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <string.h>
#include "c.h"
#include "all-io.h"
#include "fileutils.h"
#include "pathnames.h"
@ -174,7 +177,7 @@ void close_all_fds(const int exclude[], size_t exsz)
int main(int argc, char *argv[])
{
if (argc < 2)
errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds}", argv[0]);
errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]);
if (strcmp(argv[1], "--mkstemp") == 0) {
FILE *f;
@ -194,6 +197,12 @@ int main(int argc, char *argv[])
ignore_result( dup(STDIN_FILENO) );
close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds));
} else if (strcmp(argv[1], "--copy-file") == 0) {
int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
if (ret == UL_COPY_READ_ERROR)
err(EXIT_FAILURE, "read");
else if (ret == UL_COPY_WRITE_ERROR)
err(EXIT_FAILURE, "write");
}
return EXIT_SUCCESS;
}
@ -246,3 +255,42 @@ char *stripoff_last_component(char *path)
*p = '\0';
return p + 1;
}
static int copy_file_simple(int from, int to)
{
ssize_t nr;
char buf[BUFSIZ];
while ((nr = read_all(from, buf, sizeof(buf))) > 0)
if (write_all(to, buf, nr) == -1)
return UL_COPY_WRITE_ERROR;
if (nr < 0)
return UL_COPY_READ_ERROR;
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(buf, sizeof(buf));
#endif
return 0;
}
/* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
int ul_copy_file(int from, int to)
{
#ifdef HAVE_SENDFILE
struct stat st;
ssize_t nw;
if (fstat(from, &st) == -1)
return UL_COPY_READ_ERROR;
if (!S_ISREG(st.st_mode))
return copy_file_simple(from, to);
if (sendfile_all(to, from, NULL, st.st_size) < 0)
return copy_file_simple(from, to);
/* ensure we either get an EOF or an error */
while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
if (nw < 0)
return copy_file_simple(from, to);
return 0;
#else
return copy_file_simple(from, to);
#endif
}

View File

@ -76,6 +76,7 @@ if BUILD_NOLOGIN
sbin_PROGRAMS += nologin
dist_man_MANS += login-utils/nologin.8
nologin_SOURCES = login-utils/nologin.c
nologin_LDADD = $(LDADD) libcommon.la
endif

View File

@ -61,7 +61,6 @@
#elif defined(HAVE_SECURITY_OPENPAM_H)
# include <security/openpam.h>
#endif
#include <sys/sendfile.h>
#ifdef HAVE_LIBAUDIT
# include <libaudit.h>
@ -345,9 +344,7 @@ static int motddir(const char *dirname)
fd = openat(dd, d->d_name, O_RDONLY|O_CLOEXEC);
if (fd >= 0) {
struct stat st;
if (fstat(fd, &st) == 0 && st.st_size > 0)
sendfile(fileno(stdout), fd, NULL, st.st_size);
ul_copy_file(fd, fileno(stdout));
close(fd);
done++;
}
@ -396,7 +393,7 @@ static void motd(void)
if (S_ISREG(st.st_mode) && st.st_size > 0) {
int fd = open(file, O_RDONLY, 0);
if (fd >= 0) {
sendfile(fileno(stdout), fd, NULL, st.st_size);
ul_copy_file(fd, fileno(stdout));
close(fd);
}
done++;

View File

@ -14,6 +14,7 @@
#include "c.h"
#include "nls.h"
#include "pathnames.h"
#include "fileutils.h"
/*
* Always return EXIT_FAILURE (1), don't try to be smart!
@ -97,12 +98,7 @@ int main(int argc, char *argv[])
if (c < 0 || !S_ISREG(st.st_mode))
goto dflt;
else {
char buf[BUFSIZ];
ssize_t rd;
while ((rd = read(fd, buf, sizeof(buf))) > 0)
ignore_result( write(STDOUT_FILENO, buf, rd) );
ul_copy_file(fd, STDOUT_FILENO);
close(fd);
return EXIT_FAILURE;
}

View File

@ -88,23 +88,6 @@ static char *tmp_file; /* tmp file */
void pw_error (char *, int, int);
static void copyfile(int from, int to)
{
int nr, nw, off;
char buf[8 * 1024];
while ((nr = read(from, buf, sizeof(buf))) > 0)
for (off = 0; off < nr; nr -= nw, off += nw)
if ((nw = write(to, buf + off, nr)) < 0)
pw_error(tmp_file, 1, 1);
if (nr < 0)
pw_error(orig_file, 1, 1);
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(buf, sizeof(buf));
#endif
}
static void pw_init(void)
{
struct rlimit rlim;
@ -139,14 +122,19 @@ static FILE * pw_tmpfile(int lockfd)
{
FILE *fd;
char *tmpname = NULL;
int res;
if ((fd = xfmkstemp(&tmpname, "/etc", ".vipw")) == NULL) {
ulckpwdf();
err(EXIT_FAILURE, _("can't open temporary file"));
}
copyfile(lockfd, fileno(fd));
tmp_file = tmpname;
res = ul_copy_file(lockfd, fileno(fd));
if (res == UL_COPY_READ_ERROR)
pw_error(orig_file, 1, 1);
else if (res == UL_COPY_WRITE_ERROR)
pw_error(tmp_file, 1, 1);
return fd;
}