[PATCH 5/8] save: let iohelper handle inherited fd

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Rather than making the iohelper subject to a race in reopening
the file, it is nicer to pass an already-open fd by inheritance.

The old synopsis form must continue to work - if someone updates
their libvirt package and installs a new libvirt_iohelper but
without restarting the old libvirtd daemon, then the daemon can
still make calls using the old syntax but the new iohelper.

* src/util/iohelper.c (runIO): Split code for open...
(prepare): ...to new function.
(usage): Update synopsis.
(main): Allow alternate calling form.
* src/fdstream.c (virFDStreamOpenFileInternal): Use alternate form.
---
 src/fdstream.c      |   32 ++++--------
 src/util/iohelper.c |  142 +++++++++++++++++++++++++++++++++++---------------
 2 files changed, 110 insertions(+), 64 deletions(-)

diff --git a/src/fdstream.c b/src/fdstream.c
index dd742e1..754431b 100644
--- a/src/fdstream.c
+++ b/src/fdstream.c
@@ -535,6 +535,14 @@ virFDStreamOpenFileInternal(virStreamPtr st,
         goto error;
     }

+    if (offset &&
+        lseek(fd, offset, SEEK_SET) != offset) {
+        virReportSystemError(errno,
+                             _("Unable to seek %s to %llu"),
+                             path, offset);
+        goto error;
+    }
+
     /* Thanks to the POSIX i/o model, we can't reliably get
      * non-blocking I/O on block devs/regular files. To
      * support those we need to fork a helper process todo
@@ -545,14 +553,13 @@ virFDStreamOpenFileInternal(virStreamPtr st,
          !S_ISFIFO(sb.st_mode))) {
         int childfd;

-        if ((oflags & O_RDWR) == O_RDWR) {
+        if ((oflags & O_ACCMODE) == O_RDWR) {
             streamsReportError(VIR_ERR_INTERNAL_ERROR,
                                _("%s: Cannot request read and write flags together"),
                                path);
             goto error;
         }

-        VIR_FORCE_CLOSE(fd);
         if (pipe(fds) < 0) {
             virReportSystemError(errno, "%s",
                                  _("Unable to create pipe"));
@@ -562,18 +569,9 @@ virFDStreamOpenFileInternal(virStreamPtr st,
         cmd = virCommandNewArgList(LIBEXECDIR "/libvirt_iohelper",
                                    path,
                                    NULL);
-        virCommandAddArgFormat(cmd, "%d", oflags);
-        virCommandAddArgFormat(cmd, "%d", mode);
-        virCommandAddArgFormat(cmd, "%llu", offset);
         virCommandAddArgFormat(cmd, "%llu", length);
-        virCommandAddArgFormat(cmd, "%u", delete);
-
-        /* when running iohelper we don't want to delete file now,
-         * because a race condition may occur in which we delete it
-         * before iohelper even opens it. We want iohelper to remove
-         * the file instead.
-         */
-        delete = false;
+        virCommandTransferFD(cmd, fd);
+        virCommandAddArgFormat(cmd, "%d", fd);

         if (oflags == O_RDONLY) {
             childfd = fds[1];
@@ -590,14 +588,6 @@ virFDStreamOpenFileInternal(virStreamPtr st,
             goto error;

         VIR_FORCE_CLOSE(childfd);
-    } else {
-        if (offset &&
-            lseek(fd, offset, SEEK_SET) != offset) {
-                virReportSystemError(errno,
-                                     _("Unable to seek %s to %llu"),
-                                     path, offset);
-                goto error;
-        }
     }

     if (virFDStreamOpenInternal(st, fd, cmd, errfd, length) < 0)
diff --git a/src/util/iohelper.c b/src/util/iohelper.c
index 0368eba..1185bde 100644
--- a/src/util/iohelper.c
+++ b/src/util/iohelper.c
@@ -42,19 +42,11 @@

 #define VIR_FROM_THIS VIR_FROM_STORAGE

-static int runIO(const char *path,
-                 int oflags,
-                 int mode,
-                 unsigned long long offset,
-                 unsigned long long length)
+static int
+prepare(const char *path, int oflags, int mode,
+        unsigned long long offset)
 {
-    char *buf = NULL;
-    size_t buflen = 1024*1024;
-    int fd;
-    int ret = -1;
-    int fdin, fdout;
-    const char *fdinname, *fdoutname;
-    unsigned long long total = 0;
+    int fd = -1;

     if (oflags & O_CREAT) {
         fd = open(path, oflags, mode);
@@ -70,10 +62,25 @@ static int runIO(const char *path,
         if (lseek(fd, offset, SEEK_SET) < 0) {
             virReportSystemError(errno, _("Unable to seek %s to %llu"),
                                  path, offset);
+            VIR_FORCE_CLOSE(fd);
             goto cleanup;
         }
     }

+cleanup:
+    return fd;
+}
+
+static int
+runIO(const char *path, int fd, int oflags, unsigned long long length)
+{
+    char *buf = NULL;
+    size_t buflen = 1024*1024;
+    int ret = -1;
+    int fdin, fdout;
+    const char *fdinname, *fdoutname;
+    unsigned long long total = 0;
+
     if (VIR_ALLOC_N(buf, buflen) < 0) {
         virReportOOMError();
         goto cleanup;
@@ -138,61 +145,109 @@ cleanup:
     return ret;
 }

-int main(int argc, char **argv)
+static const char *program_name;
+
+ATTRIBUTE_NORETURN static void
+usage(int status)
+{
+    if (status) {
+        fprintf(stderr, _("%s: try --help for more details"), program_name);
+    } else {
+        printf(_("Usage: %s FILENAME OFLAGS MODE OFFSET LENGTH DELETE\n"
+                 "   or: %s FILENAME LENGTH FD\n"),
+               program_name, program_name);
+    }
+    exit(status);
+}
+
+int
+main(int argc, char **argv)
 {
     const char *path;
     virErrorPtr err;
     unsigned long long offset;
     unsigned long long length;
-    int oflags;
+    int oflags = -1;
     int mode;
-    unsigned int delete;
+    unsigned int delete = 0;
+    int fd = -1;
+    int lengthIndex = 0;
+
+    program_name = argv[0];

     if (setlocale(LC_ALL, "") == NULL ||
         bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
         textdomain(PACKAGE) == NULL) {
-        fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
+        fprintf(stderr, _("%s: initialization failed\n"), program_name);
         exit(EXIT_FAILURE);
     }

     if (virThreadInitialize() < 0 ||
         virErrorInitialize() < 0 ||
         virRandomInitialize(time(NULL) ^ getpid())) {
-        fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
-        exit(EXIT_FAILURE);
-    }
-
-    if (argc != 7) {
-        fprintf(stderr, _("%s: syntax FILENAME FLAGS MODE OFFSET LENGTH DELETE\n"), argv[0]);
+        fprintf(stderr, _("%s: initialization failed\n"), program_name);
         exit(EXIT_FAILURE);
     }

     path = argv[1];

-    if (virStrToLong_i(argv[2], NULL, 10, &oflags) < 0) {
-        fprintf(stderr, _("%s: malformed file flags %s"), argv[0], argv[2]);
-        exit(EXIT_FAILURE);
-    }
-
-    if (virStrToLong_i(argv[3], NULL, 10, &mode) < 0) {
-        fprintf(stderr, _("%s: malformed file mode %s"), argv[0], argv[3]);
-        exit(EXIT_FAILURE);
+    if (argc > 1 && STREQ(argv[1], "--help"))
+        usage(EXIT_SUCCESS);
+    if (argc == 7) { /* FILENAME OFLAGS MODE OFFSET LENGTH DELETE */
+        lengthIndex = 5;
+        if (virStrToLong_i(argv[2], NULL, 10, &oflags) < 0) {
+            fprintf(stderr, _("%s: malformed file flags %s"),
+                    program_name, argv[2]);
+            exit(EXIT_FAILURE);
+        }
+        if (virStrToLong_i(argv[3], NULL, 10, &mode) < 0) {
+            fprintf(stderr, _("%s: malformed file mode %s"),
+                    program_name, argv[3]);
+            exit(EXIT_FAILURE);
+        }
+        if (virStrToLong_ull(argv[4], NULL, 10, &offset) < 0) {
+            fprintf(stderr, _("%s: malformed file offset %s"),
+                    program_name, argv[4]);
+            exit(EXIT_FAILURE);
+        }
+        if (argc == 7 && virStrToLong_ui(argv[6], NULL, 10, &delete) < 0) {
+            fprintf(stderr, _("%s: malformed delete flag %s"),
+                    program_name, argv[6]);
+            exit(EXIT_FAILURE);
+        }
+        fd = prepare(path, oflags, mode, offset);
+    } else if (argc == 4) { /* FILENAME LENGTH FD */
+        lengthIndex = 2;
+        if (virStrToLong_i(argv[3], NULL, 10, &fd) < 0) {
+            fprintf(stderr, _("%s: malformed fd %s"),
+                    program_name, argv[3]);
+            exit(EXIT_FAILURE);
+        }
+#ifdef F_GETFL
+        oflags = fcntl(fd, F_GETFL);
+#else
+        /* Stupid mingw.  */
+        if (fd == STDIN_FILENO)
+            oflags = O_RDONLY;
+        else if (fd == STDOUT_FILENO)
+            oflags = O_WRONLY;
+#endif
+        if (oflags < 0) {
+            fprintf(stderr, _("%s: unable to determine access mode of fd %d"),
+                    program_name, fd);
+            exit(EXIT_FAILURE);
+        }
+    } else { /* unknown argc pattern */
+        usage(EXIT_FAILURE);
     }

-    if (virStrToLong_ull(argv[4], NULL, 10, &offset) < 0) {
-        fprintf(stderr, _("%s: malformed file offset %s"), argv[0], argv[4]);
-        exit(EXIT_FAILURE);
-    }
-    if (virStrToLong_ull(argv[5], NULL, 10, &length) < 0) {
-        fprintf(stderr, _("%s: malformed file length %s"), argv[0], argv[5]);
-        exit(EXIT_FAILURE);
-    }
-    if (virStrToLong_ui(argv[6], NULL, 10, &delete) < 0) {
-        fprintf(stderr, _("%s: malformed delete flag %s"), argv[0],argv[6]);
+    if (virStrToLong_ull(argv[lengthIndex], NULL, 10, &length) < 0) {
+        fprintf(stderr, _("%s: malformed file length %s"),
+                program_name, argv[lengthIndex]);
         exit(EXIT_FAILURE);
     }

-    if (runIO(path, oflags, mode, offset, length) < 0)
+    if (fd < 0 || runIO(path, fd, oflags, length) < 0)
         goto error;

     if (delete)
@@ -203,9 +258,10 @@ int main(int argc, char **argv)
 error:
     err = virGetLastError();
     if (err) {
-        fprintf(stderr, "%s: %s\n", argv[0], err->message);
+        fprintf(stderr, "%s: %s\n", program_name, err->message);
     } else {
-        fprintf(stderr, _("%s: unknown failure with %s\n"), argv[0], path);
+        fprintf(stderr, _("%s: unknown failure with %s\n"),
+                program_name, path);
     }
     exit(EXIT_FAILURE);
 }
-- 
1.7.4.4

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]