New APIs: upload-offset and download-offset
authorRichard W.M. Jones <rjones@redhat.com>
Mon, 20 Sep 2010 17:23:58 +0000 (18:23 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 21 Sep 2010 18:51:22 +0000 (19:51 +0100)
These APIs allow you to efficiently write and read parts of
files or devices.

daemon/upload.c
generator/generator_actions.ml
src/MAX_PROC_NR

index 604e705..623ad3a 100644 (file)
@@ -39,15 +39,15 @@ write_cb (void *fd_ptr, const void *buf, size_t len)
 }
 
 /* Has one FileIn parameter. */
-int
-do_upload (const char *filename)
+static int
+upload (const char *filename, int flags, int64_t offset)
 {
   int err, fd, r, is_dev;
 
   is_dev = STRPREFIX (filename, "/dev/");
 
   if (!is_dev) CHROOT_IN;
-  fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666);
+  fd = open (filename, flags, 0666);
   if (!is_dev) CHROOT_OUT;
   if (fd == -1) {
     err = errno;
@@ -57,6 +57,16 @@ do_upload (const char *filename)
     return -1;
   }
 
+  if (offset) {
+    if (lseek (fd, offset, SEEK_SET) == -1) {
+      err = errno;
+      r = cancel_receive ();
+      errno = err;
+      if (r != -2) reply_with_perror ("lseek: %s", filename);
+      return -1;
+    }
+  }
+
   r = receive_file (write_cb, &fd);
   if (r == -1) {               /* write error */
     err = errno;
@@ -85,6 +95,25 @@ do_upload (const char *filename)
   return 0;
 }
 
+/* Has one FileIn parameter. */
+int
+do_upload (const char *filename)
+{
+  return upload (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0);
+}
+
+/* Has one FileIn parameter. */
+int
+do_upload_offset (const char *filename, int64_t offset)
+{
+  if (offset < 0) {
+    reply_with_perror ("%s: offset in file is negative", filename);
+    return -1;
+  }
+
+  return upload (filename, O_WRONLY|O_CREAT|O_NOCTTY, offset);
+}
+
 /* Has one FileOut parameter. */
 int
 do_download (const char *filename)
@@ -156,3 +185,83 @@ do_download (const char *filename)
 
   return 0;
 }
+
+/* Has one FileOut parameter. */
+int
+do_download_offset (const char *filename, int64_t offset, int64_t size)
+{
+  int fd, r, is_dev;
+  char buf[GUESTFS_MAX_CHUNK_SIZE];
+
+  if (offset < 0) {
+    reply_with_perror ("%s: offset in file is negative", filename);
+    return -1;
+  }
+
+  if (size < 0) {
+    reply_with_perror ("%s: size is negative", filename);
+    return -1;
+  }
+  uint64_t usize = (uint64_t) size;
+
+  is_dev = STRPREFIX (filename, "/dev/");
+
+  if (!is_dev) CHROOT_IN;
+  fd = open (filename, O_RDONLY);
+  if (!is_dev) CHROOT_OUT;
+  if (fd == -1) {
+    reply_with_perror ("%s", filename);
+    return -1;
+  }
+
+  if (offset) {
+    if (lseek (fd, offset, SEEK_SET) == -1) {
+      reply_with_perror ("lseek: %s", filename);
+      return -1;
+    }
+  }
+
+  uint64_t total = usize, sent = 0;
+
+  /* Now we must send the reply message, before the file contents.  After
+   * this there is no opportunity in the protocol to send any error
+   * message back.  Instead we can only cancel the transfer.
+   */
+  reply (NULL, NULL);
+
+  while (usize > 0) {
+    r = read (fd, buf, usize > sizeof buf ? sizeof buf : usize);
+    if (r == -1) {
+      perror (filename);
+      send_file_end (1);        /* Cancel. */
+      close (fd);
+      return -1;
+    }
+
+    if (r == 0)
+      /* The documentation leaves this case undefined.  Currently we
+       * just read fewer bytes than requested.
+       */
+      break;
+
+    if (send_file_write (buf, r) < 0) {
+      close (fd);
+      return -1;
+    }
+
+    sent += r;
+    usize -= r;
+    notify_progress (sent, total);
+  }
+
+  if (close (fd) == -1) {
+    perror (filename);
+    send_file_end (1);         /* Cancel. */
+    return -1;
+  }
+
+  if (send_file_end (0))       /* Normal end of file. */
+    return -1;
+
+  return 0;
+}
index d01871f..5a95a6d 100644 (file)
@@ -5117,6 +5117,55 @@ removes the partition number, returning the device name
 The named partition must exist, for example as a string returned
 from C<guestfs_list_partitions>.");
 
+  ("upload_offset", (RErr, [FileIn "filename"; Dev_or_Path "remotefilename"; Int64 "offset"]), 273, [],
+   (let md5 = Digest.to_hex (Digest.file "COPYING.LIB") in
+    [InitBasicFS, Always, TestOutput (
+       [["upload_offset"; "../COPYING.LIB"; "/COPYING.LIB"; "0"];
+        ["checksum"; "md5"; "/COPYING.LIB"]], md5)]),
+   "upload a file from the local machine with offset",
+   "\
+Upload local file C<filename> to C<remotefilename> on the
+filesystem.
+
+C<remotefilename> is overwritten starting at the byte C<offset>
+specified.  The intention is to overwrite parts of existing
+files or devices, although if a non-existant file is specified
+then it is created with a \"hole\" before C<offset>.  The
+size of the data written is implicit in the size of the
+source C<filename>.
+
+Note that there is no limit on the amount of data that
+can be uploaded with this call, unlike with C<guestfs_pwrite>,
+and this call always writes the full amount unless an
+error occurs.
+
+See also C<guestfs_upload>, C<guestfs_pwrite>.");
+
+  ("download_offset", (RErr, [Dev_or_Path "remotefilename"; FileOut "filename"; Int64 "offset"; Int64 "size"]), 274, [Progress],
+   (let md5 = Digest.to_hex (Digest.file "COPYING.LIB") in
+    let offset = string_of_int 100 in
+    let size = string_of_int ((Unix.stat "COPYING.LIB").Unix.st_size - 100) in
+    [InitBasicFS, Always, TestOutput (
+       (* Pick a file from cwd which isn't likely to change. *)
+       [["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
+        ["download_offset"; "/COPYING.LIB"; "testdownload.tmp"; offset; size];
+        ["upload_offset"; "testdownload.tmp"; "/COPYING.LIB"; offset];
+        ["checksum"; "md5"; "/COPYING.LIB"]], md5)]),
+   "download a file to the local machine with offset and size",
+   "\
+Download file C<remotefilename> and save it as C<filename>
+on the local machine.
+
+C<remotefilename> is read for C<size> bytes starting at C<offset>
+(this region must be within the file or device).
+
+Note that there is no limit on the amount of data that
+can be downloaded with this call, unlike with C<guestfs_pread>,
+and this call always reads the full amount unless an
+error occurs.
+
+See also C<guestfs_download>, C<guestfs_pread>.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
index 31e9cf9..d4d5a4b 100644 (file)
@@ -1 +1 @@
-272
+274