Changeset View
Standalone View
contrib/hyperv/tools/hv_vss_daemon.c
- This file was added.
#include <string.h> | |||||
#include <stdio.h> | |||||
#include <sys/ioctl.h> | |||||
#include <sys/param.h> | |||||
#include <sys/ucred.h> | |||||
#include <sys/mount.h> | |||||
#include <sys/types.h> | |||||
#include <unistd.h> | |||||
#include <stdlib.h> | |||||
#include <poll.h> | |||||
#include <stdint.h> | |||||
#include <syslog.h> | |||||
#include <errno.h> | |||||
#include <err.h> | |||||
#include <fcntl.h> | |||||
#include <ufs/ffs/fs.h> | |||||
#include <paths.h> | |||||
#include <sysexits.h> | |||||
#include "hv_snapshot.h" | |||||
#define UNDEF_FREEZE_THAW (0) | |||||
howard0su_gmail.com: how about zfs? or other filesystem doesn't support suspend? | |||||
Not Done Inline ActionsAs I know, ZFS does not support it. I need to double check it. If we want to extend this driver to support the other filesystem, what we need to modify is the daemon. So, the effort is not big. honzhan_microsoft.com: As I know, ZFS does not support it. I need to double check it. If we want to extend this driver… | |||||
Not Done Inline ActionsThis path is already defiined in paths.h. kib: This path is already defiined in paths.h. | |||||
#define FREEZE (1) | |||||
#define THAW (2) | |||||
#define VSS_LOG(priority, format, args...) do { \ | |||||
Not Done Inline ActionsWhy do you need additional define for 'unopened handle' ? Common unix practice is to mark closed file descriptor with the -1 value, which is very convenient since error return from open(2) is also -1. Below you test for -1 and UNOPENED_HANDLE, which would remove several lines of code if testing only for -1. kib: Why do you need additional define for 'unopened handle' ? Common unix practice is to mark… | |||||
Not Done Inline ActionsYeah, I'll remove UNOPEND_HANDLE. honzhan_microsoft.com: Yeah, I'll remove UNOPEND_HANDLE. | |||||
if (is_debugging == 1) { \ | |||||
if (is_daemon == 1) \ | |||||
syslog(priority, format, ## args); \ | |||||
else \ | |||||
printf(format, ## args); \ | |||||
} else { \ | |||||
if (priority < LOG_DEBUG) { \ | |||||
if (is_daemon == 1) \ | |||||
syslog(priority, format, ## args); \ | |||||
else \ | |||||
printf(format, ## args); \ | |||||
} \ | |||||
} \ | |||||
} while(0) | |||||
static int is_daemon = 1; | |||||
static int is_debugging = 0; | |||||
static int g_ufs_suspend_handle = -1; | |||||
static const char *dev = "/dev"; | |||||
static int | |||||
check(void) | |||||
Not Done Inline ActionsUse ANSI C definitions (freeze(void)). kib: Use ANSI C definitions (freeze(void)). | |||||
{ | |||||
struct statfs *mntbuf, *statfsp; | |||||
int mntsize; | |||||
int i; | |||||
mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); | |||||
if (mntsize == 0) { | |||||
VSS_LOG(LOG_ERR, "There is no mount information\n"); | |||||
return (EINVAL); | |||||
} | |||||
for (i = mntsize - 1; i >= 0; --i) | |||||
{ | |||||
statfsp = &mntbuf[i]; | |||||
if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { | |||||
continue; /* skip to freeze '/dev' */ | |||||
} else if (statfsp->f_flags & MNT_RDONLY) { | |||||
continue; /* skip to freeze RDONLY partition */ | |||||
} else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { | |||||
return (EPERM); /* only UFS can be freezed */ | |||||
} | |||||
} | |||||
Not Done Inline Actionsspace between for and '('. kib: space between for and '('. | |||||
Not Done Inline ActionsYeah. honzhan_microsoft.com: Yeah. | |||||
return (0); | |||||
} | |||||
static int | |||||
freeze(void) | |||||
{ | |||||
struct statfs *mntbuf, *statfsp; | |||||
int mntsize; | |||||
int error = 0; | |||||
int i; | |||||
g_ufs_suspend_handle = open(_PATH_UFSSUSPEND, O_RDWR); | |||||
Not Done Inline ActionsThis is excessive. The suspend ioctl does race-free sync when needed (after the writes are blocked), and only for the suspended volume. kib: This is excessive. The suspend ioctl does race-free sync when needed (after the writes are… | |||||
Not Done Inline ActionsThanks. That is what I want to confirm. honzhan_microsoft.com: Thanks. That is what I want to confirm. | |||||
if (g_ufs_suspend_handle == -1) { | |||||
VSS_LOG(LOG_ERR, "unable to open %s", _PATH_UFSSUSPEND); | |||||
return (errno); | |||||
} | |||||
mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); | |||||
if (mntsize == 0) { | |||||
VSS_LOG(LOG_ERR, "There is no mount information\n"); | |||||
return (EINVAL); | |||||
} | |||||
for (i = mntsize - 1; i >= 0; --i) | |||||
Not Done Inline ActionsThis is rather useless label. Why not write return (1); instead of all error = 1; goto handle_error; places ? kib: This is rather useless label. Why not write return (1); instead of all error = 1; goto… | |||||
{ | |||||
statfsp = &mntbuf[i]; | |||||
if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { | |||||
continue; /* skip to freeze '/dev' */ | |||||
} else if (statfsp->f_flags & MNT_RDONLY) { | |||||
continue; /* skip to freeze RDONLY partition */ | |||||
} else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { | |||||
Not Done Inline ActionsUse ANSI C. kib: Use ANSI C. | |||||
Not Done Inline ActionsSure. honzhan_microsoft.com: Sure. | |||||
continue; /* only UFS can be freezed */ | |||||
} | |||||
error = ioctl(g_ufs_suspend_handle, UFSSUSPEND, &statfsp->f_fsid); | |||||
if (error != 0) { | |||||
VSS_LOG(LOG_ERR, "error: %d\n", errno); | |||||
error = errno; | |||||
} else { | |||||
VSS_LOG(LOG_INFO, "Successfully suspend fs: %s\n", | |||||
statfsp->f_mntonname); | |||||
} | |||||
} | |||||
return (error); | |||||
} | |||||
/** | |||||
* close the opened handle will thaw the FS. | |||||
*/ | |||||
static int | |||||
thaw(void) | |||||
{ | |||||
Not Done Inline Actionsstatic void usage(const char *cmd) { fprintf(); kib: ```
static void
usage(const char *cmd)
{
fprintf();
``` | |||||
Not Done Inline ActionsThanks for your example. honzhan_microsoft.com: Thanks for your example. | |||||
int error = 0; | |||||
if (g_ufs_suspend_handle != -1) { | |||||
error = close(g_ufs_suspend_handle); | |||||
if (!error) { | |||||
g_ufs_suspend_handle = -1; | |||||
VSS_LOG(LOG_INFO, "Successfully thaw the fs\n"); | |||||
} else { | |||||
error = errno; | |||||
VSS_LOG(LOG_ERR, "Fail to thaw the fs: " | |||||
"%d %s\n", errno, strerror(errno)); | |||||
} | |||||
} else { | |||||
VSS_LOG(LOG_INFO, "The fs has already been thawed\n"); | |||||
} | |||||
return (error); | |||||
} | |||||
static void | |||||
usage(const char* cmd) { | |||||
fprintf(stderr, "%s -f : freeze the filesystem\n" | |||||
" -t : thaw the filesystem\n", cmd); | |||||
exit(1); | |||||
} | |||||
Not Done Inline ActionsYou silently ignore invalid options or stray arguments there. kib: You silently ignore invalid options or stray arguments there. | |||||
Not Done Inline ActionsOh, I need to print usage information for wrong options. honzhan_microsoft.com: Oh, I need to print usage information for wrong options. | |||||
int | |||||
main(int argc, char* argv[]) { | |||||
struct hv_vss_opt_msg userdata; | |||||
struct pollfd hv_vss_poll_fd[1]; | |||||
uint32_t op; | |||||
int ch, r, len, error; | |||||
int hv_vss_dev_fd; | |||||
int freeze_thaw = UNDEF_FREEZE_THAW; | |||||
while ((ch = getopt(argc, argv, "dn")) != -1) { | |||||
switch (ch) { | |||||
case 'n': | |||||
/* Run as regular process for debugging purpose. */ | |||||
is_daemon = 0; | |||||
break; | |||||
case 'd': | |||||
/* Generate debugging output */ | |||||
is_debugging = 1; | |||||
break; | |||||
default: | |||||
usage(argv[0]); | |||||
Not Done Inline ActionsExcessive (). kib: Excessive (). | |||||
Not Done Inline ActionsOk. honzhan_microsoft.com: Ok. | |||||
break; | |||||
} | |||||
} | |||||
openlog("HV_VSS", 0, LOG_USER); | |||||
/* Become daemon first. */ | |||||
if (is_daemon == 1) | |||||
daemon(1, 0); | |||||
else | |||||
VSS_LOG(LOG_DEBUG, "Run as regular process.\n"); | |||||
VSS_LOG(LOG_INFO, "HV_VSS starting; pid is: %d\n", getpid()); | |||||
/* register the daemon */ | |||||
hv_vss_dev_fd = open("/dev/hv_fsvss_dev", O_RDWR); | |||||
if (hv_vss_dev_fd < 0) { | |||||
VSS_LOG(LOG_ERR, "Fail to open /dev/hv_fsvss_dev, error: %d %s\n", | |||||
errno, strerror(errno)); | |||||
exit(EXIT_FAILURE); | |||||
} | |||||
hv_vss_poll_fd[0].fd = hv_vss_dev_fd; | |||||
hv_vss_poll_fd[0].events = POLLIN | POLLRDNORM; | |||||
while (1) { | |||||
Not Done Inline ActionsDo not do that. errno values and exit(3) codes are different value spaces. Please see sysexits(3) for common exit(3) codes. kib: Do not do that. errno values and exit(3) codes are different value spaces. Please see… | |||||
Not Done Inline ActionsThanks for pointing this preferable exit. honzhan_microsoft.com: Thanks for pointing this preferable exit. | |||||
r = poll(hv_vss_poll_fd, 1, INFTIM); | |||||
VSS_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n", | |||||
r, hv_vss_poll_fd[0].revents); | |||||
if (r == 0 || (r < 0 && errno == EAGAIN) || | |||||
(r < 0 && errno == EINTR)) { | |||||
/* Nothing to read */ | |||||
continue; | |||||
} | |||||
if (r < 0) { | |||||
/* | |||||
* For poll return failure other than EAGAIN, | |||||
* we want to exit. | |||||
*/ | |||||
VSS_LOG(LOG_ERR, "Poll failed.\n"); | |||||
perror("poll"); | |||||
exit(EIO); | |||||
} | |||||
/* Read from character device */ | |||||
error = ioctl(hv_vss_dev_fd, IOCHVVSSREAD, &userdata); | |||||
if (error < 0) { | |||||
VSS_LOG(LOG_ERR, "Read failed.\n"); | |||||
perror("pread"); | |||||
exit(EIO); | |||||
} | |||||
if (userdata.status != 0) { | |||||
VSS_LOG(LOG_ERR, "data read error\n"); | |||||
continue; | |||||
} | |||||
/* | |||||
* We will use the KVP header information to pass back | |||||
* the error from this daemon. So, first save the op | |||||
* and pool info to local variables. | |||||
*/ | |||||
op = userdata.opt; | |||||
switch (op) { | |||||
case HV_VSS_CHECK: | |||||
error = check(); | |||||
break; | |||||
case HV_VSS_FREEZE: | |||||
error = freeze(); | |||||
break; | |||||
case HV_VSS_THAW: | |||||
error = thaw(); | |||||
break; | |||||
default: | |||||
Not Done Inline ActionsThis return is not needed, and it is in fact strange to see use of exit() and return mixed in the main() function. kib: This return is not needed, and it is in fact strange to see use of exit() and return mixed in… | |||||
Not Done Inline ActionsYes. honzhan_microsoft.com: Yes. | |||||
VSS_LOG(LOG_ERR, "Illegal operation: %d\n", op); | |||||
error = VSS_FAIL; | |||||
} | |||||
if (error) | |||||
userdata.status = VSS_FAIL; | |||||
else | |||||
userdata.status = VSS_SUCCESS; | |||||
error = ioctl(hv_vss_dev_fd, IOCHVVSSWRITE, &userdata); | |||||
if (error != 0) { | |||||
VSS_LOG(LOG_ERR, "Fail to write to device\n"); | |||||
exit(EXIT_FAILURE); | |||||
} else { | |||||
VSS_LOG(LOG_INFO, "Send response %d for %s to kernel\n", | |||||
userdata.status, op == HV_VSS_FREEZE ? "Freeze" : | |||||
(op == HV_VSS_THAW ? "Thaw" : "Check")); | |||||
} | |||||
} | |||||
} |
how about zfs? or other filesystem doesn't support suspend?