diff options
Diffstat (limited to 'tools/hv/hv_vss_daemon.c')
-rw-r--r-- | tools/hv/hv_vss_daemon.c | 95 |
1 files changed, 82 insertions, 13 deletions
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 6a213b8cd7b..5e63f70bd95 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -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); |