diff --git a/appvm/qubes_add_pendrive_script.c b/appvm/qubes_add_pendrive_script.c index d8e4ad26..d931ba9d 100644 --- a/appvm/qubes_add_pendrive_script.c +++ b/appvm/qubes_add_pendrive_script.c @@ -143,6 +143,8 @@ void suicide(struct xs_handle *xs) exit(1); } +// we are DVM, AppVM sends us a document to work on +// /dev/xvdh contains the dvm_header and document data void dvm_transaction_request(char *seq, struct xs_handle *xs) { char filename[1024], cmdbuf[1024]; @@ -189,12 +191,16 @@ void dvm_transaction_request(char *seq, struct xs_handle *xs) if (stat_pre.st_mtime == stat_post.st_mtime) suicide(xs); xs_daemon_close(xs); + // if the modify timestamp of the document file has changed, write back the + // modified content to the requestor AppVM execl("/usr/lib/qubes/qvm-dvm-transfer", "qvm-dvm-transfer", src_vm, filename, seq, NULL); syslog(LOG_DAEMON | LOG_ERR, "execl qvm-dvm-transfer"); suicide(xs); } +// we are AppVM, DVM sends us a modified document +// /dev/xvdh contains the dvm_header and document data void dvm_transaction_return(char *seq_string, struct xs_handle *xs) { int seq = strtoul(seq_string, 0, 10); @@ -213,6 +219,7 @@ void dvm_transaction_return(char *seq_string, struct xs_handle *xs) goto out_err; } drop_to_user(); + // read the file name for which the open-in-dvm with transaction=="seq" was started snprintf(db_name, sizeof(db_name), DBDIR "/%d", seq); db_fd = open(db_name, O_RDONLY); if (!db_fd) { diff --git a/appvm/qubes_penctl.c b/appvm/qubes_penctl.c index 878652e3..284f34cb 100644 --- a/appvm/qubes_penctl.c +++ b/appvm/qubes_penctl.c @@ -39,6 +39,13 @@ void check_name(unsigned char *s) exit(1); } } +/* +A tool to request action from qfileexchgd by writing to device/qpen xenstore key. +new - please attach a vfat-formatted block device at /dev/xvdg; I will either write some files to it and + then request sending it to other AppVM, or I will place dvm_header+some file on it and send it to DVM +send vmname - detach my /dev/xvdg and attach it to vmname at /dev/xvdh +umount - I am done with my /dev/xvdh, please detach it +*/ void usage(char *argv0) { diff --git a/appvm/qvm-open-in-dvm.c b/appvm/qvm-open-in-dvm.c index acfa81ce..d72989f7 100644 --- a/appvm/qvm-open-in-dvm.c +++ b/appvm/qvm-open-in-dvm.c @@ -62,7 +62,12 @@ int get_and_set_seq() close(seq_fd); return seq; } - +/* +Write the filename we are sending to DVM to DBDIR/transaction_seq +When DVM sends us a modified document via transaction with transaction_seq, +we will know that we are supposed to update the document with the +filename at DBDIR/transaction_seq +*/ void write_db(char *name, int seq) { int db_fd; @@ -137,6 +142,7 @@ int main(int argc, char **argv) perror("xs_domain_open"); exit(1); } + // request a new block device at /dev/xvdg from qfileexchgd if (!xs_write(xs, 0, "device/qpen", "new", 3)) { perror("xs_write"); exit(1); @@ -150,8 +156,10 @@ int main(int argc, char **argv) } setuid(getuid()); if (argc == 3) + // we are AppVM; get new seq seq = get_and_set_seq(); else + // we are DVM; use the cmdline transaction_seq seq = strtoul(argv[3], 0, 0); file_fd = open(abs_filename, O_RDONLY); if (file_fd < 0) { @@ -165,6 +173,9 @@ int main(int argc, char **argv) copy_file(xvdg_fd, file_fd); close(file_fd); close(xvdg_fd); + // request qfileexchgd to send our /dev/xvdg to its destination + // either "disposable", which means "create DVM for me" + // or vmname, meaning this is a reply to originator AppVM snprintf(buf, sizeof(buf), "send %s %d", argv[1], seq); if (!xs_write(xs, 0, "device/qpen", buf, strlen(buf))) { perror("xs_write"); diff --git a/dom0/pendrive_swapper/README b/dom0/pendrive_swapper/README new file mode 100644 index 00000000..c05f863c --- /dev/null +++ b/dom0/pendrive_swapper/README @@ -0,0 +1,62 @@ + qfilexchgd is a daemon responsible for managing exchange of block +devices ("virtual pendrives") between VMs. It is used for +a) copying files between AppVMs +b) copying a single file between an AppVM and a DVM + + qfilexchgd is event driven. The sources of events are: +a) trigger of xenstore watch for the changes in /local/domain xenstore hierarchy - +to detect start/stop of VMs, and maintain vmname->vm_xid dictionary +b) triger of xenstore watch for a change in /local/domain/domid/device/qpen +key - VMs write to this key to request service from qfilexchgd + + Copying files between AppVMs is implemented as follows: +1) AppVM1 user runs qvm-copy-to-vm script (accessible from Dolphin file manager by +"right click on a file(s)->Actions->Send to VM" menu). It calls +"/usr/lib/qubes/qubes_penctl new", and it writes "new" request to its device/qpen +key. qfilexchgd creates a new 1G file, makes vfat fs on it, and does block-attach +so that this file is attached as /dev/xvdg in AppVM1. +2) AppVM1 mounts /dev/xvdg on /mnt/outgoing and copies some files there, +then unmounts it. +3) AppVM1 writes "send DestVM" request to its device/qpen key (calling +"/usr/lib/qubes/qubes_penctl send DestVM"). After getting confirmation by +displaying a dialog box in dom0 display, qfilexchgd detaches /dev/xvdg +from AppVM1, attaches it as /dev/xvdh to DestVM. +4) In DestVM, udev script for /dev/xvdh named qubes_add_pendrive_script (see +/etc/udev/rules.d/qubes.rules) mounts /dev/xvdh on /mnt/incoming, and then +waits for /mnt/incoming to become unmounted. A file manager +running in DestVM shows a new volume, and user in DestVM may copy files from +it. When user in DestVM is done, then user unmounts /dev/xvdh. +qubes_add_pendrive_script then tells qfilexchgd to detach /dev/xvdh and +terminates. + + Copying a single file between AppVM and a DVM is implemented as +follows: +1) User in AppVM1 runs qvm-open-in-dvm (accessible from Dolphin file manager +by "right click on a file->Actions->Open in DVM" menu). qvm-open-in-dvm +a) gets a new /dev/xvdg (just as described in previous paragraph) +b) computes a new unique transaction seq SEQ, +c) writes the requested file name (say, /home/user/document.txt) to +/home/user/.dvm/SEQ. +d) creates a dvm_header (see core.git/appvm/dvm.h) on /dev/xvdg, followed by +file contents +e) writes the "send disposable SEQ" to its device/qpen xenstore key. +2) qfilexchgd sees that "send" argument=="disposable", and creates the new +DVM by calling /usr/lib/qubes/qubes_restore. It adds new DVM to qubesDB via +qvm_collection.add_new_appvm. Then it attaches /dev/xvdg from AppVM1 as +/dev/xvdh in DVM. +3) In DVM, qubes_add_pendrive_script sees non-zero "qubes_transaction_seq" +key in xenstore, and instead processing it as in the case of normal copy, +treats it as a request to DVM (because we run in DVM). It retrieves the +body of the file passed in +/dev/xvdh, copies to /tmp, and runs "mime-open" to open appropriate +executable to edit it. When mime-open returns, if the file was modified, +it is sent back to AppVM1 (by writing "send AppVM1 SEQ" to device/qpen). +Then DVM destroys itself. +4) In AppVM1, a new /dev/xvdh appears (because DVM sent it). +qubes_add_pendrive_script sees non-zero "qubes_transaction_seq" key, and +treats it as response from DVM (because we run in AppVM, not DVM). It +retrieves the filename from +/home/user/.dvm/SEQ, and copies data from /dev/xvdh to it. + + qfilexchgd is started after first qubes_guid is started, so that it +has access to X display in dom0 to present dialog messages. diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c index a38aa1fe..3ba33800 100644 --- a/dom0/restore/qubes_restore.c +++ b/dom0/restore/qubes_restore.c @@ -197,7 +197,10 @@ void start_guid(int domid, int argc, char **argv) execv("/usr/bin/qubes_guid", guid_args); perror("execv"); } - +// modify the savefile. fd = fd to the open savefile, +// buf - already read 1st page of the savefile +// pattern - pattern to search for +// val - string to replace pattern with void fix_savefile(int fd, char *buf, char *pattern, char *val) { int i, len = strlen(val), origlen; @@ -237,6 +240,10 @@ char *build_dvm_ip(int netvm, int id) } #define NAME_PATTERN "/root-cow.img" +// replaces the unique portions of the savefile with per-dvm values +// returns the name of VM the savefile was taken for +// by looking for /.../vmname/root-cow.img +// normally, it should be "templatename-dvm" char *get_vmname_from_savefile(int fd) { static char buf[4096];