+struct write_cb_data {
+ int fd; /* file descriptor */
+ uint64_t written; /* bytes written so far */
+};
+
+static int
+write_cb (void *data_vp, const void *buf, size_t len)
+{
+ struct write_cb_data *data = data_vp;
+ int r;
+
+ r = xwrite (data->fd, buf, len);
+ if (r == -1)
+ return -1;
+
+ data->written += len;
+
+ if (progress_hint > 0)
+ notify_progress (data->written, progress_hint);
+
+ return 0;
+}
+
+/* Has one FileIn parameter. */
+static int
+upload (const char *filename, int flags, int64_t offset)
+{
+ struct write_cb_data data = { .written = 0 };
+ int err, r, is_dev;
+
+ is_dev = STRPREFIX (filename, "/dev/");
+
+ if (!is_dev) CHROOT_IN;
+ data.fd = open (filename, flags, 0666);
+ if (!is_dev) CHROOT_OUT;
+ if (data.fd == -1) {
+ err = errno;
+ r = cancel_receive ();
+ errno = err;
+ if (r != -2) reply_with_perror ("%s", filename);
+ return -1;
+ }
+
+ if (offset) {
+ if (lseek (data.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, &data.fd);
+ if (r == -1) { /* write error */
+ err = errno;
+ r = cancel_receive ();
+ errno = err;
+ if (r != -2) reply_with_error ("write error: %s", filename);
+ close (data.fd);
+ return -1;
+ }
+ if (r == -2) { /* cancellation from library */
+ close (data.fd);
+ /* Do NOT send any error. */
+ return -1;
+ }
+
+ if (close (data.fd) == -1) {
+ err = errno;
+ if (r == -1) /* if r == 0, file transfer ended already */
+ r = cancel_receive ();
+ errno = err;
+ if (r != -2)
+ reply_with_perror ("close: %s", filename);
+ return -1;
+ }
+
+ 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. */