qrexec-lib: convert tabs to spaces
- Fix compile error on gcc 6 (-Werror=misleading-indentation) - Follow coding style: https://www.qubes-os.org/doc/coding-style/
This commit is contained in:
parent
93f676d998
commit
410ad3d25f
@ -28,88 +28,88 @@
|
|||||||
static int total_mem;
|
static int total_mem;
|
||||||
static char *limited_malloc(int len)
|
static char *limited_malloc(int len)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
total_mem += len;
|
total_mem += len;
|
||||||
if (total_mem > BUFFER_LIMIT) {
|
if (total_mem > BUFFER_LIMIT) {
|
||||||
fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n");
|
fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
ret = malloc(len);
|
ret = malloc(len);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void limited_free(char *ptr, int len)
|
static void limited_free(char *ptr, int len)
|
||||||
{
|
{
|
||||||
free(ptr);
|
free(ptr);
|
||||||
total_mem -= len;
|
total_mem -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_init(struct buffer *b)
|
void buffer_init(struct buffer *b)
|
||||||
{
|
{
|
||||||
b->buflen = 0;
|
b->buflen = 0;
|
||||||
b->data = NULL;
|
b->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_free(struct buffer *b)
|
void buffer_free(struct buffer *b)
|
||||||
{
|
{
|
||||||
if (b->buflen)
|
if (b->buflen)
|
||||||
limited_free(b->data, b->buflen);
|
limited_free(b->data, b->buflen);
|
||||||
buffer_init(b);
|
buffer_init(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The following two functions can be made much more efficient.
|
The following two functions can be made much more efficient.
|
||||||
Yet the profiling output show they are not significant CPU hogs, so
|
Yet the profiling output show they are not significant CPU hogs, so
|
||||||
we keep them so simple to make them obviously correct.
|
we keep them so simple to make them obviously correct.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void buffer_append(struct buffer *b, const char *data, int len)
|
void buffer_append(struct buffer *b, const char *data, int len)
|
||||||
{
|
{
|
||||||
int newsize;
|
int newsize;
|
||||||
char *qdata;
|
char *qdata;
|
||||||
if (len < 0 || len > BUFFER_LIMIT) {
|
if (len < 0 || len > BUFFER_LIMIT) {
|
||||||
fprintf(stderr, "buffer_append %d\n", len);
|
fprintf(stderr, "buffer_append %d\n", len);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
newsize = len + b->buflen;
|
newsize = len + b->buflen;
|
||||||
qdata = limited_malloc(len + b->buflen);
|
qdata = limited_malloc(len + b->buflen);
|
||||||
memcpy(qdata, b->data, b->buflen);
|
memcpy(qdata, b->data, b->buflen);
|
||||||
memcpy(qdata + b->buflen, data, len);
|
memcpy(qdata + b->buflen, data, len);
|
||||||
buffer_free(b);
|
buffer_free(b);
|
||||||
b->buflen = newsize;
|
b->buflen = newsize;
|
||||||
b->data = qdata;
|
b->data = qdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_remove(struct buffer *b, int len)
|
void buffer_remove(struct buffer *b, int len)
|
||||||
{
|
{
|
||||||
int newsize;
|
int newsize;
|
||||||
char *qdata = NULL;
|
char *qdata = NULL;
|
||||||
if (len < 0 || len > b->buflen) {
|
if (len < 0 || len > b->buflen) {
|
||||||
fprintf(stderr, "buffer_remove %d/%d\n", len, b->buflen);
|
fprintf(stderr, "buffer_remove %d/%d\n", len, b->buflen);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
newsize = b->buflen - len;
|
newsize = b->buflen - len;
|
||||||
if (newsize > 0) {
|
if (newsize > 0) {
|
||||||
qdata = limited_malloc(newsize);
|
qdata = limited_malloc(newsize);
|
||||||
memcpy(qdata, b->data + len, newsize);
|
memcpy(qdata, b->data + len, newsize);
|
||||||
}
|
}
|
||||||
buffer_free(b);
|
buffer_free(b);
|
||||||
b->buflen = newsize;
|
b->buflen = newsize;
|
||||||
b->data = qdata;
|
b->data = qdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
int buffer_len(struct buffer *b)
|
int buffer_len(struct buffer *b)
|
||||||
{
|
{
|
||||||
return b->buflen;
|
return b->buflen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *buffer_data(struct buffer *b)
|
void *buffer_data(struct buffer *b)
|
||||||
{
|
{
|
||||||
return b->data;
|
return b->data;
|
||||||
}
|
}
|
||||||
|
@ -6,44 +6,44 @@
|
|||||||
notify_progress_t *notify_progress_func = NULL;
|
notify_progress_t *notify_progress_func = NULL;
|
||||||
void register_notify_progress(notify_progress_t *func)
|
void register_notify_progress(notify_progress_t *func)
|
||||||
{
|
{
|
||||||
notify_progress_func = func;
|
notify_progress_func = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_file(int outfd, int infd, long long size, unsigned long *crc32)
|
int copy_file(int outfd, int infd, long long size, unsigned long *crc32)
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
long long written = 0;
|
long long written = 0;
|
||||||
int ret;
|
int ret;
|
||||||
int count;
|
int count;
|
||||||
while (written < size) {
|
while (written < size) {
|
||||||
if (size - written > (int)sizeof(buf))
|
if (size - written > (int)sizeof(buf))
|
||||||
count = sizeof buf;
|
count = sizeof buf;
|
||||||
else
|
else
|
||||||
count = size - written;
|
count = size - written;
|
||||||
ret = read(infd, buf, count);
|
ret = read(infd, buf, count);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return COPY_FILE_READ_EOF;
|
return COPY_FILE_READ_EOF;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return COPY_FILE_READ_ERROR;
|
return COPY_FILE_READ_ERROR;
|
||||||
/* acumulate crc32 if requested */
|
/* acumulate crc32 if requested */
|
||||||
if (crc32)
|
if (crc32)
|
||||||
*crc32 = Crc32_ComputeBuf(*crc32, buf, ret);
|
*crc32 = Crc32_ComputeBuf(*crc32, buf, ret);
|
||||||
if (!write_all(outfd, buf, ret))
|
if (!write_all(outfd, buf, ret))
|
||||||
return COPY_FILE_WRITE_ERROR;
|
return COPY_FILE_WRITE_ERROR;
|
||||||
if (notify_progress_func != NULL)
|
if (notify_progress_func != NULL)
|
||||||
notify_progress_func(ret, 0);
|
notify_progress_func(ret, 0);
|
||||||
written += ret;
|
written += ret;
|
||||||
}
|
}
|
||||||
return COPY_FILE_OK;
|
return COPY_FILE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * copy_file_status_to_str(int status)
|
const char * copy_file_status_to_str(int status)
|
||||||
{
|
{
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case COPY_FILE_OK: return "OK";
|
case COPY_FILE_OK: return "OK";
|
||||||
case COPY_FILE_READ_EOF: return "Unexpected end of data while reading";
|
case COPY_FILE_READ_EOF: return "Unexpected end of data while reading";
|
||||||
case COPY_FILE_READ_ERROR: return "Error reading";
|
case COPY_FILE_READ_ERROR: return "Error reading";
|
||||||
case COPY_FILE_WRITE_ERROR: return "Error writing";
|
case COPY_FILE_WRITE_ERROR: return "Error writing";
|
||||||
default: return "????????";
|
default: return "????????";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,56 +28,56 @@
|
|||||||
|
|
||||||
static do_exec_t *exec_func = NULL;
|
static do_exec_t *exec_func = NULL;
|
||||||
void register_exec_func(do_exec_t *func) {
|
void register_exec_func(do_exec_t *func) {
|
||||||
exec_func = func;
|
exec_func = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_fds(int fdin, int fdout, int fderr)
|
void fix_fds(int fdin, int fdout, int fderr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 256; i++)
|
for (i = 0; i < 256; i++)
|
||||||
if (i != fdin && i != fdout && i != fderr)
|
if (i != fdin && i != fdout && i != fderr)
|
||||||
close(i);
|
close(i);
|
||||||
dup2(fdin, 0);
|
dup2(fdin, 0);
|
||||||
dup2(fdout, 1);
|
dup2(fdout, 1);
|
||||||
dup2(fderr, 2);
|
dup2(fderr, 2);
|
||||||
close(fdin);
|
close(fdin);
|
||||||
close(fdout);
|
close(fdout);
|
||||||
if (fderr != 2)
|
if (fderr != 2)
|
||||||
close(fderr);
|
close(fderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||||
int *stderr_fd)
|
int *stderr_fd)
|
||||||
{
|
{
|
||||||
int inpipe[2], outpipe[2], errpipe[2];
|
int inpipe[2], outpipe[2], errpipe[2];
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
|
||||||
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe) ||
|
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe) ||
|
||||||
(stderr_fd && socketpair(AF_UNIX, SOCK_STREAM, 0, errpipe))) {
|
(stderr_fd && socketpair(AF_UNIX, SOCK_STREAM, 0, errpipe))) {
|
||||||
perror("socketpair");
|
perror("socketpair");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
switch (*pid = fork()) {
|
switch (*pid = fork()) {
|
||||||
case -1:
|
case -1:
|
||||||
perror("fork");
|
perror("fork");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
case 0:
|
case 0:
|
||||||
if (stderr_fd) {
|
if (stderr_fd) {
|
||||||
fix_fds(inpipe[0], outpipe[1], errpipe[1]);
|
fix_fds(inpipe[0], outpipe[1], errpipe[1]);
|
||||||
} else
|
} else
|
||||||
fix_fds(inpipe[0], outpipe[1], 2);
|
fix_fds(inpipe[0], outpipe[1], 2);
|
||||||
|
|
||||||
if (exec_func != NULL)
|
if (exec_func != NULL)
|
||||||
exec_func(cmdline);
|
exec_func(cmdline);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
close(inpipe[0]);
|
close(inpipe[0]);
|
||||||
close(outpipe[1]);
|
close(outpipe[1]);
|
||||||
*stdin_fd = inpipe[1];
|
*stdin_fd = inpipe[1];
|
||||||
*stdout_fd = outpipe[0];
|
*stdout_fd = outpipe[0];
|
||||||
if (stderr_fd) {
|
if (stderr_fd) {
|
||||||
close(errpipe[1]);
|
close(errpipe[1]);
|
||||||
*stderr_fd = errpipe[0];
|
*stderr_fd = errpipe[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,90 +27,90 @@
|
|||||||
|
|
||||||
void perror_wrapper(const char * msg)
|
void perror_wrapper(const char * msg)
|
||||||
{
|
{
|
||||||
int prev=errno;
|
int prev=errno;
|
||||||
perror(msg);
|
perror(msg);
|
||||||
errno=prev;
|
errno=prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_nonblock(int fd)
|
void set_nonblock(int fd)
|
||||||
{
|
{
|
||||||
int fl = fcntl(fd, F_GETFL, 0);
|
int fl = fcntl(fd, F_GETFL, 0);
|
||||||
if (fl & O_NONBLOCK)
|
if (fl & O_NONBLOCK)
|
||||||
return;
|
return;
|
||||||
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_block(int fd)
|
void set_block(int fd)
|
||||||
{
|
{
|
||||||
int fl = fcntl(fd, F_GETFL, 0);
|
int fl = fcntl(fd, F_GETFL, 0);
|
||||||
if (!(fl & O_NONBLOCK))
|
if (!(fl & O_NONBLOCK))
|
||||||
return;
|
return;
|
||||||
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
|
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_all(int fd, const void *buf, int size)
|
int write_all(int fd, const void *buf, int size)
|
||||||
{
|
{
|
||||||
int written = 0;
|
int written = 0;
|
||||||
int ret;
|
int ret;
|
||||||
while (written < size) {
|
while (written < size) {
|
||||||
ret = write(fd, (char *) buf + written, size - written);
|
ret = write(fd, (char *) buf + written, size - written);
|
||||||
if (ret == -1 && errno == EINTR)
|
if (ret == -1 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
written += ret;
|
written += ret;
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "sent %d bytes\n", size);
|
// fprintf(stderr, "sent %d bytes\n", size);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_all(int fd, void *buf, int size)
|
int read_all(int fd, void *buf, int size)
|
||||||
{
|
{
|
||||||
int got_read = 0;
|
int got_read = 0;
|
||||||
int ret;
|
int ret;
|
||||||
while (got_read < size) {
|
while (got_read < size) {
|
||||||
ret = read(fd, (char *) buf + got_read, size - got_read);
|
ret = read(fd, (char *) buf + got_read, size - got_read);
|
||||||
if (ret == -1 && errno == EINTR)
|
if (ret == -1 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
fprintf(stderr, "EOF\n");
|
fprintf(stderr, "EOF\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno != EAGAIN)
|
if (errno != EAGAIN)
|
||||||
perror_wrapper("read");
|
perror_wrapper("read");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (got_read == 0) {
|
if (got_read == 0) {
|
||||||
// force blocking operation on further reads
|
// force blocking operation on further reads
|
||||||
set_block(fd);
|
set_block(fd);
|
||||||
}
|
}
|
||||||
got_read += ret;
|
got_read += ret;
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "read %d bytes\n", size);
|
// fprintf(stderr, "read %d bytes\n", size);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_fd_all(int fdout, int fdin)
|
int copy_fd_all(int fdout, int fdin)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ret = read(fdin, buf, sizeof(buf));
|
ret = read(fdin, buf, sizeof(buf));
|
||||||
if (ret == -1 && errno == EINTR)
|
if (ret == -1 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (!ret)
|
if (!ret)
|
||||||
break;
|
break;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
perror_wrapper("read");
|
perror_wrapper("read");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!write_all(fdout, buf, ret)) {
|
if (!write_all(fdout, buf, ret)) {
|
||||||
perror_wrapper("write");
|
perror_wrapper("write");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
#include <libvchan.h>
|
#include <libvchan.h>
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
char *data;
|
char *data;
|
||||||
int buflen;
|
int buflen;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* return codes for buffered writes */
|
/* return codes for buffered writes */
|
||||||
@ -48,7 +48,7 @@ int write_stdin(int fd, const char *data, int len, struct buffer *buffer);
|
|||||||
int fork_and_flush_stdin(int fd, struct buffer *buffer);
|
int fork_and_flush_stdin(int fd, struct buffer *buffer);
|
||||||
|
|
||||||
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||||
int *stderr_fd);
|
int *stderr_fd);
|
||||||
void wait_for_vchan_or_argfd(libvchan_t *vchan, int max, fd_set * rdset, fd_set * wrset);
|
void wait_for_vchan_or_argfd(libvchan_t *vchan, int max, fd_set * rdset, fd_set * wrset);
|
||||||
int read_vchan_all(libvchan_t *vchan, void *data, size_t size);
|
int read_vchan_all(libvchan_t *vchan, void *data, size_t size);
|
||||||
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size);
|
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size);
|
||||||
|
@ -35,32 +35,32 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
struct file_header {
|
struct file_header {
|
||||||
unsigned int namelen;
|
unsigned int namelen;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
unsigned long long filelen;
|
unsigned long long filelen;
|
||||||
unsigned int atime;
|
unsigned int atime;
|
||||||
unsigned int atime_nsec;
|
unsigned int atime_nsec;
|
||||||
unsigned int mtime;
|
unsigned int mtime;
|
||||||
unsigned int mtime_nsec;
|
unsigned int mtime_nsec;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct result_header {
|
struct result_header {
|
||||||
uint32_t error_code;
|
uint32_t error_code;
|
||||||
uint32_t _pad;
|
uint32_t _pad;
|
||||||
uint64_t crc32;
|
uint64_t crc32;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
/* optional info about last processed file */
|
/* optional info about last processed file */
|
||||||
struct result_header_ext {
|
struct result_header_ext {
|
||||||
uint32_t last_namelen;
|
uint32_t last_namelen;
|
||||||
char last_name[0];
|
char last_name[0];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
COPY_FILE_OK,
|
COPY_FILE_OK,
|
||||||
COPY_FILE_READ_EOF,
|
COPY_FILE_READ_EOF,
|
||||||
COPY_FILE_READ_ERROR,
|
COPY_FILE_READ_ERROR,
|
||||||
COPY_FILE_WRITE_ERROR
|
COPY_FILE_WRITE_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
/* feedback handling */
|
/* feedback handling */
|
||||||
|
@ -34,91 +34,91 @@ _Noreturn static void call_error_handler(const char *fmt, ...)
|
|||||||
|
|
||||||
static int write_all_with_crc(int fd, const void *buf, int size)
|
static int write_all_with_crc(int fd, const void *buf, int size)
|
||||||
{
|
{
|
||||||
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
|
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
|
||||||
return write_all(fd, buf, size);
|
return write_all(fd, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify_end_and_wait_for_result(void)
|
void notify_end_and_wait_for_result(void)
|
||||||
{
|
{
|
||||||
struct file_header end_hdr;
|
struct file_header end_hdr;
|
||||||
|
|
||||||
/* nofity end of transfer */
|
/* nofity end of transfer */
|
||||||
memset(&end_hdr, 0, sizeof(end_hdr));
|
memset(&end_hdr, 0, sizeof(end_hdr));
|
||||||
end_hdr.namelen = 0;
|
end_hdr.namelen = 0;
|
||||||
end_hdr.filelen = 0;
|
end_hdr.filelen = 0;
|
||||||
write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
|
write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
|
||||||
|
|
||||||
set_block(0);
|
set_block(0);
|
||||||
wait_for_result();
|
wait_for_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_for_result(void)
|
void wait_for_result(void)
|
||||||
{
|
{
|
||||||
struct result_header hdr;
|
struct result_header hdr;
|
||||||
struct result_header_ext hdr_ext;
|
struct result_header_ext hdr_ext;
|
||||||
char last_filename[MAX_PATH_LENGTH + 1];
|
char last_filename[MAX_PATH_LENGTH + 1];
|
||||||
char last_filename_prefix[] = "; Last file: ";
|
char last_filename_prefix[] = "; Last file: ";
|
||||||
|
|
||||||
if (!read_all(0, &hdr, sizeof(hdr))) {
|
if (!read_all(0, &hdr, sizeof(hdr))) {
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
// no result sent and stdin still open
|
// no result sent and stdin still open
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// other read error or EOF
|
// other read error or EOF
|
||||||
exit(1); // hopefully remote has produced error message
|
exit(1); // hopefully remote has produced error message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!read_all(0, &hdr_ext, sizeof(hdr_ext))) {
|
if (!read_all(0, &hdr_ext, sizeof(hdr_ext))) {
|
||||||
// remote used old result_header struct
|
// remote used old result_header struct
|
||||||
hdr_ext.last_namelen = 0;
|
hdr_ext.last_namelen = 0;
|
||||||
}
|
}
|
||||||
if (hdr_ext.last_namelen > MAX_PATH_LENGTH) {
|
if (hdr_ext.last_namelen > MAX_PATH_LENGTH) {
|
||||||
// read only at most MAX_PATH_LENGTH chars
|
// read only at most MAX_PATH_LENGTH chars
|
||||||
hdr_ext.last_namelen = MAX_PATH_LENGTH;
|
hdr_ext.last_namelen = MAX_PATH_LENGTH;
|
||||||
}
|
}
|
||||||
if (!read_all(0, last_filename, hdr_ext.last_namelen)) {
|
if (!read_all(0, last_filename, hdr_ext.last_namelen)) {
|
||||||
fprintf(stderr, "Failed to get last filename\n");
|
fprintf(stderr, "Failed to get last filename\n");
|
||||||
hdr_ext.last_namelen = 0;
|
hdr_ext.last_namelen = 0;
|
||||||
}
|
}
|
||||||
last_filename[hdr_ext.last_namelen] = '\0';
|
last_filename[hdr_ext.last_namelen] = '\0';
|
||||||
if (!hdr_ext.last_namelen)
|
if (!hdr_ext.last_namelen)
|
||||||
/* set prefix to empty string */
|
/* set prefix to empty string */
|
||||||
last_filename_prefix[0] = '\0';
|
last_filename_prefix[0] = '\0';
|
||||||
|
|
||||||
errno = hdr.error_code;
|
errno = hdr.error_code;
|
||||||
if (hdr.error_code != 0) {
|
if (hdr.error_code != 0) {
|
||||||
switch (hdr.error_code) {
|
switch (hdr.error_code) {
|
||||||
case EEXIST:
|
case EEXIST:
|
||||||
call_error_handler("File copy: not overwriting existing file. Clean QubesIncoming dir, and retry copy%s%s", last_filename_prefix, last_filename);
|
call_error_handler("File copy: not overwriting existing file. Clean QubesIncoming dir, and retry copy%s%s", last_filename_prefix, last_filename);
|
||||||
break;
|
break;
|
||||||
case EINVAL:
|
case EINVAL:
|
||||||
call_error_handler("File copy: Corrupted data from packer%s%s", last_filename_prefix, last_filename);
|
call_error_handler("File copy: Corrupted data from packer%s%s", last_filename_prefix, last_filename);
|
||||||
break;
|
break;
|
||||||
case EDQUOT:
|
case EDQUOT:
|
||||||
if (ignore_quota_error) {
|
if (ignore_quota_error) {
|
||||||
/* skip also CRC check as sender and receiver might be
|
/* skip also CRC check as sender and receiver might be
|
||||||
* desynchronized in this case */
|
* desynchronized in this case */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* fall though */
|
/* fall though */
|
||||||
default:
|
default:
|
||||||
call_error_handler("File copy: %s%s%s",
|
call_error_handler("File copy: %s%s%s",
|
||||||
strerror(hdr.error_code), last_filename_prefix, last_filename);
|
strerror(hdr.error_code), last_filename_prefix, last_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hdr.crc32 != crc32_sum) {
|
if (hdr.crc32 != crc32_sum) {
|
||||||
call_error_handler("File transfer failed: checksum mismatch");
|
call_error_handler("File transfer failed: checksum mismatch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_headers(const struct file_header *hdr, const char *filename)
|
void write_headers(const struct file_header *hdr, const char *filename)
|
||||||
{
|
{
|
||||||
if (!write_all_with_crc(1, hdr, sizeof(*hdr))
|
if (!write_all_with_crc(1, hdr, sizeof(*hdr))
|
||||||
|| !write_all_with_crc(1, filename, hdr->namelen)) {
|
|| !write_all_with_crc(1, filename, hdr->namelen)) {
|
||||||
set_block(0);
|
set_block(0);
|
||||||
wait_for_result();
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_file_with_crc(int outfd, int infd, long long size) {
|
int copy_file_with_crc(int outfd, int infd, long long size) {
|
||||||
@ -127,100 +127,100 @@ int copy_file_with_crc(int outfd, int infd, long long size) {
|
|||||||
|
|
||||||
int single_file_processor(const char *filename, const struct stat *st)
|
int single_file_processor(const char *filename, const struct stat *st)
|
||||||
{
|
{
|
||||||
struct file_header hdr;
|
struct file_header hdr;
|
||||||
int fd;
|
int fd;
|
||||||
mode_t mode = st->st_mode;
|
mode_t mode = st->st_mode;
|
||||||
|
|
||||||
hdr.namelen = strlen(filename) + 1;
|
hdr.namelen = strlen(filename) + 1;
|
||||||
hdr.mode = mode;
|
hdr.mode = mode;
|
||||||
hdr.atime = st->st_atim.tv_sec;
|
hdr.atime = st->st_atim.tv_sec;
|
||||||
hdr.atime_nsec = st->st_atim.tv_nsec;
|
hdr.atime_nsec = st->st_atim.tv_nsec;
|
||||||
hdr.mtime = st->st_mtim.tv_sec;
|
hdr.mtime = st->st_mtim.tv_sec;
|
||||||
hdr.mtime_nsec = st->st_mtim.tv_nsec;
|
hdr.mtime_nsec = st->st_mtim.tv_nsec;
|
||||||
|
|
||||||
if (S_ISREG(mode)) {
|
if (S_ISREG(mode)) {
|
||||||
int ret;
|
int ret;
|
||||||
fd = open(filename, O_RDONLY);
|
fd = open(filename, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
call_error_handler("open %s", filename);
|
call_error_handler("open %s", filename);
|
||||||
hdr.filelen = st->st_size;
|
hdr.filelen = st->st_size;
|
||||||
write_headers(&hdr, filename);
|
write_headers(&hdr, filename);
|
||||||
ret = copy_file(1, fd, hdr.filelen, &crc32_sum);
|
ret = copy_file(1, fd, hdr.filelen, &crc32_sum);
|
||||||
if (ret != COPY_FILE_OK) {
|
if (ret != COPY_FILE_OK) {
|
||||||
if (ret != COPY_FILE_WRITE_ERROR)
|
if (ret != COPY_FILE_WRITE_ERROR)
|
||||||
call_error_handler("Copying file %s: %s", filename,
|
call_error_handler("Copying file %s: %s", filename,
|
||||||
copy_file_status_to_str(ret));
|
copy_file_status_to_str(ret));
|
||||||
else {
|
else {
|
||||||
set_block(0);
|
set_block(0);
|
||||||
wait_for_result();
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
if (S_ISDIR(mode)) {
|
if (S_ISDIR(mode)) {
|
||||||
hdr.filelen = 0;
|
hdr.filelen = 0;
|
||||||
write_headers(&hdr, filename);
|
write_headers(&hdr, filename);
|
||||||
}
|
}
|
||||||
if (S_ISLNK(mode)) {
|
if (S_ISLNK(mode)) {
|
||||||
char name[st->st_size + 1];
|
char name[st->st_size + 1];
|
||||||
if (readlink(filename, name, sizeof(name)) != st->st_size)
|
if (readlink(filename, name, sizeof(name)) != st->st_size)
|
||||||
call_error_handler("readlink %s", filename);
|
call_error_handler("readlink %s", filename);
|
||||||
hdr.filelen = st->st_size;
|
hdr.filelen = st->st_size;
|
||||||
write_headers(&hdr, filename);
|
write_headers(&hdr, filename);
|
||||||
if (!write_all_with_crc(1, name, st->st_size)) {
|
if (!write_all_with_crc(1, name, st->st_size)) {
|
||||||
set_block(0);
|
set_block(0);
|
||||||
wait_for_result();
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check for possible error from qfile-unpacker
|
// check for possible error from qfile-unpacker
|
||||||
wait_for_result();
|
wait_for_result();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_fs_walk(const char *file, int ignore_symlinks)
|
int do_fs_walk(const char *file, int ignore_symlinks)
|
||||||
{
|
{
|
||||||
char *newfile;
|
char *newfile;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
|
||||||
if (lstat(file, &st))
|
if (lstat(file, &st))
|
||||||
call_error_handler("stat %s", file);
|
call_error_handler("stat %s", file);
|
||||||
if (S_ISLNK(st.st_mode) && ignore_symlinks)
|
if (S_ISLNK(st.st_mode) && ignore_symlinks)
|
||||||
return 0;
|
return 0;
|
||||||
single_file_processor(file, &st);
|
single_file_processor(file, &st);
|
||||||
if (!S_ISDIR(st.st_mode))
|
if (!S_ISDIR(st.st_mode))
|
||||||
return 0;
|
return 0;
|
||||||
dir = opendir(file);
|
dir = opendir(file);
|
||||||
if (!dir)
|
if (!dir)
|
||||||
call_error_handler("opendir %s", file);
|
call_error_handler("opendir %s", file);
|
||||||
while ((ent = readdir(dir))) {
|
while ((ent = readdir(dir))) {
|
||||||
char *fname = ent->d_name;
|
char *fname = ent->d_name;
|
||||||
if (!strcmp(fname, ".") || !strcmp(fname, ".."))
|
if (!strcmp(fname, ".") || !strcmp(fname, ".."))
|
||||||
continue;
|
continue;
|
||||||
if (asprintf(&newfile, "%s/%s", file, fname) >= 0) {
|
if (asprintf(&newfile, "%s/%s", file, fname) >= 0) {
|
||||||
do_fs_walk(newfile, ignore_symlinks);
|
do_fs_walk(newfile, ignore_symlinks);
|
||||||
free(newfile);
|
free(newfile);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "asprintf failed\n");
|
fprintf(stderr, "asprintf failed\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
// directory metadata is resent; this makes the code simple,
|
// directory metadata is resent; this makes the code simple,
|
||||||
// and the atime/mtime is set correctly at the second time
|
// and the atime/mtime is set correctly at the second time
|
||||||
single_file_processor(file, &st);
|
single_file_processor(file, &st);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qfile_pack_init(void) {
|
void qfile_pack_init(void) {
|
||||||
crc32_sum = 0;
|
crc32_sum = 0;
|
||||||
ignore_quota_error = 0;
|
ignore_quota_error = 0;
|
||||||
// this will allow checking for possible feedback packet in the middle of transfer
|
// this will allow checking for possible feedback packet in the middle of transfer
|
||||||
set_nonblock(0);
|
set_nonblock(0);
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
error_handler = NULL;
|
error_handler = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,51 +28,51 @@
|
|||||||
|
|
||||||
int wait_for_vchan_or_argfd_once(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
|
int wait_for_vchan_or_argfd_once(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
|
||||||
{
|
{
|
||||||
int vfd, ret;
|
int vfd, ret;
|
||||||
struct timespec tv = { 1, 100000000 };
|
struct timespec tv = { 1, 100000000 };
|
||||||
sigset_t empty_set;
|
sigset_t empty_set;
|
||||||
|
|
||||||
sigemptyset(&empty_set);
|
sigemptyset(&empty_set);
|
||||||
|
|
||||||
vfd = libvchan_fd_for_select(ctrl);
|
vfd = libvchan_fd_for_select(ctrl);
|
||||||
FD_SET(vfd, rdset);
|
FD_SET(vfd, rdset);
|
||||||
if (vfd > max)
|
if (vfd > max)
|
||||||
max = vfd;
|
max = vfd;
|
||||||
max++;
|
max++;
|
||||||
ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set);
|
ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno != EINTR) {
|
if (errno != EINTR) {
|
||||||
perror("select");
|
perror("select");
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
FD_ZERO(rdset);
|
FD_ZERO(rdset);
|
||||||
FD_ZERO(wrset);
|
FD_ZERO(wrset);
|
||||||
fprintf(stderr, "eintr\n");
|
fprintf(stderr, "eintr\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!libvchan_is_open(ctrl)) {
|
if (!libvchan_is_open(ctrl)) {
|
||||||
fprintf(stderr, "libvchan_is_eof\n");
|
fprintf(stderr, "libvchan_is_eof\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
if (FD_ISSET(vfd, rdset))
|
if (FD_ISSET(vfd, rdset))
|
||||||
// the following will never block; we need to do this to
|
// the following will never block; we need to do this to
|
||||||
// clear libvchan_fd pending state
|
// clear libvchan_fd pending state
|
||||||
libvchan_wait(ctrl);
|
libvchan_wait(ctrl);
|
||||||
if (libvchan_data_ready(ctrl))
|
if (libvchan_data_ready(ctrl))
|
||||||
return 1;
|
return 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_for_vchan_or_argfd(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
|
void wait_for_vchan_or_argfd(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
|
||||||
{
|
{
|
||||||
fd_set r = *rdset, w = *wrset;
|
fd_set r = *rdset, w = *wrset;
|
||||||
do {
|
do {
|
||||||
*rdset = r;
|
*rdset = r;
|
||||||
*wrset = w;
|
*wrset = w;
|
||||||
}
|
}
|
||||||
while (wait_for_vchan_or_argfd_once(ctrl, max, rdset, wrset) == 0);
|
while (wait_for_vchan_or_argfd_once(ctrl, max, rdset, wrset) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size) {
|
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size) {
|
||||||
|
@ -29,45 +29,45 @@
|
|||||||
|
|
||||||
int get_server_socket(const char *socket_address)
|
int get_server_socket(const char *socket_address)
|
||||||
{
|
{
|
||||||
struct sockaddr_un sockname;
|
struct sockaddr_un sockname;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
unlink(socket_address);
|
unlink(socket_address);
|
||||||
|
|
||||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
printf("socket() failed\n");
|
printf("socket() failed\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
memset(&sockname, 0, sizeof(sockname));
|
memset(&sockname, 0, sizeof(sockname));
|
||||||
sockname.sun_family = AF_UNIX;
|
sockname.sun_family = AF_UNIX;
|
||||||
strncpy(sockname.sun_path, socket_address, sizeof sockname.sun_path);
|
strncpy(sockname.sun_path, socket_address, sizeof sockname.sun_path);
|
||||||
sockname.sun_path[sizeof sockname.sun_path - 1] = 0;
|
sockname.sun_path[sizeof sockname.sun_path - 1] = 0;
|
||||||
|
|
||||||
if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) {
|
if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) {
|
||||||
printf("bind() failed\n");
|
printf("bind() failed\n");
|
||||||
close(s);
|
close(s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
// chmod(sockname.sun_path, 0666);
|
// chmod(sockname.sun_path, 0666);
|
||||||
if (listen(s, 5) == -1) {
|
if (listen(s, 5) == -1) {
|
||||||
perror("listen() failed\n");
|
perror("listen() failed\n");
|
||||||
close(s);
|
close(s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_accept(int s)
|
int do_accept(int s)
|
||||||
{
|
{
|
||||||
struct sockaddr_un peer;
|
struct sockaddr_un peer;
|
||||||
unsigned int addrlen;
|
unsigned int addrlen;
|
||||||
int fd;
|
int fd;
|
||||||
addrlen = sizeof(peer);
|
addrlen = sizeof(peer);
|
||||||
fd = accept(s, (struct sockaddr *) &peer, &addrlen);
|
fd = accept(s, (struct sockaddr *) &peer, &addrlen);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
perror("unix accept");
|
perror("unix accept");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
@ -39,206 +39,206 @@ void send_status_and_crc(int code, const char *last_filename);
|
|||||||
|
|
||||||
void do_exit(int code, const char *last_filename)
|
void do_exit(int code, const char *last_filename)
|
||||||
{
|
{
|
||||||
close(0);
|
close(0);
|
||||||
send_status_and_crc(code, last_filename);
|
send_status_and_crc(code, last_filename);
|
||||||
exit(code);
|
exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_size_limit(unsigned long long new_bytes_limit, unsigned long long new_files_limit)
|
void set_size_limit(unsigned long long new_bytes_limit, unsigned long long new_files_limit)
|
||||||
{
|
{
|
||||||
bytes_limit = new_bytes_limit;
|
bytes_limit = new_bytes_limit;
|
||||||
files_limit = new_files_limit;
|
files_limit = new_files_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_verbose(int value)
|
void set_verbose(int value)
|
||||||
{
|
{
|
||||||
verbose = value;
|
verbose = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_procfs_fd(int value)
|
void set_procfs_fd(int value)
|
||||||
{
|
{
|
||||||
procdir_fd = value;
|
procdir_fd = value;
|
||||||
use_tmpfile = 1;
|
use_tmpfile = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long crc32_sum = 0;
|
unsigned long crc32_sum = 0;
|
||||||
int read_all_with_crc(int fd, void *buf, int size) {
|
int read_all_with_crc(int fd, void *buf, int size) {
|
||||||
int ret;
|
int ret;
|
||||||
ret = read_all(fd, buf, size);
|
ret = read_all(fd, buf, size);
|
||||||
if (ret)
|
if (ret)
|
||||||
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
|
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_status_and_crc(int code, const char *last_filename) {
|
void send_status_and_crc(int code, const char *last_filename) {
|
||||||
struct result_header hdr;
|
struct result_header hdr;
|
||||||
struct result_header_ext hdr_ext;
|
struct result_header_ext hdr_ext;
|
||||||
int saved_errno;
|
int saved_errno;
|
||||||
|
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
hdr.error_code = code;
|
hdr.error_code = code;
|
||||||
hdr.crc32 = crc32_sum;
|
hdr.crc32 = crc32_sum;
|
||||||
if (!write_all(1, &hdr, sizeof(hdr)))
|
if (!write_all(1, &hdr, sizeof(hdr)))
|
||||||
perror("write status");
|
perror("write status");
|
||||||
if (last_filename) {
|
if (last_filename) {
|
||||||
hdr_ext.last_namelen = strlen(last_filename);
|
hdr_ext.last_namelen = strlen(last_filename);
|
||||||
if (!write_all(1, &hdr_ext, sizeof(hdr_ext)))
|
if (!write_all(1, &hdr_ext, sizeof(hdr_ext)))
|
||||||
perror("write status ext");
|
perror("write status ext");
|
||||||
if (!write_all(1, last_filename, hdr_ext.last_namelen))
|
if (!write_all(1, last_filename, hdr_ext.last_namelen))
|
||||||
perror("write last_filename");
|
perror("write last_filename");
|
||||||
}
|
}
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_times_and_perms(struct file_header *untrusted_hdr,
|
void fix_times_and_perms(struct file_header *untrusted_hdr,
|
||||||
const char *untrusted_name)
|
const char *untrusted_name)
|
||||||
{
|
{
|
||||||
struct timeval times[2] =
|
struct timeval times[2] =
|
||||||
{ {untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000},
|
{
|
||||||
{untrusted_hdr->mtime,
|
{untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000},
|
||||||
untrusted_hdr->mtime_nsec / 1000}
|
{untrusted_hdr->mtime, untrusted_hdr->mtime_nsec / 1000}
|
||||||
};
|
};
|
||||||
if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */
|
if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
if (utimes(untrusted_name, times)) /* as above */
|
if (utimes(untrusted_name, times)) /* as above */
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void process_one_file_reg(struct file_header *untrusted_hdr,
|
void process_one_file_reg(struct file_header *untrusted_hdr,
|
||||||
const char *untrusted_name)
|
const char *untrusted_name)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int fdout = -1;
|
int fdout = -1;
|
||||||
|
|
||||||
/* make the file inaccessible until fully written */
|
/* make the file inaccessible until fully written */
|
||||||
if (use_tmpfile) {
|
if (use_tmpfile) {
|
||||||
fdout = open(".", O_WRONLY | O_TMPFILE, 0700);
|
fdout = open(".", O_WRONLY | O_TMPFILE, 0700);
|
||||||
if (fdout < 0) {
|
if (fdout < 0) {
|
||||||
if (errno==ENOENT || /* most likely, kernel too old for O_TMPFILE */
|
if (errno==ENOENT || /* most likely, kernel too old for O_TMPFILE */
|
||||||
errno==EOPNOTSUPP) /* filesystem has no support for O_TMPFILE */
|
errno==EOPNOTSUPP) /* filesystem has no support for O_TMPFILE */
|
||||||
use_tmpfile = 0;
|
use_tmpfile = 0;
|
||||||
else
|
else
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fdout < 0)
|
if (fdout < 0)
|
||||||
fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0000); /* safe because of chroot */
|
fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0000); /* safe because of chroot */
|
||||||
if (fdout < 0)
|
if (fdout < 0)
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
/* sizes are signed elsewhere */
|
/* sizes are signed elsewhere */
|
||||||
if (untrusted_hdr->filelen > LLONG_MAX || (bytes_limit && untrusted_hdr->filelen > bytes_limit))
|
if (untrusted_hdr->filelen > LLONG_MAX || (bytes_limit && untrusted_hdr->filelen > bytes_limit))
|
||||||
do_exit(EDQUOT, untrusted_name);
|
do_exit(EDQUOT, untrusted_name);
|
||||||
if (bytes_limit && total_bytes > bytes_limit - untrusted_hdr->filelen)
|
if (bytes_limit && total_bytes > bytes_limit - untrusted_hdr->filelen)
|
||||||
do_exit(EDQUOT, untrusted_name);
|
do_exit(EDQUOT, untrusted_name);
|
||||||
total_bytes += untrusted_hdr->filelen;
|
total_bytes += untrusted_hdr->filelen;
|
||||||
ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum);
|
ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum);
|
||||||
if (ret != COPY_FILE_OK) {
|
if (ret != COPY_FILE_OK) {
|
||||||
if (ret == COPY_FILE_READ_EOF
|
if (ret == COPY_FILE_READ_EOF
|
||||||
|| ret == COPY_FILE_READ_ERROR)
|
|| ret == COPY_FILE_READ_ERROR)
|
||||||
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote will produce error message
|
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote will produce error message
|
||||||
else
|
else
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
}
|
}
|
||||||
if (use_tmpfile) {
|
if (use_tmpfile) {
|
||||||
char fd_str[7];
|
char fd_str[7];
|
||||||
snprintf(fd_str, sizeof(fd_str), "%d", fdout);
|
snprintf(fd_str, sizeof(fd_str), "%d", fdout);
|
||||||
if (linkat(procdir_fd, fd_str, AT_FDCWD, untrusted_name, AT_SYMLINK_FOLLOW) < 0)
|
if (linkat(procdir_fd, fd_str, AT_FDCWD, untrusted_name, AT_SYMLINK_FOLLOW) < 0)
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
}
|
}
|
||||||
close(fdout);
|
close(fdout);
|
||||||
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void process_one_file_dir(struct file_header *untrusted_hdr,
|
void process_one_file_dir(struct file_header *untrusted_hdr,
|
||||||
const char *untrusted_name)
|
const char *untrusted_name)
|
||||||
{
|
{
|
||||||
// fix perms only when the directory is sent for the second time
|
// fix perms only when the directory is sent for the second time
|
||||||
// it allows to transfer r.x directory contents, as we create it rwx initially
|
// it allows to transfer r.x directory contents, as we create it rwx initially
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */
|
if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */
|
||||||
return;
|
return;
|
||||||
if (errno != EEXIST)
|
if (errno != EEXIST)
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
if (stat(untrusted_name,&buf) < 0)
|
if (stat(untrusted_name,&buf) < 0)
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
total_bytes += buf.st_size;
|
total_bytes += buf.st_size;
|
||||||
/* size accumulated after the fact, so don't check limit here */
|
/* size accumulated after the fact, so don't check limit here */
|
||||||
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
fix_times_and_perms(untrusted_hdr, untrusted_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_one_file_link(struct file_header *untrusted_hdr,
|
void process_one_file_link(struct file_header *untrusted_hdr,
|
||||||
const char *untrusted_name)
|
const char *untrusted_name)
|
||||||
{
|
{
|
||||||
char untrusted_content[MAX_PATH_LENGTH];
|
char untrusted_content[MAX_PATH_LENGTH];
|
||||||
unsigned int filelen;
|
unsigned int filelen;
|
||||||
if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1)
|
if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1)
|
||||||
do_exit(ENAMETOOLONG, untrusted_name);
|
do_exit(ENAMETOOLONG, untrusted_name);
|
||||||
filelen = untrusted_hdr->filelen; /* sanitized above */
|
filelen = untrusted_hdr->filelen; /* sanitized above */
|
||||||
total_bytes += filelen;
|
total_bytes += filelen;
|
||||||
if (bytes_limit && total_bytes > bytes_limit)
|
if (bytes_limit && total_bytes > bytes_limit)
|
||||||
do_exit(EDQUOT, untrusted_name);
|
do_exit(EDQUOT, untrusted_name);
|
||||||
if (!read_all_with_crc(0, untrusted_content, filelen))
|
if (!read_all_with_crc(0, untrusted_content, filelen))
|
||||||
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote has produced error message
|
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote has produced error message
|
||||||
untrusted_content[filelen] = 0;
|
untrusted_content[filelen] = 0;
|
||||||
if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */
|
if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */
|
||||||
do_exit(errno, untrusted_name);
|
do_exit(errno, untrusted_name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_one_file(struct file_header *untrusted_hdr)
|
void process_one_file(struct file_header *untrusted_hdr)
|
||||||
{
|
{
|
||||||
unsigned int namelen;
|
unsigned int namelen;
|
||||||
if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1)
|
if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1)
|
||||||
do_exit(ENAMETOOLONG, NULL); /* filename too long so not received at all */
|
do_exit(ENAMETOOLONG, NULL); /* filename too long so not received at all */
|
||||||
namelen = untrusted_hdr->namelen; /* sanitized above */
|
namelen = untrusted_hdr->namelen; /* sanitized above */
|
||||||
if (!read_all_with_crc(0, untrusted_namebuf, namelen))
|
if (!read_all_with_crc(0, untrusted_namebuf, namelen))
|
||||||
do_exit(LEGAL_EOF, NULL); // hopefully remote has produced error message
|
do_exit(LEGAL_EOF, NULL); // hopefully remote has produced error message
|
||||||
untrusted_namebuf[namelen] = 0;
|
untrusted_namebuf[namelen] = 0;
|
||||||
if (S_ISREG(untrusted_hdr->mode))
|
if (S_ISREG(untrusted_hdr->mode))
|
||||||
process_one_file_reg(untrusted_hdr, untrusted_namebuf);
|
process_one_file_reg(untrusted_hdr, untrusted_namebuf);
|
||||||
else if (S_ISLNK(untrusted_hdr->mode))
|
else if (S_ISLNK(untrusted_hdr->mode))
|
||||||
process_one_file_link(untrusted_hdr, untrusted_namebuf);
|
process_one_file_link(untrusted_hdr, untrusted_namebuf);
|
||||||
else if (S_ISDIR(untrusted_hdr->mode))
|
else if (S_ISDIR(untrusted_hdr->mode))
|
||||||
process_one_file_dir(untrusted_hdr, untrusted_namebuf);
|
process_one_file_dir(untrusted_hdr, untrusted_namebuf);
|
||||||
else
|
else
|
||||||
do_exit(EINVAL, untrusted_namebuf);
|
do_exit(EINVAL, untrusted_namebuf);
|
||||||
if (verbose && !S_ISDIR(untrusted_hdr->mode))
|
if (verbose && !S_ISDIR(untrusted_hdr->mode))
|
||||||
fprintf(stderr, "%s\n", untrusted_namebuf);
|
fprintf(stderr, "%s\n", untrusted_namebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_unpack(void)
|
int do_unpack(void)
|
||||||
{
|
{
|
||||||
struct file_header untrusted_hdr;
|
struct file_header untrusted_hdr;
|
||||||
#ifdef HAVE_SYNCFS
|
#ifdef HAVE_SYNCFS
|
||||||
int cwd_fd;
|
int cwd_fd;
|
||||||
int saved_errno;
|
int saved_errno;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
total_bytes = total_files = 0;
|
total_bytes = total_files = 0;
|
||||||
/* initialize checksum */
|
/* initialize checksum */
|
||||||
crc32_sum = 0;
|
crc32_sum = 0;
|
||||||
while (read_all_with_crc(0, &untrusted_hdr, sizeof untrusted_hdr)) {
|
while (read_all_with_crc(0, &untrusted_hdr, sizeof untrusted_hdr)) {
|
||||||
/* check for end of transfer marker */
|
/* check for end of transfer marker */
|
||||||
if (untrusted_hdr.namelen == 0) {
|
if (untrusted_hdr.namelen == 0) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
total_files++;
|
total_files++;
|
||||||
if (files_limit && total_files > files_limit)
|
if (files_limit && total_files > files_limit)
|
||||||
do_exit(EDQUOT, untrusted_namebuf);
|
do_exit(EDQUOT, untrusted_namebuf);
|
||||||
process_one_file(&untrusted_hdr);
|
process_one_file(&untrusted_hdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SYNCFS
|
#ifdef HAVE_SYNCFS
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
cwd_fd = open(".", O_RDONLY);
|
cwd_fd = open(".", O_RDONLY);
|
||||||
if (cwd_fd >= 0 && syncfs(cwd_fd) == 0 && close(cwd_fd) == 0)
|
if (cwd_fd >= 0 && syncfs(cwd_fd) == 0 && close(cwd_fd) == 0)
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
send_status_and_crc(errno, untrusted_namebuf);
|
send_status_and_crc(errno, untrusted_namebuf);
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
@ -34,86 +34,86 @@ reports that "fd" is writable. Write as much as possible to fd.
|
|||||||
*/
|
*/
|
||||||
int flush_client_data(int fd, struct buffer *buffer)
|
int flush_client_data(int fd, struct buffer *buffer)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int len;
|
int len;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
len = buffer_len(buffer);
|
len = buffer_len(buffer);
|
||||||
if (!len) {
|
if (!len) {
|
||||||
return WRITE_STDIN_OK;
|
return WRITE_STDIN_OK;
|
||||||
}
|
}
|
||||||
if (len > MAX_DATA_CHUNK)
|
if (len > MAX_DATA_CHUNK)
|
||||||
len = MAX_DATA_CHUNK;
|
len = MAX_DATA_CHUNK;
|
||||||
ret = write(fd, buffer_data(buffer), len);
|
ret = write(fd, buffer_data(buffer), len);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
if (errno != EAGAIN) {
|
if (errno != EAGAIN) {
|
||||||
return WRITE_STDIN_ERROR;
|
return WRITE_STDIN_ERROR;
|
||||||
} else
|
} else
|
||||||
return WRITE_STDIN_BUFFERED;
|
return WRITE_STDIN_BUFFERED;
|
||||||
}
|
}
|
||||||
// we previously called buffer_remove(buffer, len)
|
// we previously called buffer_remove(buffer, len)
|
||||||
// it will be wrong if we change MAX_DATA_CHUNK to something large
|
// it will be wrong if we change MAX_DATA_CHUNK to something large
|
||||||
// as pipes writes are atomic only to PIPE_MAX limit
|
// as pipes writes are atomic only to PIPE_MAX limit
|
||||||
buffer_remove(buffer, ret);
|
buffer_remove(buffer, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write "len" bytes from "data" to "fd". If not all written, buffer the rest
|
* Write "len" bytes from "data" to "fd". If not all written, buffer the rest
|
||||||
to "buffer".
|
* to "buffer".
|
||||||
*/
|
*/
|
||||||
int write_stdin(int fd, const char *data, int len, struct buffer *buffer)
|
int write_stdin(int fd, const char *data, int len, struct buffer *buffer)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int written = 0;
|
int written = 0;
|
||||||
|
|
||||||
if (buffer_len(buffer)) {
|
if (buffer_len(buffer)) {
|
||||||
buffer_append(buffer, data, len);
|
buffer_append(buffer, data, len);
|
||||||
return WRITE_STDIN_BUFFERED;
|
return WRITE_STDIN_BUFFERED;
|
||||||
}
|
}
|
||||||
while (written < len) {
|
while (written < len) {
|
||||||
ret = write(fd, data + written, len - written);
|
ret = write(fd, data + written, len - written);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
perror("write_stdin: write returns 0 ???");
|
perror("write_stdin: write returns 0 ???");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
if (errno != EAGAIN)
|
if (errno != EAGAIN)
|
||||||
return WRITE_STDIN_ERROR;
|
return WRITE_STDIN_ERROR;
|
||||||
|
|
||||||
buffer_append(buffer, data + written,
|
buffer_append(buffer, data + written,
|
||||||
len - written);
|
len - written);
|
||||||
|
|
||||||
return WRITE_STDIN_BUFFERED;
|
return WRITE_STDIN_BUFFERED;
|
||||||
}
|
}
|
||||||
written += ret;
|
written += ret;
|
||||||
}
|
}
|
||||||
return WRITE_STDIN_OK;
|
return WRITE_STDIN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Data feed process has exited, so we need to clear all control structures for
|
* Data feed process has exited, so we need to clear all control structures for
|
||||||
the client. However, if we have buffered data for the client (which is rare btw),
|
* the client. However, if we have buffered data for the client (which is rare btw),
|
||||||
fire&forget a separate process to flush them.
|
* fire&forget a separate process to flush them.
|
||||||
*/
|
*/
|
||||||
int fork_and_flush_stdin(int fd, struct buffer *buffer)
|
int fork_and_flush_stdin(int fd, struct buffer *buffer)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (!buffer_len(buffer))
|
if (!buffer_len(buffer))
|
||||||
return 0;
|
return 0;
|
||||||
switch (fork()) {
|
switch (fork()) {
|
||||||
case -1:
|
case -1:
|
||||||
perror("fork");
|
perror("fork");
|
||||||
exit(1);
|
exit(1);
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
for (i = 0; i < MAX_FDS; i++)
|
for (i = 0; i < MAX_FDS; i++)
|
||||||
if (i != fd && i != 2)
|
if (i != fd && i != 2)
|
||||||
close(i);
|
close(i);
|
||||||
set_block(fd);
|
set_block(fd);
|
||||||
write_all(fd, buffer_data(buffer), buffer_len(buffer));
|
write_all(fd, buffer_data(buffer), buffer_len(buffer));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user