Tools: hv: vssdaemon: ignore the EBUSY on multiple freezing the same partition
authorDexuan Cui <decui@microsoft.com>
Fri, 26 Sep 2014 04:52:04 +0000 (21:52 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Nov 2014 18:13:29 +0000 (10:13 -0800)
If a partition appears mounted more than once in /proc/mounts, vss_do_freeze()
succeeds only for the first time and gets EBUSY (on freeze) or EINVAL (on
thaw) for the second time. The patch ignores these to make the backup feature
work.

Also improved the error handling in case a freeze operation fails.

Signed-off-by: Dexuan Cui <decui@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
tools/hv/hv_vss_daemon.c

index 6a213b8cd7b9034ad097d60b2ce5295e458d4054..1db9430d3fe4d544a693e8c10aa9bd6decf03bd1 100644 (file)
@@ -44,21 +44,39 @@ static struct sockaddr_nl addr;
 #endif
 
 
-static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op)
+/* Don't use syslog() in the function since that can cause write to disk */
+static int vss_do_freeze(char *dir, unsigned int cmd)
 {
        int ret, fd = open(dir, O_RDONLY);
 
        if (fd < 0)
                return 1;
+
        ret = ioctl(fd, cmd, 0);
-       syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno));
+
+       /*
+        * If a partition is mounted more than once, only the first
+        * FREEZE/THAW can succeed and the later ones will get
+        * EBUSY/EINVAL respectively: there could be 2 cases:
+        * 1) a user may mount the same partition to differnt directories
+        *  by mistake or on purpose;
+        * 2) The subvolume of btrfs appears to have the same partition
+        * mounted more than once.
+        */
+       if (ret) {
+               if ((cmd == FIFREEZE && errno == EBUSY) ||
+                   (cmd == FITHAW && errno == EINVAL)) {
+                       close(fd);
+                       return 0;
+               }
+       }
+
        close(fd);
        return !!ret;
 }
 
 static int vss_operate(int operation)
 {
-       char *fs_op;
        char match[] = "/dev/";
        FILE *mounts;
        struct mntent *ent;
@@ -68,11 +86,9 @@ static int vss_operate(int operation)
        switch (operation) {
        case VSS_OP_FREEZE:
                cmd = FIFREEZE;
-               fs_op = "freeze";
                break;
        case VSS_OP_THAW:
                cmd = FITHAW;
-               fs_op = "thaw";
                break;
        default:
                return -1;
@@ -93,14 +109,22 @@ static int vss_operate(int operation)
                        root_seen = 1;
                        continue;
                }
-               error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op);
+               error |= vss_do_freeze(ent->mnt_dir, cmd);
+               if (error && operation == VSS_OP_FREEZE)
+                       goto err;
        }
        endmntent(mounts);
 
        if (root_seen) {
-               error |= vss_do_freeze("/", cmd, fs_op);
+               error |= vss_do_freeze("/", cmd);
+               if (error && operation == VSS_OP_FREEZE)
+                       goto err;
        }
 
+       return error;
+err:
+       endmntent(mounts);
+       vss_operate(VSS_OP_THAW);
        return error;
 }
 
@@ -249,8 +273,16 @@ int main(void)
                case VSS_OP_FREEZE:
                case VSS_OP_THAW:
                        error = vss_operate(op);
-                       if (error)
+                       syslog(LOG_INFO, "VSS: op=%s: %s\n",
+                               op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
+                               error ? "failed" : "succeeded");
+
+                       if (error) {
                                error = HV_E_FAIL;
+                               syslog(LOG_ERR, "op=%d failed!", op);
+                               syslog(LOG_ERR, "report it with these files:");
+                               syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
+                       }
                        break;
                default:
                        syslog(LOG_ERR, "Illegal op:%d\n", op);