7a3408
From b3ada4fb6e742813d3eca32bac7bf88e4db54d8e Mon Sep 17 00:00:00 2001
7a3408
Message-Id: <b3ada4fb6e742813d3eca32bac7bf88e4db54d8e@dist-git>
7a3408
From: John Ferlan <jferlan@redhat.com>
7a3408
Date: Wed, 2 Sep 2015 09:55:13 -0400
7a3408
Subject: [PATCH] virfile: Introduce virFileUnlink
7a3408
7a3408
https://bugzilla.redhat.com/show_bug.cgi?id=1253609
7a3408
7a3408
In an NFS root-squashed environment the 'vol-delete' command will fail to
7a3408
'unlink' the target volume since it was created under a different uid:gid.
7a3408
7a3408
This code continues the concepts introduced in virFileOpenForked and
7a3408
virDirCreate[NoFork] with respect to running the unlink command under
7a3408
the uid/gid of the child. Unlike the other two, don't retry on EACCES
7a3408
(that's why we're here doing this now).
7a3408
7a3408
(cherry picked from commit 35847860f65f92e444db9730e00cdaef45198e0c)
7a3408
Signed-off-by: John Ferlan <jferlan@redhat.com>
7a3408
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
7a3408
---
7a3408
 src/libvirt_private.syms         |   1 +
7a3408
 src/storage/storage_backend_fs.c |   3 +-
7a3408
 src/util/virfile.c               | 106 +++++++++++++++++++++++++++++++++++++++
7a3408
 src/util/virfile.h               |   1 +
7a3408
 4 files changed, 110 insertions(+), 1 deletion(-)
7a3408
7a3408
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
7a3408
index e5d8437..15fa785 100644
7a3408
--- a/src/libvirt_private.syms
7a3408
+++ b/src/libvirt_private.syms
7a3408
@@ -1452,6 +1452,7 @@ virFileSanitizePath;
7a3408
 virFileSkipRoot;
7a3408
 virFileStripSuffix;
7a3408
 virFileTouch;
7a3408
+virFileUnlink;
7a3408
 virFileUnlock;
7a3408
 virFileUpdatePerm;
7a3408
 virFileWaitForDevices;
7a3408
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
7a3408
index c0ea1df..f41a41e 100644
7a3408
--- a/src/storage/storage_backend_fs.c
7a3408
+++ b/src/storage/storage_backend_fs.c
7a3408
@@ -1203,7 +1203,8 @@ virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
7a3408
 
7a3408
     switch ((virStorageVolType) vol->type) {
7a3408
     case VIR_STORAGE_VOL_FILE:
7a3408
-        if (unlink(vol->target.path) < 0) {
7a3408
+        if (virFileUnlink(vol->target.path, vol->target.perms->uid,
7a3408
+                          vol->target.perms->gid) < 0) {
7a3408
             /* Silently ignore failures where the vol has already gone away */
7a3408
             if (errno != ENOENT) {
7a3408
                 virReportSystemError(errno,
7a3408
diff --git a/src/util/virfile.c b/src/util/virfile.c
7a3408
index e657913..7c6e72c 100644
7a3408
--- a/src/util/virfile.c
7a3408
+++ b/src/util/virfile.c
7a3408
@@ -2285,6 +2285,112 @@ virFileOpenAs(const char *path, int openflags, mode_t mode,
7a3408
     return ret;
7a3408
 }
7a3408
 
7a3408
+
7a3408
+/* virFileUnlink:
7a3408
+ * @path: file to unlink
7a3408
+ * @uid: uid that was used to create the file (not required)
7a3408
+ * @gid: gid that was used to create the file (not required)
7a3408
+ *
7a3408
+ * If a file/volume was created in an NFS root-squash environment,
7a3408
+ * then we must 'unlink' the file in the same environment. Unlike
7a3408
+ * the virFileOpenAs[Forked] and virDirCreate[NoFork], this code
7a3408
+ * takes no extra flags and does not bother with EACCES failures
7a3408
+ * from the child.
7a3408
+ */
7a3408
+int
7a3408
+virFileUnlink(const char *path,
7a3408
+              uid_t uid,
7a3408
+              gid_t gid)
7a3408
+{
7a3408
+    pid_t pid;
7a3408
+    int waitret;
7a3408
+    int status, ret = 0;
7a3408
+    gid_t *groups;
7a3408
+    int ngroups;
7a3408
+
7a3408
+    /* If not running as root or if a non explicit uid/gid was being used for
7a3408
+     * the file/volume, then use unlink directly
7a3408
+     */
7a3408
+    if ((geteuid() != 0) ||
7a3408
+        ((uid == (uid_t) -1) && (gid == (gid_t) -1)))
7a3408
+        return unlink(path);
7a3408
+
7a3408
+    /* Otherwise, we have to deal with the NFS root-squash craziness
7a3408
+     * to run under the uid/gid that created the volume in order to
7a3408
+     * perform the unlink of the volume.
7a3408
+     */
7a3408
+    if (uid == (uid_t) -1)
7a3408
+        uid = geteuid();
7a3408
+    if (gid == (gid_t) -1)
7a3408
+        gid = getegid();
7a3408
+
7a3408
+    ngroups = virGetGroupList(uid, gid, &groups);
7a3408
+    if (ngroups < 0)
7a3408
+        return -errno;
7a3408
+
7a3408
+    pid = virFork();
7a3408
+
7a3408
+    if (pid < 0) {
7a3408
+        ret = -errno;
7a3408
+        VIR_FREE(groups);
7a3408
+        return ret;
7a3408
+    }
7a3408
+
7a3408
+    if (pid) { /* parent */
7a3408
+        /* wait for child to complete, and retrieve its exit code */
7a3408
+        VIR_FREE(groups);
7a3408
+
7a3408
+        while ((waitret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
7a3408
+        if (waitret == -1) {
7a3408
+            ret = -errno;
7a3408
+            virReportSystemError(errno,
7a3408
+                                 _("failed to wait for child unlinking '%s'"),
7a3408
+                                 path);
7a3408
+            goto parenterror;
7a3408
+        }
7a3408
+
7a3408
+        /*
7a3408
+         * If waitpid succeeded, but if the child exited abnormally or
7a3408
+         * reported non-zero status, report failure
7a3408
+         */
7a3408
+        if (!WIFEXITED(status) || (WEXITSTATUS(status)) != 0) {
7a3408
+            char *msg = virProcessTranslateStatus(status);
7a3408
+            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                           _("child failed to unlink '%s': %s"),
7a3408
+                           path, msg);
7a3408
+            VIR_FREE(msg);
7a3408
+            if (WIFEXITED(status))
7a3408
+                ret = -WEXITSTATUS(status);
7a3408
+            else
7a3408
+                ret = -EACCES;
7a3408
+        }
7a3408
+
7a3408
+ parenterror:
7a3408
+        return ret;
7a3408
+    }
7a3408
+
7a3408
+    /* child */
7a3408
+
7a3408
+    /* set desired uid/gid, then attempt to unlink the file */
7a3408
+    if (virSetUIDGID(uid, gid, groups, ngroups) < 0) {
7a3408
+        ret = errno;
7a3408
+        goto childerror;
7a3408
+    }
7a3408
+
7a3408
+    if (unlink(path) < 0) {
7a3408
+        ret = errno;
7a3408
+        goto childerror;
7a3408
+    }
7a3408
+
7a3408
+ childerror:
7a3408
+    if ((ret & 0xff) != ret) {
7a3408
+        VIR_WARN("unable to pass desired return value %d", ret);
7a3408
+        ret = 0xff;
7a3408
+    }
7a3408
+    _exit(ret);
7a3408
+}
7a3408
+
7a3408
+
7a3408
 /* return -errno on failure, or 0 on success */
7a3408
 static int
7a3408
 virDirCreateNoFork(const char *path,
7a3408
diff --git a/src/util/virfile.h b/src/util/virfile.h
7a3408
index 2d27e89..797ca65 100644
7a3408
--- a/src/util/virfile.h
7a3408
+++ b/src/util/virfile.h
7a3408
@@ -219,6 +219,7 @@ int virFileOpenAs(const char *path, int openflags, mode_t mode,
7a3408
                   uid_t uid, gid_t gid,
7a3408
                   unsigned int flags)
7a3408
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
7a3408
+int virFileUnlink(const char *path, uid_t uid, gid_t gid);
7a3408
 
7a3408
 enum {
7a3408
     VIR_DIR_CREATE_NONE        = 0,
7a3408
-- 
7a3408
2.5.1
7a3408