pwm: Remove unnecessary check before of_node_put()
[firefly-linux-kernel-4.4.55.git] / tools / hv / hv_vss_daemon.c
index 6a213b8cd7b9034ad097d60b2ce5295e458d4054..5e63f70bd956e56f9506ef306e11ff2206c23b52 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/hyperv.h>
 #include <linux/netlink.h>
 #include <syslog.h>
+#include <getopt.h>
 
 static struct sockaddr_nl addr;
 
@@ -44,35 +45,51 @@ 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;
        unsigned int cmd;
-       int error = 0, root_seen = 0;
+       int error = 0, root_seen = 0, save_errno = 0;
 
        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;
@@ -85,7 +102,7 @@ static int vss_operate(int operation)
        while ((ent = getmntent(mounts))) {
                if (strncmp(ent->mnt_fsname, match, strlen(match)))
                        continue;
-               if (strcmp(ent->mnt_type, "iso9660") == 0)
+               if (hasmntopt(ent, MNTOPT_RO) != NULL)
                        continue;
                if (strcmp(ent->mnt_type, "vfat") == 0)
                        continue;
@@ -93,14 +110,30 @@ 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;
        }
 
+       goto out;
+err:
+       save_errno = errno;
+       vss_operate(VSS_OP_THAW);
+       /* Call syslog after we thaw all filesystems */
+       if (ent)
+               syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
+                      ent->mnt_dir, save_errno, strerror(save_errno));
+       else
+               syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
+                      strerror(save_errno));
+out:
+       endmntent(mounts);
        return error;
 }
 
@@ -131,7 +164,15 @@ static int netlink_send(int fd, struct cn_msg *msg)
        return sendmsg(fd, &message, 0);
 }
 
-int main(void)
+void print_usage(char *argv[])
+{
+       fprintf(stderr, "Usage: %s [options]\n"
+               "Options are:\n"
+               "  -n, --no-daemon        stay in foreground, don't daemonize\n"
+               "  -h, --help             print this help\n", argv[0]);
+}
+
+int main(int argc, char *argv[])
 {
        int fd, len, nl_group;
        int error;
@@ -143,8 +184,28 @@ int main(void)
        struct hv_vss_msg *vss_msg;
        char *vss_recv_buffer;
        size_t vss_recv_buffer_len;
+       int daemonize = 1, long_index = 0, opt;
+
+       static struct option long_options[] = {
+               {"help",        no_argument,       0,  'h' },
+               {"no-daemon",   no_argument,       0,  'n' },
+               {0,             0,                 0,  0   }
+       };
+
+       while ((opt = getopt_long(argc, argv, "hn", long_options,
+                                 &long_index)) != -1) {
+               switch (opt) {
+               case 'n':
+                       daemonize = 0;
+                       break;
+               case 'h':
+               default:
+                       print_usage(argv);
+                       exit(EXIT_FAILURE);
+               }
+       }
 
-       if (daemon(1, 0))
+       if (daemonize && daemon(1, 0))
                return 1;
 
        openlog("Hyper-V VSS", 0, LOG_USER);
@@ -249,8 +310,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);