diff --git a/appvm/Makefile b/appvm/Makefile index 3f81fb94..c6f4d256 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,10 +1,12 @@ CC=gcc CFLAGS=-Wall -I../common -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent dvm_file_editor: dvm_file_editor.o ../common/ioall.o $(CC) -o dvm_file_editor dvm_file_editor.o ../common/ioall.o qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o - $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o + $(CC) -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o +qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o + $(CC) -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o diff --git a/appvm/copy_file.c b/appvm/copy_file.c new file mode 100644 index 00000000..5f7fc793 --- /dev/null +++ b/appvm/copy_file.c @@ -0,0 +1,28 @@ +#include +#include +extern void notify_progress(int, int); + +char * copy_file(int outfd, int infd, long long size) +{ + char buf[4096]; + long long written = 0; + int ret; + int count; + while (written < size) { + if (size - written > sizeof(buf)) + count = sizeof buf; + else + count = size - written; + ret = read(infd, buf, count); + if (!ret) + return("EOF while reading file"); + if (ret < 0) + return("error reading file"); + if (!write_all(outfd, buf, ret)) + return("error writing file content"); + notify_progress(ret, 0); + written += ret; + } + return NULL; +} + diff --git a/appvm/filecopy.h b/appvm/filecopy.h new file mode 100644 index 00000000..b4f6638c --- /dev/null +++ b/appvm/filecopy.h @@ -0,0 +1,18 @@ +#define FILECOPY_SPOOL "/home/user/.filecopyspool" +#define FILECOPY_VMNAME_SIZE 32 +#define PROGRESS_NOTIFY_DELTA (15*1000*1000) +#define MAX_PATH_LENGTH 16384 + +#define LEGAL_EOF 31415926 + +struct file_header { +unsigned int namelen; +unsigned int mode; +unsigned long long filelen; +unsigned int atime; +unsigned int atime_nsec; +unsigned int mtime; +unsigned int mtime_nsec; +}; + +char * copy_file(int outfd, int infd, long long size); diff --git a/appvm/qfile-agent.c b/appvm/qfile-agent.c new file mode 100644 index 00000000..b4ba354e --- /dev/null +++ b/appvm/qfile-agent.c @@ -0,0 +1,201 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filecopy.h" + +char *client_flags; +void do_notify_progress(long long total) +{ + FILE *progress; + if (!client_flags[0]) + return; + progress = fopen(client_flags, "w"); + if (!progress) + return; + fprintf(progress, "%d %lld", getpid(), total); + fclose(progress); +} + +void notify_progress(int size, int force) +{ + static long long total = 0; + static long long prev_total = 0; + total += size; + if (total > prev_total + PROGRESS_NOTIFY_DELTA || force) { + do_notify_progress(total); + prev_total = total; + } +} + +void write_headers(struct file_header *hdr, char *filename) +{ + if (!write_all(1, hdr, sizeof(*hdr)) + || !write_all(1, filename, hdr->namelen)) + gui_fatal("writing file headers to remove AppVM"); +} + +int single_file_processor(char *filename, struct stat *st) +{ + struct file_header hdr; + int fd; + mode_t mode = st->st_mode; + + hdr.namelen = strlen(filename) + 1; + hdr.mode = mode; + hdr.atime = st->st_atim.tv_sec; + hdr.atime_nsec = st->st_atim.tv_nsec; + hdr.mtime = st->st_mtim.tv_sec; + hdr.mtime_nsec = st->st_mtim.tv_nsec; + + if (S_ISREG(mode)) { + char * ret; + fd = open(filename, O_RDONLY); + if (!fd) + gui_fatal("open %s", filename); + hdr.filelen = st->st_size; + write_headers(&hdr, filename); + ret=copy_file(1, fd, hdr.filelen); + if (ret) + gui_fatal("Copying file %s: %s", filename, ret); + close(fd); + } + if (S_ISDIR(mode)) { + hdr.filelen = 0; + write_headers(&hdr, filename); + } + if (S_ISLNK(mode)) { + char name[st->st_size + 1]; + if (readlink(filename, name, sizeof(name)) != st->st_size) + gui_fatal("readlink %s", filename); + hdr.filelen = st->st_size + 1; + write_headers(&hdr, filename); + if (!write_all(1, name, st->st_size + 1)) + gui_fatal("write to remote VM"); + } + return 0; +} + +int do_fs_walk(char *file) +{ + char *newfile; + struct stat st; + struct dirent *ent; + DIR *dir; + + if (lstat(file, &st)) + gui_fatal("stat %s", file); + single_file_processor(file, &st); + if (!S_ISDIR(st.st_mode)) + return 0; + dir = opendir(file); + if (!dir) + gui_fatal("opendir %s", file); + while ((ent = readdir(dir))) { + char *fname = ent->d_name; + if (!strcmp(fname, ".") || !strcmp(fname, "..")) + continue; + asprintf(&newfile, "%s/%s", file, fname); + do_fs_walk(newfile); + free(newfile); + } + closedir(dir); + // directory metadata is resent; this makes the code simple, + // and the atime/mtime is set correctly at the second time + single_file_processor(file, &st); + return 0; +} + +void send_vmname(char *vmname) +{ + char buf[FILECOPY_VMNAME_SIZE]; + memset(buf, 0, sizeof(buf)); + strncat(buf, vmname, sizeof(buf) - 1); + if (!write_all(1, buf, sizeof buf)) + gui_fatal("writing vmname to remote VM"); +} + +char *get_item(char *data, char **current, int size) +{ + char *ret; + if ((unsigned long) *current >= (unsigned long) data + size) + return NULL; + ret = *current; + *current += strlen(ret) + 1; + return ret; +} + +void parse_entry(char *data, int datasize) +{ + char *current = data; + char *vmname, *entry, *sep; + vmname = get_item(data, ¤t, datasize); + client_flags = get_item(data, ¤t, datasize); + notify_progress(0, 1); + send_vmname(vmname); + while ((entry = get_item(data, ¤t, datasize))) { + sep = rindex(entry, '/'); + if (!sep) + gui_fatal("Internal error: nonabsolute filenames not allowed"); + *sep = 0; + if (entry[0] == 0) + chdir("/"); + else if (chdir(entry)) + gui_fatal("chdir to %s", entry); + do_fs_walk(sep + 1); + } + notify_progress(0, 1); +} + +void process_spoolentry(char *entry_name) +{ + char *abs_spool_entry_name; + int entry_fd; + struct stat st; + char *entry; + int entry_size; + asprintf(&abs_spool_entry_name, "%s/%s", FILECOPY_SPOOL, + entry_name); + entry_fd = open(abs_spool_entry_name, O_RDONLY); + unlink(abs_spool_entry_name); + if (entry_fd < 0 || fstat(entry_fd, &st)) + gui_fatal("bad file copy spool entry"); + entry_size = st.st_size; + entry = calloc(1, entry_size + 1); + if (!entry) + gui_fatal("malloc"); + if (!read_all(entry_fd, entry, entry_size)) + gui_fatal("read filecopy entry"); + close(entry_fd); + parse_entry(entry, entry_size); +} + +void scan_spool(char *name) +{ + struct dirent *ent; + DIR *dir = opendir(name); + if (!dir) + gui_fatal("opendir %s", name); + while ((ent = readdir(dir))) { + char *fname = ent->d_name; + if (fname[0] != '.') + process_spoolentry(fname); + break; + } + closedir(dir); +} + +int main() +{ + signal(SIGPIPE, SIG_IGN); + scan_spool(FILECOPY_SPOOL); + return 0; +} diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index b947b83b..df0b5907 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -76,7 +76,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_RO mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes -cp dvm_file_editor $RPM_BUILD_ROOT/usr/lib/qubes +cp dvm_file_editor qfile-agent $RPM_BUILD_ROOT/usr/lib/qubes ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} @@ -216,6 +216,7 @@ rm -rf $RPM_BUILD_ROOT %attr(4755,root,root) /usr/lib/qubes/qubes_penctl /usr/lib/qubes/qubes_add_pendrive_script /usr/lib/qubes/qrexec_agent +/usr/lib/qubes/qfile-agent /etc/udev/rules.d/qubes.rules /etc/sysconfig/iptables /var/lib/qubes