1 /* 2 * QEMU Guest Agent Linux-specific command implementations 3 * 4 * Copyright IBM Corp. 2011 5 * 6 * Authors: 7 * Michael Roth <mdroth@linux.vnet.ibm.com> 8 * Michal Privoznik <mprivozn@redhat.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or later. 11 * See the COPYING file in the top-level directory. 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qapi/error.h" 16 #include "commands-common.h" 17 #include "cutils.h" 18 #include <mntent.h> 19 #include <sys/ioctl.h> 20 21 #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) 22 static int dev_major_minor(const char *devpath, 23 unsigned int *devmajor, unsigned int *devminor) 24 { 25 struct stat st; 26 27 *devmajor = 0; 28 *devminor = 0; 29 30 if (stat(devpath, &st) < 0) { 31 slog("failed to stat device file '%s': %s", devpath, strerror(errno)); 32 return -1; 33 } 34 if (S_ISDIR(st.st_mode)) { 35 /* It is bind mount */ 36 return -2; 37 } 38 if (S_ISBLK(st.st_mode)) { 39 *devmajor = major(st.st_rdev); 40 *devminor = minor(st.st_rdev); 41 return 0; 42 } 43 return -1; 44 } 45 46 static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) 47 { 48 struct mntent *ment; 49 FsMount *mount; 50 char const *mtab = "/proc/self/mounts"; 51 FILE *fp; 52 unsigned int devmajor, devminor; 53 54 fp = setmntent(mtab, "r"); 55 if (!fp) { 56 error_setg(errp, "failed to open mtab file: '%s'", mtab); 57 return false; 58 } 59 60 while ((ment = getmntent(fp))) { 61 /* 62 * An entry which device name doesn't start with a '/' is 63 * either a dummy file system or a network file system. 64 * Add special handling for smbfs and cifs as is done by 65 * coreutils as well. 66 */ 67 if ((ment->mnt_fsname[0] != '/') || 68 (strcmp(ment->mnt_type, "smbfs") == 0) || 69 (strcmp(ment->mnt_type, "cifs") == 0)) { 70 continue; 71 } 72 if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) { 73 /* Skip bind mounts */ 74 continue; 75 } 76 77 mount = g_new0(FsMount, 1); 78 mount->dirname = g_strdup(ment->mnt_dir); 79 mount->devtype = g_strdup(ment->mnt_type); 80 mount->devmajor = devmajor; 81 mount->devminor = devminor; 82 83 QTAILQ_INSERT_TAIL(mounts, mount, next); 84 } 85 86 endmntent(fp); 87 return true; 88 } 89 90 static void decode_mntname(char *name, int len) 91 { 92 int i, j = 0; 93 for (i = 0; i <= len; i++) { 94 if (name[i] != '\\') { 95 name[j++] = name[i]; 96 } else if (name[i + 1] == '\\') { 97 name[j++] = '\\'; 98 i++; 99 } else if (name[i + 1] >= '0' && name[i + 1] <= '3' && 100 name[i + 2] >= '0' && name[i + 2] <= '7' && 101 name[i + 3] >= '0' && name[i + 3] <= '7') { 102 name[j++] = (name[i + 1] - '0') * 64 + 103 (name[i + 2] - '0') * 8 + 104 (name[i + 3] - '0'); 105 i += 3; 106 } else { 107 name[j++] = name[i]; 108 } 109 } 110 } 111 112 /* 113 * Walk the mount table and build a list of local file systems 114 */ 115 bool build_fs_mount_list(FsMountList *mounts, Error **errp) 116 { 117 FsMount *mount; 118 char const *mountinfo = "/proc/self/mountinfo"; 119 FILE *fp; 120 char *line = NULL, *dash; 121 size_t n; 122 char check; 123 unsigned int devmajor, devminor; 124 int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e; 125 126 fp = fopen(mountinfo, "r"); 127 if (!fp) { 128 return build_fs_mount_list_from_mtab(mounts, errp); 129 } 130 131 while (getline(&line, &n, fp) != -1) { 132 ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c", 133 &devmajor, &devminor, &dir_s, &dir_e, &check); 134 if (ret < 3) { 135 continue; 136 } 137 dash = strstr(line + dir_e, " - "); 138 if (!dash) { 139 continue; 140 } 141 ret = sscanf(dash, " - %n%*s%n %n%*s%n%c", 142 &type_s, &type_e, &dev_s, &dev_e, &check); 143 if (ret < 1) { 144 continue; 145 } 146 line[dir_e] = 0; 147 dash[type_e] = 0; 148 dash[dev_e] = 0; 149 decode_mntname(line + dir_s, dir_e - dir_s); 150 decode_mntname(dash + dev_s, dev_e - dev_s); 151 if (devmajor == 0) { 152 /* btrfs reports major number = 0 */ 153 if (strcmp("btrfs", dash + type_s) != 0 || 154 dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) { 155 continue; 156 } 157 } 158 159 mount = g_new0(FsMount, 1); 160 mount->dirname = g_strdup(line + dir_s); 161 mount->devtype = g_strdup(dash + type_s); 162 mount->devmajor = devmajor; 163 mount->devminor = devminor; 164 165 QTAILQ_INSERT_TAIL(mounts, mount, next); 166 } 167 free(line); 168 169 fclose(fp); 170 return true; 171 } 172 #endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ 173 174 #ifdef CONFIG_FSFREEZE 175 /* 176 * Walk list of mounted file systems in the guest, and freeze the ones which 177 * are real local file systems. 178 */ 179 int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, 180 strList *mountpoints, 181 FsMountList mounts, 182 Error **errp) 183 { 184 struct FsMount *mount; 185 strList *list; 186 int fd, ret, i = 0; 187 188 QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { 189 /* To issue fsfreeze in the reverse order of mounts, check if the 190 * mount is listed in the list here */ 191 if (has_mountpoints) { 192 for (list = mountpoints; list; list = list->next) { 193 if (strcmp(list->value, mount->dirname) == 0) { 194 break; 195 } 196 } 197 if (!list) { 198 continue; 199 } 200 } 201 202 fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); 203 if (fd == -1) { 204 error_setg_errno(errp, errno, "failed to open %s", mount->dirname); 205 return -1; 206 } 207 208 /* we try to cull filesystems we know won't work in advance, but other 209 * filesystems may not implement fsfreeze for less obvious reasons. 210 * these will report EOPNOTSUPP. we simply ignore these when tallying 211 * the number of frozen filesystems. 212 * if a filesystem is mounted more than once (aka bind mount) a 213 * consecutive attempt to freeze an already frozen filesystem will 214 * return EBUSY. 215 * 216 * any other error means a failure to freeze a filesystem we 217 * expect to be freezable, so return an error in those cases 218 * and return system to thawed state. 219 */ 220 ret = ioctl(fd, FIFREEZE); 221 if (ret == -1) { 222 if (errno != EOPNOTSUPP && errno != EBUSY) { 223 error_setg_errno(errp, errno, "failed to freeze %s", 224 mount->dirname); 225 close(fd); 226 return -1; 227 } 228 } else { 229 i++; 230 } 231 close(fd); 232 } 233 return i; 234 } 235 236 int qmp_guest_fsfreeze_do_thaw(Error **errp) 237 { 238 int ret; 239 FsMountList mounts; 240 FsMount *mount; 241 int fd, i = 0, logged; 242 Error *local_err = NULL; 243 244 QTAILQ_INIT(&mounts); 245 if (!build_fs_mount_list(&mounts, &local_err)) { 246 error_propagate(errp, local_err); 247 return -1; 248 } 249 250 QTAILQ_FOREACH(mount, &mounts, next) { 251 logged = false; 252 fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); 253 if (fd == -1) { 254 continue; 255 } 256 /* we have no way of knowing whether a filesystem was actually unfrozen 257 * as a result of a successful call to FITHAW, only that if an error 258 * was returned the filesystem was *not* unfrozen by that particular 259 * call. 260 * 261 * since multiple preceding FIFREEZEs require multiple calls to FITHAW 262 * to unfreeze, continuing issuing FITHAW until an error is returned, 263 * in which case either the filesystem is in an unfreezable state, or, 264 * more likely, it was thawed previously (and remains so afterward). 265 * 266 * also, since the most recent successful call is the one that did 267 * the actual unfreeze, we can use this to provide an accurate count 268 * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which 269 * may * be useful for determining whether a filesystem was unfrozen 270 * during the freeze/thaw phase by a process other than qemu-ga. 271 */ 272 do { 273 ret = ioctl(fd, FITHAW); 274 if (ret == 0 && !logged) { 275 i++; 276 logged = true; 277 } 278 } while (ret == 0); 279 close(fd); 280 } 281 282 free_fs_mount_list(&mounts); 283 284 return i; 285 } 286 #endif /* CONFIG_FSFREEZE */ 287