diff --git a/qubes_rpc/ioall.c b/qubes_rpc/ioall.c index 239f333..2a81df4 100644 --- a/qubes_rpc/ioall.c +++ b/qubes_rpc/ioall.c @@ -32,6 +32,17 @@ void perror_wrapper(char * msg) errno=prev; } +void set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} + +void set_block(int fd) +{ + int fl = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, fl & ~O_NONBLOCK); +} int write_all(int fd, void *buf, int size) { @@ -42,7 +53,6 @@ int write_all(int fd, void *buf, int size) if (ret == -1 && errno == EINTR) continue; if (ret <= 0) { - perror_wrapper("write"); return 0; } written += ret; @@ -65,9 +75,14 @@ int read_all(int fd, void *buf, int size) return 0; } if (ret < 0) { - perror_wrapper("read"); + if (errno != EAGAIN) + perror_wrapper("read"); return 0; } + if (got_read == 0) { + // force blocking operation on further reads + set_block(fd); + } got_read += ret; } // fprintf(stderr, "read %d bytes\n", size); diff --git a/qubes_rpc/ioall.h b/qubes_rpc/ioall.h index 1a700c6..c9913e8 100644 --- a/qubes_rpc/ioall.h +++ b/qubes_rpc/ioall.h @@ -1,3 +1,5 @@ int write_all(int fd, void *buf, int size); int read_all(int fd, void *buf, int size); int copy_fd_all(int fdout, int fdin); +void set_nonblock(int fd); +void set_block(int fd); diff --git a/qubes_rpc/qfile-agent.c b/qubes_rpc/qfile-agent.c index 4b37738..0a1256f 100644 --- a/qubes_rpc/qfile-agent.c +++ b/qubes_rpc/qfile-agent.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "filecopy.h" #include "crc32.h" @@ -49,6 +50,37 @@ void do_notify_progress(long long total, int flag) } } +void wait_for_result() +{ + struct result_header hdr; + + if (!read_all(0, &hdr, sizeof(hdr))) { + if (errno == EAGAIN) { + // no result sent and stdin still open + return; + } else { + // other read error or EOF + exit(1); // hopefully remote has produced error message + } + } + if (hdr.error_code != 0) { + switch (hdr.error_code) { + case EEXIST: + gui_fatal("File copy: not overwriting existing file. Clean incoming dir, and retry copy"); + break; + case EINVAL: + gui_fatal("File copy: Corrupted data from packer"); + break; + default: + gui_fatal("File copy: %s", + strerror(hdr.error_code)); + } + } + if (hdr.crc32 != crc32_sum) { + gui_fatal("File transfer failed: checksum mismatch"); + } +} + void notify_progress(int size, int flag) { static long long total = 0; @@ -56,6 +88,11 @@ void notify_progress(int size, int flag) total += size; if (total > prev_total + PROGRESS_NOTIFY_DELTA || (flag != PROGRESS_FLAG_NORMAL)) { + // check for possible error from qfile-unpacker; if error occured, + // exit() will be called, so don't bother with current state + // (notify_progress can be called as callback from copy_file()) + if (flag == PROGRESS_FLAG_NORMAL) + wait_for_result(); do_notify_progress(total, flag); prev_total = total; } @@ -64,8 +101,11 @@ void notify_progress(int size, int flag) void write_headers(struct file_header *hdr, char *filename) { 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); + wait_for_result(); exit(1); + } } int single_file_processor(char *filename, struct stat *st) @@ -89,13 +129,15 @@ int single_file_processor(char *filename, struct stat *st) hdr.filelen = st->st_size; write_headers(&hdr, filename); ret = copy_file(1, fd, hdr.filelen, &crc32_sum); - // if COPY_FILE_WRITE_ERROR, hopefully remote will produce a message if (ret != COPY_FILE_OK) { if (ret != COPY_FILE_WRITE_ERROR) gui_fatal("Copying file %s: %s", filename, copy_file_status_to_str(ret)); - else + else { + set_block(0); + wait_for_result(); exit(1); + } } close(fd); } @@ -109,9 +151,14 @@ int single_file_processor(char *filename, struct stat *st) gui_fatal("readlink %s", filename); hdr.filelen = st->st_size + 1; write_headers(&hdr, filename); - if (!write_all_with_crc(1, name, st->st_size + 1)) + if (!write_all_with_crc(1, name, st->st_size + 1)) { + set_block(0); + wait_for_result(); exit(1); + } } + // check for possible error from qfile-unpacker + wait_for_result(); return 0; } @@ -147,7 +194,6 @@ int do_fs_walk(char *file) void notify_end_and_wait_for_result() { - struct result_header hdr; struct file_header end_hdr; /* nofity end of transfer */ @@ -156,17 +202,8 @@ void notify_end_and_wait_for_result() end_hdr.filelen = 0; write_all_with_crc(1, &end_hdr, sizeof(end_hdr)); - /* wait for result */ - if (!read_all(0, &hdr, sizeof(hdr))) { - exit(1); // hopefully remote has produced error message - } - if (hdr.error_code != 0) { - gui_fatal("Error writing files: %s", - strerror(hdr.error_code)); - } - if (hdr.crc32 != crc32_sum) { - gui_fatal("File transfer failed: checksum mismatch"); - } + set_block(0); + wait_for_result(); } char *get_abs_path(char *cwd, char *pathname) @@ -186,6 +223,8 @@ int main(int argc, char **argv) char *sep; signal(SIGPIPE, SIG_IGN); + // this will allow checking for possible feedback packet in the middle of transfer + set_nonblock(0); notify_progress(0, PROGRESS_FLAG_INIT); crc32_sum = 0; cwd = getcwd(NULL, 0); diff --git a/qubes_rpc/qfile-unpacker.c b/qubes_rpc/qfile-unpacker.c index dd0a510..402a009 100644 --- a/qubes_rpc/qfile-unpacker.c +++ b/qubes_rpc/qfile-unpacker.c @@ -29,32 +29,14 @@ int prepare_creds_return_uid(char *username) return pwd->pw_uid; } -void wait_for_child(int statusfd) -{ - int status; - if (read(statusfd, &status, sizeof status)!=sizeof status) - gui_fatal("File copy error: Internal error reading status from unpacker"); - errno = status; - switch (status) { - case LEGAL_EOF: break; - case 0: gui_fatal("File copy: Connection terminated unexpectedly"); break; - case EINVAL: gui_fatal("File copy: Corrupted data from packer"); break; - case EEXIST: gui_fatal("File copy: not overwriting existing file. Clean ~/incoming, and retry copy"); break; - default: gui_fatal("File copy"); - } -} - -extern void do_unpack(int); +extern int do_unpack(void); int main(int argc, char ** argv) { char *incoming_dir; - int pipefds[2]; int uid; char *remote_domain; - pipe(pipefds); - uid = prepare_creds_return_uid("user"); remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); @@ -67,23 +49,8 @@ int main(int argc, char ** argv) mkdir(incoming_dir, 0700); if (chdir(incoming_dir)) gui_fatal("Error chdir to %s", incoming_dir); - switch (fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - if (chroot(incoming_dir)) //impossible - gui_fatal("Error chroot to %s", incoming_dir); - setuid(uid); - close(pipefds[0]); - do_unpack(pipefds[1]); - exit(0); - default:; - } - + if (chroot(incoming_dir)) //impossible + gui_fatal("Error chroot to %s", incoming_dir); setuid(uid); - close(pipefds[1]); - wait_for_child(pipefds[0]); - - return 0; + return do_unpack(); } diff --git a/qubes_rpc/qubes.Filecopy.policy b/qubes_rpc/qubes.Filecopy.policy index 0a0d735..e0bef3e 100644 --- a/qubes_rpc/qubes.Filecopy.policy +++ b/qubes_rpc/qubes.Filecopy.policy @@ -3,4 +3,4 @@ ## Please use a single # to start your custom comments -$anyvm $anyvm ask,user=root +$anyvm $anyvm ask diff --git a/qubes_rpc/unpack.c b/qubes_rpc/unpack.c index 580095d..9c018fa 100644 --- a/qubes_rpc/unpack.c +++ b/qubes_rpc/unpack.c @@ -35,14 +35,24 @@ int read_all_with_crc(int fd, void *buf, int size) { return ret; } -int global_status_fd; -void do_exit(int code) -{ - int codebuf = code; - write(global_status_fd, &codebuf, sizeof codebuf); - exit(0); +void send_status_and_crc(int code) { + struct result_header hdr; + int saved_errno; + + saved_errno = errno; + hdr.error_code = code; + hdr.crc32 = crc32_sum; + if (!write_all(1, &hdr, sizeof(hdr))) + perror("write status"); + errno = saved_errno; } +void do_exit(int code) +{ + close(0); + send_status_and_crc(code); + exit(code); +} void fix_times_and_perms(struct file_header *untrusted_hdr, char *untrusted_name) @@ -130,20 +140,8 @@ void process_one_file(struct file_header *untrusted_hdr) do_exit(EINVAL); } -void send_status_and_crc() { - struct result_header hdr; - int saved_errno; - - saved_errno = errno; - hdr.error_code = errno; - hdr.crc32 = crc32_sum; - write_all(1, &hdr, sizeof(hdr)); - errno = saved_errno; -} - -void do_unpack(int fd) +int do_unpack() { - global_status_fd = fd; struct file_header untrusted_hdr; /* initialize checksum */ crc32_sum = 0; @@ -158,9 +156,6 @@ void do_unpack(int fd) if (files_limit && total_files > files_limit) do_exit(EDQUOT); } - send_status_and_crc(); - if (errno) - do_exit(errno); - else - do_exit(LEGAL_EOF); + send_status_and_crc(errno); + return errno; } diff --git a/rpm_spec/core-vm.spec b/rpm_spec/core-vm.spec index dd5e26a..7b202d3 100644 --- a/rpm_spec/core-vm.spec +++ b/rpm_spec/core-vm.spec @@ -405,7 +405,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/meminfo-writer /usr/lib/qubes/network-manager-prepare-conf-dir /usr/lib/qubes/qfile-agent -/usr/lib/qubes/qfile-unpacker +%attr(4755,root,root) /usr/lib/qubes/qfile-unpacker /usr/lib/qubes/qopen-in-vm /usr/lib/qubes/qrexec_agent /usr/lib/qubes/qrexec_client_vm