cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hv_vss_daemon.c (7917B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * An implementation of the host initiated guest snapshot for Hyper-V.
      4 *
      5 * Copyright (C) 2013, Microsoft, Inc.
      6 * Author : K. Y. Srinivasan <kys@microsoft.com>
      7 */
      8
      9
     10#include <sys/types.h>
     11#include <sys/poll.h>
     12#include <sys/ioctl.h>
     13#include <sys/stat.h>
     14#include <sys/sysmacros.h>
     15#include <fcntl.h>
     16#include <stdio.h>
     17#include <mntent.h>
     18#include <stdlib.h>
     19#include <unistd.h>
     20#include <string.h>
     21#include <ctype.h>
     22#include <errno.h>
     23#include <linux/fs.h>
     24#include <linux/major.h>
     25#include <linux/hyperv.h>
     26#include <syslog.h>
     27#include <getopt.h>
     28#include <stdbool.h>
     29#include <dirent.h>
     30
     31static bool fs_frozen;
     32
     33/* Don't use syslog() in the function since that can cause write to disk */
     34static int vss_do_freeze(char *dir, unsigned int cmd)
     35{
     36	int ret, fd = open(dir, O_RDONLY);
     37
     38	if (fd < 0)
     39		return 1;
     40
     41	ret = ioctl(fd, cmd, 0);
     42
     43	/*
     44	 * If a partition is mounted more than once, only the first
     45	 * FREEZE/THAW can succeed and the later ones will get
     46	 * EBUSY/EINVAL respectively: there could be 2 cases:
     47	 * 1) a user may mount the same partition to different directories
     48	 *  by mistake or on purpose;
     49	 * 2) The subvolume of btrfs appears to have the same partition
     50	 * mounted more than once.
     51	 */
     52	if (ret) {
     53		if ((cmd == FIFREEZE && errno == EBUSY) ||
     54		    (cmd == FITHAW && errno == EINVAL)) {
     55			close(fd);
     56			return 0;
     57		}
     58	}
     59
     60	close(fd);
     61	return !!ret;
     62}
     63
     64static bool is_dev_loop(const char *blkname)
     65{
     66	char *buffer;
     67	DIR *dir;
     68	struct dirent *entry;
     69	bool ret = false;
     70
     71	buffer = malloc(PATH_MAX);
     72	if (!buffer) {
     73		syslog(LOG_ERR, "Can't allocate memory!");
     74		exit(1);
     75	}
     76
     77	snprintf(buffer, PATH_MAX, "%s/loop", blkname);
     78	if (!access(buffer, R_OK | X_OK)) {
     79		ret = true;
     80		goto free_buffer;
     81	} else if (errno != ENOENT) {
     82		syslog(LOG_ERR, "Can't access: %s; error:%d %s!",
     83		       buffer, errno, strerror(errno));
     84	}
     85
     86	snprintf(buffer, PATH_MAX, "%s/slaves", blkname);
     87	dir = opendir(buffer);
     88	if (!dir) {
     89		if (errno != ENOENT)
     90			syslog(LOG_ERR, "Can't opendir: %s; error:%d %s!",
     91			       buffer, errno, strerror(errno));
     92		goto free_buffer;
     93	}
     94
     95	while ((entry = readdir(dir)) != NULL) {
     96		if (strcmp(entry->d_name, ".") == 0 ||
     97		    strcmp(entry->d_name, "..") == 0)
     98			continue;
     99
    100		snprintf(buffer, PATH_MAX, "%s/slaves/%s", blkname,
    101			 entry->d_name);
    102		if (is_dev_loop(buffer)) {
    103			ret = true;
    104			break;
    105		}
    106	}
    107	closedir(dir);
    108free_buffer:
    109	free(buffer);
    110	return ret;
    111}
    112
    113static int vss_operate(int operation)
    114{
    115	char match[] = "/dev/";
    116	FILE *mounts;
    117	struct mntent *ent;
    118	struct stat sb;
    119	char errdir[1024] = {0};
    120	char blkdir[23]; /* /sys/dev/block/XXX:XXX */
    121	unsigned int cmd;
    122	int error = 0, root_seen = 0, save_errno = 0;
    123
    124	switch (operation) {
    125	case VSS_OP_FREEZE:
    126		cmd = FIFREEZE;
    127		break;
    128	case VSS_OP_THAW:
    129		cmd = FITHAW;
    130		break;
    131	default:
    132		return -1;
    133	}
    134
    135	mounts = setmntent("/proc/mounts", "r");
    136	if (mounts == NULL)
    137		return -1;
    138
    139	while ((ent = getmntent(mounts))) {
    140		if (strncmp(ent->mnt_fsname, match, strlen(match)))
    141			continue;
    142		if (stat(ent->mnt_fsname, &sb)) {
    143			syslog(LOG_ERR, "Can't stat: %s; error:%d %s!",
    144			       ent->mnt_fsname, errno, strerror(errno));
    145		} else {
    146			sprintf(blkdir, "/sys/dev/block/%d:%d",
    147				major(sb.st_rdev), minor(sb.st_rdev));
    148			if (is_dev_loop(blkdir))
    149				continue;
    150		}
    151		if (hasmntopt(ent, MNTOPT_RO) != NULL)
    152			continue;
    153		if (strcmp(ent->mnt_type, "vfat") == 0)
    154			continue;
    155		if (strcmp(ent->mnt_dir, "/") == 0) {
    156			root_seen = 1;
    157			continue;
    158		}
    159		error |= vss_do_freeze(ent->mnt_dir, cmd);
    160		if (operation == VSS_OP_FREEZE) {
    161			if (error)
    162				goto err;
    163			fs_frozen = true;
    164		}
    165	}
    166
    167	endmntent(mounts);
    168
    169	if (root_seen) {
    170		error |= vss_do_freeze("/", cmd);
    171		if (operation == VSS_OP_FREEZE) {
    172			if (error)
    173				goto err;
    174			fs_frozen = true;
    175		}
    176	}
    177
    178	if (operation == VSS_OP_THAW && !error)
    179		fs_frozen = false;
    180
    181	goto out;
    182err:
    183	save_errno = errno;
    184	if (ent) {
    185		strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
    186		endmntent(mounts);
    187	}
    188	vss_operate(VSS_OP_THAW);
    189	fs_frozen = false;
    190	/* Call syslog after we thaw all filesystems */
    191	if (ent)
    192		syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
    193		       errdir, save_errno, strerror(save_errno));
    194	else
    195		syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
    196		       strerror(save_errno));
    197out:
    198	return error;
    199}
    200
    201void print_usage(char *argv[])
    202{
    203	fprintf(stderr, "Usage: %s [options]\n"
    204		"Options are:\n"
    205		"  -n, --no-daemon        stay in foreground, don't daemonize\n"
    206		"  -h, --help             print this help\n", argv[0]);
    207}
    208
    209int main(int argc, char *argv[])
    210{
    211	int vss_fd = -1, len;
    212	int error;
    213	struct pollfd pfd;
    214	int	op;
    215	struct hv_vss_msg vss_msg[1];
    216	int daemonize = 1, long_index = 0, opt;
    217	int in_handshake;
    218	__u32 kernel_modver;
    219
    220	static struct option long_options[] = {
    221		{"help",	no_argument,	   0,  'h' },
    222		{"no-daemon",	no_argument,	   0,  'n' },
    223		{0,		0,		   0,  0   }
    224	};
    225
    226	while ((opt = getopt_long(argc, argv, "hn", long_options,
    227				  &long_index)) != -1) {
    228		switch (opt) {
    229		case 'n':
    230			daemonize = 0;
    231			break;
    232		case 'h':
    233			print_usage(argv);
    234			exit(0);
    235		default:
    236			print_usage(argv);
    237			exit(EXIT_FAILURE);
    238		}
    239	}
    240
    241	if (daemonize && daemon(1, 0))
    242		return 1;
    243
    244	openlog("Hyper-V VSS", 0, LOG_USER);
    245	syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
    246
    247reopen_vss_fd:
    248	if (vss_fd != -1)
    249		close(vss_fd);
    250	if (fs_frozen) {
    251		if (vss_operate(VSS_OP_THAW) || fs_frozen) {
    252			syslog(LOG_ERR, "failed to thaw file system: err=%d",
    253			       errno);
    254			exit(EXIT_FAILURE);
    255		}
    256	}
    257
    258	in_handshake = 1;
    259	vss_fd = open("/dev/vmbus/hv_vss", O_RDWR);
    260	if (vss_fd < 0) {
    261		syslog(LOG_ERR, "open /dev/vmbus/hv_vss failed; error: %d %s",
    262		       errno, strerror(errno));
    263		exit(EXIT_FAILURE);
    264	}
    265	/*
    266	 * Register ourselves with the kernel.
    267	 */
    268	vss_msg->vss_hdr.operation = VSS_OP_REGISTER1;
    269
    270	len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
    271	if (len < 0) {
    272		syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
    273		       errno, strerror(errno));
    274		close(vss_fd);
    275		exit(EXIT_FAILURE);
    276	}
    277
    278	pfd.fd = vss_fd;
    279
    280	while (1) {
    281		pfd.events = POLLIN;
    282		pfd.revents = 0;
    283
    284		if (poll(&pfd, 1, -1) < 0) {
    285			syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
    286			if (errno == EINVAL) {
    287				close(vss_fd);
    288				exit(EXIT_FAILURE);
    289			}
    290			else
    291				continue;
    292		}
    293
    294		len = read(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
    295
    296		if (in_handshake) {
    297			if (len != sizeof(kernel_modver)) {
    298				syslog(LOG_ERR, "invalid version negotiation");
    299				exit(EXIT_FAILURE);
    300			}
    301			kernel_modver = *(__u32 *)vss_msg;
    302			in_handshake = 0;
    303			syslog(LOG_INFO, "VSS: kernel module version: %d",
    304			       kernel_modver);
    305			continue;
    306		}
    307
    308		if (len != sizeof(struct hv_vss_msg)) {
    309			syslog(LOG_ERR, "read failed; error:%d %s",
    310			       errno, strerror(errno));
    311			goto reopen_vss_fd;
    312		}
    313
    314		op = vss_msg->vss_hdr.operation;
    315		error =  HV_S_OK;
    316
    317		switch (op) {
    318		case VSS_OP_FREEZE:
    319		case VSS_OP_THAW:
    320			error = vss_operate(op);
    321			syslog(LOG_INFO, "VSS: op=%s: %s\n",
    322				op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
    323				error ? "failed" : "succeeded");
    324
    325			if (error) {
    326				error = HV_E_FAIL;
    327				syslog(LOG_ERR, "op=%d failed!", op);
    328				syslog(LOG_ERR, "report it with these files:");
    329				syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
    330			}
    331			break;
    332		case VSS_OP_HOT_BACKUP:
    333			syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n");
    334			break;
    335		default:
    336			syslog(LOG_ERR, "Illegal op:%d\n", op);
    337		}
    338
    339		/*
    340		 * The write() may return an error due to the faked VSS_OP_THAW
    341		 * message upon hibernation. Ignore the error by resetting the
    342		 * dev file, i.e. closing and re-opening it.
    343		 */
    344		vss_msg->error = error;
    345		len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
    346		if (len != sizeof(struct hv_vss_msg)) {
    347			syslog(LOG_ERR, "write failed; error: %d %s", errno,
    348			       strerror(errno));
    349			goto reopen_vss_fd;
    350		}
    351	}
    352
    353	close(vss_fd);
    354	exit(0);
    355}