1 /* 2 * 9p Posix callback 3 * 4 * Copyright IBM, Corp. 2010 5 * 6 * Authors: 7 * Anthony Liguori <aliguori@us.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. See 10 * the COPYING file in the top-level directory. 11 */ 12 13 /* 14 * Not so fast! You might want to read the 9p developer docs first: 15 * https://wiki.qemu.org/Documentation/9p 16 */ 17 18 #include "qemu/osdep.h" 19 #include "9p.h" 20 #include "9p-local.h" 21 #include "9p-xattr.h" 22 #include "9p-util.h" 23 #include "fsdev/qemu-fsdev.h" /* local_ops */ 24 #include <arpa/inet.h> 25 #include <pwd.h> 26 #include <grp.h> 27 #include <sys/socket.h> 28 #include <sys/un.h> 29 #include "qemu/xattr.h" 30 #include "qapi/error.h" 31 #include "qemu/cutils.h" 32 #include "qemu/error-report.h" 33 #include "qemu/option.h" 34 #include <libgen.h> 35 #ifdef CONFIG_LINUX 36 #include <linux/fs.h> 37 #ifdef CONFIG_LINUX_MAGIC_H 38 #include <linux/magic.h> 39 #endif 40 #endif 41 #include <sys/ioctl.h> 42 43 #ifndef XFS_SUPER_MAGIC 44 #define XFS_SUPER_MAGIC 0x58465342 45 #endif 46 #ifndef EXT2_SUPER_MAGIC 47 #define EXT2_SUPER_MAGIC 0xEF53 48 #endif 49 #ifndef REISERFS_SUPER_MAGIC 50 #define REISERFS_SUPER_MAGIC 0x52654973 51 #endif 52 #ifndef BTRFS_SUPER_MAGIC 53 #define BTRFS_SUPER_MAGIC 0x9123683E 54 #endif 55 56 typedef struct { 57 int mountfd; 58 } LocalData; 59 60 int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, 61 mode_t mode) 62 { 63 LocalData *data = fs_ctx->private; 64 int fd = data->mountfd; 65 66 while (*path && fd != -1) { 67 const char *c; 68 int next_fd; 69 char *head; 70 71 /* Only relative paths without consecutive slashes */ 72 assert(*path != '/'); 73 74 head = g_strdup(path); 75 c = qemu_strchrnul(path, '/'); 76 if (*c) { 77 /* Intermediate path element */ 78 head[c - path] = 0; 79 path = c + 1; 80 next_fd = openat_dir(fd, head); 81 } else { 82 /* Rightmost path element */ 83 next_fd = openat_file(fd, head, flags, mode); 84 path = c; 85 } 86 g_free(head); 87 if (fd != data->mountfd) { 88 close_preserve_errno(fd); 89 } 90 fd = next_fd; 91 } 92 93 assert(fd != data->mountfd); 94 return fd; 95 } 96 97 int local_opendir_nofollow(FsContext *fs_ctx, const char *path) 98 { 99 return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0); 100 } 101 102 static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd, 103 const char *npath) 104 { 105 int serrno = errno; 106 qemu_renameat(odirfd, opath, ndirfd, npath); 107 errno = serrno; 108 } 109 110 static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) 111 { 112 int serrno = errno; 113 qemu_unlinkat(dirfd, path, flags); 114 errno = serrno; 115 } 116 117 #define VIRTFS_META_DIR ".virtfs_metadata" 118 #define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root" 119 120 static FILE *local_fopenat(int dirfd, const char *name, const char *mode) 121 { 122 int fd, o_mode = 0; 123 FILE *fp; 124 int flags; 125 /* 126 * only supports two modes 127 */ 128 if (mode[0] == 'r') { 129 flags = O_RDONLY; 130 } else if (mode[0] == 'w') { 131 flags = O_WRONLY | O_TRUNC | O_CREAT; 132 o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 133 } else { 134 return NULL; 135 } 136 fd = openat_file(dirfd, name, flags, o_mode); 137 if (fd == -1) { 138 return NULL; 139 } 140 fp = fdopen(fd, mode); 141 if (!fp) { 142 close(fd); 143 } 144 return fp; 145 } 146 147 #define ATTR_MAX 100 148 static void local_mapped_file_attr(int dirfd, const char *name, 149 struct stat *stbuf) 150 { 151 FILE *fp; 152 char buf[ATTR_MAX]; 153 int map_dirfd; 154 155 if (strcmp(name, ".")) { 156 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 157 if (map_dirfd == -1) { 158 return; 159 } 160 161 fp = local_fopenat(map_dirfd, name, "r"); 162 close_preserve_errno(map_dirfd); 163 } else { 164 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r"); 165 } 166 if (!fp) { 167 return; 168 } 169 memset(buf, 0, ATTR_MAX); 170 while (fgets(buf, ATTR_MAX, fp)) { 171 if (!strncmp(buf, "virtfs.uid", 10)) { 172 stbuf->st_uid = atoi(buf + 11); 173 } else if (!strncmp(buf, "virtfs.gid", 10)) { 174 stbuf->st_gid = atoi(buf + 11); 175 } else if (!strncmp(buf, "virtfs.mode", 11)) { 176 stbuf->st_mode = atoi(buf + 12); 177 } else if (!strncmp(buf, "virtfs.rdev", 11)) { 178 stbuf->st_rdev = atoi(buf + 12); 179 } 180 memset(buf, 0, ATTR_MAX); 181 } 182 fclose(fp); 183 } 184 185 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) 186 { 187 int err = -1; 188 char *dirpath = g_path_get_dirname(fs_path->data); 189 char *name = g_path_get_basename(fs_path->data); 190 int dirfd; 191 192 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 193 if (dirfd == -1) { 194 goto out; 195 } 196 197 err = qemu_fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); 198 if (err) { 199 goto err_out; 200 } 201 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 202 /* Actual credentials are part of extended attrs */ 203 uid_t tmp_uid; 204 gid_t tmp_gid; 205 mode_t tmp_mode; 206 dev_t tmp_dev; 207 208 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid, 209 sizeof(uid_t)) > 0) { 210 stbuf->st_uid = le32_to_cpu(tmp_uid); 211 } 212 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid, 213 sizeof(gid_t)) > 0) { 214 stbuf->st_gid = le32_to_cpu(tmp_gid); 215 } 216 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode, 217 sizeof(mode_t)) > 0) { 218 stbuf->st_mode = le32_to_cpu(tmp_mode); 219 } 220 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev, 221 sizeof(dev_t)) > 0) { 222 stbuf->st_rdev = le64_to_cpu(tmp_dev); 223 } 224 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 225 local_mapped_file_attr(dirfd, name, stbuf); 226 } 227 228 err_out: 229 close_preserve_errno(dirfd); 230 out: 231 g_free(name); 232 g_free(dirpath); 233 return err; 234 } 235 236 static int local_set_mapped_file_attrat(int dirfd, const char *name, 237 FsCred *credp) 238 { 239 FILE *fp; 240 int ret; 241 char buf[ATTR_MAX]; 242 int uid = -1, gid = -1, mode = -1, rdev = -1; 243 int map_dirfd = -1, map_fd; 244 bool is_root = !strcmp(name, "."); 245 246 if (is_root) { 247 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r"); 248 if (!fp) { 249 if (errno == ENOENT) { 250 goto update_map_file; 251 } else { 252 return -1; 253 } 254 } 255 } else { 256 ret = qemu_mkdirat(dirfd, VIRTFS_META_DIR, 0700); 257 if (ret < 0 && errno != EEXIST) { 258 return -1; 259 } 260 261 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 262 if (map_dirfd == -1) { 263 return -1; 264 } 265 266 fp = local_fopenat(map_dirfd, name, "r"); 267 if (!fp) { 268 if (errno == ENOENT) { 269 goto update_map_file; 270 } else { 271 close_preserve_errno(map_dirfd); 272 return -1; 273 } 274 } 275 } 276 memset(buf, 0, ATTR_MAX); 277 while (fgets(buf, ATTR_MAX, fp)) { 278 if (!strncmp(buf, "virtfs.uid", 10)) { 279 uid = atoi(buf + 11); 280 } else if (!strncmp(buf, "virtfs.gid", 10)) { 281 gid = atoi(buf + 11); 282 } else if (!strncmp(buf, "virtfs.mode", 11)) { 283 mode = atoi(buf + 12); 284 } else if (!strncmp(buf, "virtfs.rdev", 11)) { 285 rdev = atoi(buf + 12); 286 } 287 memset(buf, 0, ATTR_MAX); 288 } 289 fclose(fp); 290 291 update_map_file: 292 if (is_root) { 293 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "w"); 294 } else { 295 fp = local_fopenat(map_dirfd, name, "w"); 296 /* We can't go this far with map_dirfd not being a valid file descriptor 297 * but some versions of gcc aren't smart enough to see it. 298 */ 299 if (map_dirfd != -1) { 300 close_preserve_errno(map_dirfd); 301 } 302 } 303 if (!fp) { 304 return -1; 305 } 306 307 map_fd = fileno(fp); 308 assert(map_fd != -1); 309 ret = fchmod(map_fd, 0600); 310 assert(ret == 0); 311 312 if (credp->fc_uid != -1) { 313 uid = credp->fc_uid; 314 } 315 if (credp->fc_gid != -1) { 316 gid = credp->fc_gid; 317 } 318 if (credp->fc_mode != (mode_t)-1) { 319 mode = credp->fc_mode; 320 } 321 if (credp->fc_rdev != -1) { 322 rdev = credp->fc_rdev; 323 } 324 325 if (uid != -1) { 326 fprintf(fp, "virtfs.uid=%d\n", uid); 327 } 328 if (gid != -1) { 329 fprintf(fp, "virtfs.gid=%d\n", gid); 330 } 331 if (mode != -1) { 332 fprintf(fp, "virtfs.mode=%d\n", mode); 333 } 334 if (rdev != -1) { 335 fprintf(fp, "virtfs.rdev=%d\n", rdev); 336 } 337 fclose(fp); 338 339 return 0; 340 } 341 342 static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode) 343 { 344 struct stat stbuf; 345 int fd, ret; 346 347 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW). 348 * Unfortunately, the linux kernel doesn't implement it yet. 349 */ 350 351 /* First, we clear non-racing symlinks out of the way. */ 352 if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) { 353 return -1; 354 } 355 if (S_ISLNK(stbuf.st_mode)) { 356 errno = ELOOP; 357 return -1; 358 } 359 360 fd = openat_file(dirfd, name, O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); 361 #if O_PATH_9P_UTIL == 0 362 /* Fallback for systems that don't support O_PATH: we depend on the file 363 * being readable or writable. 364 */ 365 if (fd == -1) { 366 /* In case the file is writable-only and isn't a directory. */ 367 if (errno == EACCES) { 368 fd = openat_file(dirfd, name, O_WRONLY, 0); 369 } 370 if (fd == -1 && errno == EISDIR) { 371 errno = EACCES; 372 } 373 } 374 if (fd == -1) { 375 return -1; 376 } 377 ret = fchmod(fd, mode); 378 #else 379 /* Access modes are ignored when O_PATH is supported. If name is a symbolic 380 * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor 381 * referring to the symbolic link. 382 */ 383 if (fd == -1) { 384 return -1; 385 } 386 387 /* Now we handle racing symlinks. */ 388 ret = fstat(fd, &stbuf); 389 if (!ret) { 390 if (S_ISLNK(stbuf.st_mode)) { 391 errno = ELOOP; 392 ret = -1; 393 } else { 394 char *proc_path = g_strdup_printf("/proc/self/fd/%d", fd); 395 ret = chmod(proc_path, mode); 396 g_free(proc_path); 397 } 398 } 399 #endif 400 close_preserve_errno(fd); 401 return ret; 402 } 403 404 static int local_set_xattrat(int dirfd, const char *path, FsCred *credp) 405 { 406 int err; 407 408 if (credp->fc_uid != -1) { 409 uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); 410 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid, 411 sizeof(uid_t), 0); 412 if (err) { 413 return err; 414 } 415 } 416 if (credp->fc_gid != -1) { 417 uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); 418 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid, 419 sizeof(gid_t), 0); 420 if (err) { 421 return err; 422 } 423 } 424 if (credp->fc_mode != (mode_t)-1) { 425 uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); 426 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode, 427 sizeof(mode_t), 0); 428 if (err) { 429 return err; 430 } 431 } 432 if (credp->fc_rdev != -1) { 433 uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); 434 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev, 435 sizeof(dev_t), 0); 436 if (err) { 437 return err; 438 } 439 } 440 return 0; 441 } 442 443 static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd, 444 const char *name, FsCred *credp) 445 { 446 if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 447 AT_SYMLINK_NOFOLLOW) < 0) { 448 /* 449 * If we fail to change ownership and if we are 450 * using security model none. Ignore the error 451 */ 452 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 453 return -1; 454 } 455 } 456 457 return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777); 458 } 459 460 static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, 461 char *buf, size_t bufsz) 462 { 463 ssize_t tsize = -1; 464 465 if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || 466 (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { 467 int fd; 468 469 fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0); 470 if (fd == -1) { 471 return -1; 472 } 473 tsize = RETRY_ON_EINTR(read(fd, (void *)buf, bufsz)); 474 close_preserve_errno(fd); 475 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 476 (fs_ctx->export_flags & V9FS_SM_NONE)) { 477 char *dirpath = g_path_get_dirname(fs_path->data); 478 char *name = g_path_get_basename(fs_path->data); 479 int dirfd; 480 481 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 482 if (dirfd == -1) { 483 goto out; 484 } 485 486 tsize = readlinkat(dirfd, name, buf, bufsz); 487 close_preserve_errno(dirfd); 488 out: 489 g_free(name); 490 g_free(dirpath); 491 } 492 return tsize; 493 } 494 495 static int local_close(FsContext *ctx, V9fsFidOpenState *fs) 496 { 497 return close(fs->fd); 498 } 499 500 static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) 501 { 502 return closedir(fs->dir.stream); 503 } 504 505 static int local_open(FsContext *ctx, V9fsPath *fs_path, 506 int flags, V9fsFidOpenState *fs) 507 { 508 int fd; 509 510 fd = local_open_nofollow(ctx, fs_path->data, flags, 0); 511 if (fd == -1) { 512 return -1; 513 } 514 fs->fd = fd; 515 return fs->fd; 516 } 517 518 static int local_opendir(FsContext *ctx, 519 V9fsPath *fs_path, V9fsFidOpenState *fs) 520 { 521 int dirfd; 522 DIR *stream; 523 524 dirfd = local_opendir_nofollow(ctx, fs_path->data); 525 if (dirfd == -1) { 526 return -1; 527 } 528 529 stream = fdopendir(dirfd); 530 if (!stream) { 531 close(dirfd); 532 return -1; 533 } 534 fs->dir.stream = stream; 535 return 0; 536 } 537 538 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) 539 { 540 rewinddir(fs->dir.stream); 541 } 542 543 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) 544 { 545 return telldir(fs->dir.stream); 546 } 547 548 static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name) 549 { 550 return 551 !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE); 552 } 553 554 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) 555 { 556 struct dirent *entry; 557 558 again: 559 entry = readdir(fs->dir.stream); 560 if (!entry) { 561 return NULL; 562 } 563 #ifdef CONFIG_DARWIN 564 int off; 565 off = telldir(fs->dir.stream); 566 /* If telldir fails, fail the entire readdir call */ 567 if (off < 0) { 568 return NULL; 569 } 570 entry->d_seekoff = off; 571 #endif 572 573 if (ctx->export_flags & V9FS_SM_MAPPED) { 574 entry->d_type = DT_UNKNOWN; 575 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 576 if (local_is_mapped_file_metadata(ctx, entry->d_name)) { 577 /* skip the meta data */ 578 goto again; 579 } 580 entry->d_type = DT_UNKNOWN; 581 } 582 583 return entry; 584 } 585 586 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) 587 { 588 seekdir(fs->dir.stream, off); 589 } 590 591 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, 592 const struct iovec *iov, 593 int iovcnt, off_t offset) 594 { 595 #ifdef CONFIG_PREADV 596 return preadv(fs->fd, iov, iovcnt, offset); 597 #else 598 int err = lseek(fs->fd, offset, SEEK_SET); 599 if (err == -1) { 600 return err; 601 } else { 602 return readv(fs->fd, iov, iovcnt); 603 } 604 #endif 605 } 606 607 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, 608 const struct iovec *iov, 609 int iovcnt, off_t offset) 610 { 611 ssize_t ret; 612 #ifdef CONFIG_PREADV 613 ret = pwritev(fs->fd, iov, iovcnt, offset); 614 #else 615 int err = lseek(fs->fd, offset, SEEK_SET); 616 if (err == -1) { 617 return err; 618 } else { 619 ret = writev(fs->fd, iov, iovcnt); 620 } 621 #endif 622 #ifdef CONFIG_SYNC_FILE_RANGE 623 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { 624 /* 625 * Initiate a writeback. This is not a data integrity sync. 626 * We want to ensure that we don't leave dirty pages in the cache 627 * after write when writeout=immediate is specified. 628 */ 629 sync_file_range(fs->fd, offset, ret, 630 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); 631 } 632 #endif 633 return ret; 634 } 635 636 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 637 { 638 char *dirpath = g_path_get_dirname(fs_path->data); 639 char *name = g_path_get_basename(fs_path->data); 640 int ret = -1; 641 int dirfd; 642 643 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 644 if (dirfd == -1) { 645 goto out; 646 } 647 648 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 649 ret = local_set_xattrat(dirfd, name, credp); 650 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 651 ret = local_set_mapped_file_attrat(dirfd, name, credp); 652 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 653 fs_ctx->export_flags & V9FS_SM_NONE) { 654 ret = fchmodat_nofollow(dirfd, name, credp->fc_mode); 655 } 656 close_preserve_errno(dirfd); 657 658 out: 659 g_free(dirpath); 660 g_free(name); 661 return ret; 662 } 663 664 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, 665 const char *name, FsCred *credp) 666 { 667 int err = -1; 668 int dirfd; 669 670 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 671 local_is_mapped_file_metadata(fs_ctx, name)) { 672 errno = EINVAL; 673 return -1; 674 } 675 676 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 677 if (dirfd == -1) { 678 return -1; 679 } 680 681 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 682 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 683 err = qemu_mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0); 684 if (err == -1) { 685 goto out; 686 } 687 688 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 689 err = local_set_xattrat(dirfd, name, credp); 690 } else { 691 err = local_set_mapped_file_attrat(dirfd, name, credp); 692 } 693 if (err == -1) { 694 goto err_end; 695 } 696 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 697 fs_ctx->export_flags & V9FS_SM_NONE) { 698 err = qemu_mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); 699 if (err == -1) { 700 goto out; 701 } 702 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 703 if (err == -1) { 704 goto err_end; 705 } 706 } 707 goto out; 708 709 err_end: 710 unlinkat_preserve_errno(dirfd, name, 0); 711 out: 712 close_preserve_errno(dirfd); 713 return err; 714 } 715 716 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, 717 const char *name, FsCred *credp) 718 { 719 int err = -1; 720 int dirfd; 721 722 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 723 local_is_mapped_file_metadata(fs_ctx, name)) { 724 errno = EINVAL; 725 return -1; 726 } 727 728 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 729 if (dirfd == -1) { 730 return -1; 731 } 732 733 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 734 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 735 err = qemu_mkdirat(dirfd, name, fs_ctx->dmode); 736 if (err == -1) { 737 goto out; 738 } 739 credp->fc_mode = credp->fc_mode | S_IFDIR; 740 741 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 742 err = local_set_xattrat(dirfd, name, credp); 743 } else { 744 err = local_set_mapped_file_attrat(dirfd, name, credp); 745 } 746 if (err == -1) { 747 goto err_end; 748 } 749 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 750 fs_ctx->export_flags & V9FS_SM_NONE) { 751 err = qemu_mkdirat(dirfd, name, credp->fc_mode); 752 if (err == -1) { 753 goto out; 754 } 755 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 756 if (err == -1) { 757 goto err_end; 758 } 759 } 760 goto out; 761 762 err_end: 763 unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR); 764 out: 765 close_preserve_errno(dirfd); 766 return err; 767 } 768 769 static int local_fid_fd(int fid_type, V9fsFidOpenState *fs) 770 { 771 if (fid_type == P9_FID_DIR) { 772 return dirfd(fs->dir.stream); 773 } else { 774 return fs->fd; 775 } 776 } 777 778 static int local_fstat(FsContext *fs_ctx, int fid_type, 779 V9fsFidOpenState *fs, struct stat *stbuf) 780 { 781 int err, fd = local_fid_fd(fid_type, fs); 782 783 err = fstat(fd, stbuf); 784 if (err) { 785 return err; 786 } 787 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 788 /* Actual credentials are part of extended attrs */ 789 uid_t tmp_uid; 790 gid_t tmp_gid; 791 mode_t tmp_mode; 792 dev_t tmp_dev; 793 794 if (qemu_fgetxattr(fd, "user.virtfs.uid", 795 &tmp_uid, sizeof(uid_t)) > 0) { 796 stbuf->st_uid = le32_to_cpu(tmp_uid); 797 } 798 if (qemu_fgetxattr(fd, "user.virtfs.gid", 799 &tmp_gid, sizeof(gid_t)) > 0) { 800 stbuf->st_gid = le32_to_cpu(tmp_gid); 801 } 802 if (qemu_fgetxattr(fd, "user.virtfs.mode", 803 &tmp_mode, sizeof(mode_t)) > 0) { 804 stbuf->st_mode = le32_to_cpu(tmp_mode); 805 } 806 if (qemu_fgetxattr(fd, "user.virtfs.rdev", 807 &tmp_dev, sizeof(dev_t)) > 0) { 808 stbuf->st_rdev = le64_to_cpu(tmp_dev); 809 } 810 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 811 errno = EOPNOTSUPP; 812 return -1; 813 } 814 return err; 815 } 816 817 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, 818 int flags, FsCred *credp, V9fsFidOpenState *fs) 819 { 820 int fd = -1; 821 int err = -1; 822 int dirfd; 823 824 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 825 local_is_mapped_file_metadata(fs_ctx, name)) { 826 errno = EINVAL; 827 return -1; 828 } 829 830 /* 831 * Mark all the open to not follow symlinks 832 */ 833 flags |= O_NOFOLLOW; 834 835 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 836 if (dirfd == -1) { 837 return -1; 838 } 839 840 /* Determine the security model */ 841 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 842 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 843 fd = openat_file(dirfd, name, flags, fs_ctx->fmode); 844 if (fd == -1) { 845 goto out; 846 } 847 credp->fc_mode = credp->fc_mode | S_IFREG; 848 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 849 /* Set client credentials in xattr */ 850 err = local_set_xattrat(dirfd, name, credp); 851 } else { 852 err = local_set_mapped_file_attrat(dirfd, name, credp); 853 } 854 if (err == -1) { 855 goto err_end; 856 } 857 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 858 (fs_ctx->export_flags & V9FS_SM_NONE)) { 859 fd = openat_file(dirfd, name, flags, credp->fc_mode); 860 if (fd == -1) { 861 goto out; 862 } 863 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 864 if (err == -1) { 865 goto err_end; 866 } 867 } 868 err = fd; 869 fs->fd = fd; 870 goto out; 871 872 err_end: 873 unlinkat_preserve_errno(dirfd, name, 874 flags & O_DIRECTORY ? AT_REMOVEDIR : 0); 875 close_preserve_errno(fd); 876 out: 877 close_preserve_errno(dirfd); 878 return err; 879 } 880 881 882 static int local_symlink(FsContext *fs_ctx, const char *oldpath, 883 V9fsPath *dir_path, const char *name, FsCred *credp) 884 { 885 int err = -1; 886 int dirfd; 887 888 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 889 local_is_mapped_file_metadata(fs_ctx, name)) { 890 errno = EINVAL; 891 return -1; 892 } 893 894 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 895 if (dirfd == -1) { 896 return -1; 897 } 898 899 /* Determine the security model */ 900 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 901 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 902 int fd; 903 ssize_t oldpath_size, write_size; 904 905 fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR, 906 fs_ctx->fmode); 907 if (fd == -1) { 908 goto out; 909 } 910 /* Write the oldpath (target) to the file. */ 911 oldpath_size = strlen(oldpath); 912 write_size = RETRY_ON_EINTR(write(fd, (void *)oldpath, oldpath_size)); 913 close_preserve_errno(fd); 914 915 if (write_size != oldpath_size) { 916 goto err_end; 917 } 918 /* Set client credentials in symlink's xattr */ 919 credp->fc_mode = credp->fc_mode | S_IFLNK; 920 921 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 922 err = local_set_xattrat(dirfd, name, credp); 923 } else { 924 err = local_set_mapped_file_attrat(dirfd, name, credp); 925 } 926 if (err == -1) { 927 goto err_end; 928 } 929 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 930 fs_ctx->export_flags & V9FS_SM_NONE) { 931 err = symlinkat(oldpath, dirfd, name); 932 if (err) { 933 goto out; 934 } 935 err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 936 AT_SYMLINK_NOFOLLOW); 937 if (err == -1) { 938 /* 939 * If we fail to change ownership and if we are 940 * using security model none. Ignore the error 941 */ 942 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 943 goto err_end; 944 } else { 945 err = 0; 946 } 947 } 948 } 949 goto out; 950 951 err_end: 952 unlinkat_preserve_errno(dirfd, name, 0); 953 out: 954 close_preserve_errno(dirfd); 955 return err; 956 } 957 958 static int local_link(FsContext *ctx, V9fsPath *oldpath, 959 V9fsPath *dirpath, const char *name) 960 { 961 char *odirpath = g_path_get_dirname(oldpath->data); 962 char *oname = g_path_get_basename(oldpath->data); 963 int ret = -1; 964 int odirfd, ndirfd; 965 966 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 967 local_is_mapped_file_metadata(ctx, name)) { 968 errno = EINVAL; 969 goto out; 970 } 971 972 odirfd = local_opendir_nofollow(ctx, odirpath); 973 if (odirfd == -1) { 974 goto out; 975 } 976 977 ndirfd = local_opendir_nofollow(ctx, dirpath->data); 978 if (ndirfd == -1) { 979 close_preserve_errno(odirfd); 980 goto out; 981 } 982 983 ret = linkat(odirfd, oname, ndirfd, name, 0); 984 if (ret < 0) { 985 goto out_close; 986 } 987 988 /* now link the virtfs_metadata files */ 989 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 990 int omap_dirfd, nmap_dirfd; 991 992 ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 993 if (ret < 0 && errno != EEXIST) { 994 goto err_undo_link; 995 } 996 997 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 998 if (omap_dirfd == -1) { 999 goto err; 1000 } 1001 1002 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1003 if (nmap_dirfd == -1) { 1004 close_preserve_errno(omap_dirfd); 1005 goto err; 1006 } 1007 1008 ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0); 1009 close_preserve_errno(nmap_dirfd); 1010 close_preserve_errno(omap_dirfd); 1011 if (ret < 0 && errno != ENOENT) { 1012 goto err_undo_link; 1013 } 1014 1015 ret = 0; 1016 } 1017 goto out_close; 1018 1019 err: 1020 ret = -1; 1021 err_undo_link: 1022 unlinkat_preserve_errno(ndirfd, name, 0); 1023 out_close: 1024 close_preserve_errno(ndirfd); 1025 close_preserve_errno(odirfd); 1026 out: 1027 g_free(oname); 1028 g_free(odirpath); 1029 return ret; 1030 } 1031 1032 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) 1033 { 1034 int fd, ret; 1035 1036 fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); 1037 if (fd == -1) { 1038 return -1; 1039 } 1040 ret = ftruncate(fd, size); 1041 close_preserve_errno(fd); 1042 return ret; 1043 } 1044 1045 static int local_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, 1046 off_t size) 1047 { 1048 int fd = local_fid_fd(fid_type, fs); 1049 1050 return ftruncate(fd, size); 1051 } 1052 1053 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 1054 { 1055 char *dirpath = g_path_get_dirname(fs_path->data); 1056 char *name = g_path_get_basename(fs_path->data); 1057 int ret = -1; 1058 int dirfd; 1059 1060 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 1061 if (dirfd == -1) { 1062 goto out; 1063 } 1064 1065 if ((credp->fc_uid == -1 && credp->fc_gid == -1) || 1066 (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 1067 (fs_ctx->export_flags & V9FS_SM_NONE)) { 1068 ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 1069 AT_SYMLINK_NOFOLLOW); 1070 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 1071 ret = local_set_xattrat(dirfd, name, credp); 1072 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1073 ret = local_set_mapped_file_attrat(dirfd, name, credp); 1074 } 1075 1076 close_preserve_errno(dirfd); 1077 out: 1078 g_free(name); 1079 g_free(dirpath); 1080 return ret; 1081 } 1082 1083 static int local_utimensat(FsContext *s, V9fsPath *fs_path, 1084 const struct timespec *buf) 1085 { 1086 char *dirpath = g_path_get_dirname(fs_path->data); 1087 char *name = g_path_get_basename(fs_path->data); 1088 int dirfd, ret = -1; 1089 1090 dirfd = local_opendir_nofollow(s, dirpath); 1091 if (dirfd == -1) { 1092 goto out; 1093 } 1094 1095 ret = qemu_utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); 1096 close_preserve_errno(dirfd); 1097 out: 1098 g_free(dirpath); 1099 g_free(name); 1100 return ret; 1101 } 1102 1103 static int local_futimens(FsContext *s, int fid_type, V9fsFidOpenState *fs, 1104 const struct timespec *times) 1105 { 1106 int fd = local_fid_fd(fid_type, fs); 1107 1108 return qemu_futimens(fd, times); 1109 } 1110 1111 static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, 1112 int flags) 1113 { 1114 int ret; 1115 1116 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1117 int map_dirfd; 1118 1119 /* We need to remove the metadata as well: 1120 * - the metadata directory if we're removing a directory 1121 * - the metadata file in the parent's metadata directory 1122 * 1123 * If any of these are missing (ie, ENOENT) then we're probably 1124 * trying to remove something that wasn't created in mapped-file 1125 * mode. We just ignore the error. 1126 */ 1127 if (flags == AT_REMOVEDIR) { 1128 int fd; 1129 1130 fd = openat_dir(dirfd, name); 1131 if (fd == -1) { 1132 return -1; 1133 } 1134 ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); 1135 close_preserve_errno(fd); 1136 if (ret < 0 && errno != ENOENT) { 1137 return -1; 1138 } 1139 } 1140 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 1141 if (map_dirfd != -1) { 1142 ret = qemu_unlinkat(map_dirfd, name, 0); 1143 close_preserve_errno(map_dirfd); 1144 if (ret < 0 && errno != ENOENT) { 1145 return -1; 1146 } 1147 } else if (errno != ENOENT) { 1148 return -1; 1149 } 1150 } 1151 1152 return qemu_unlinkat(dirfd, name, flags); 1153 } 1154 1155 static int local_remove(FsContext *ctx, const char *path) 1156 { 1157 struct stat stbuf; 1158 char *dirpath = g_path_get_dirname(path); 1159 char *name = g_path_get_basename(path); 1160 int flags = 0; 1161 int dirfd; 1162 int err = -1; 1163 1164 dirfd = local_opendir_nofollow(ctx, dirpath); 1165 if (dirfd == -1) { 1166 goto out; 1167 } 1168 1169 if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { 1170 goto err_out; 1171 } 1172 1173 if (S_ISDIR(stbuf.st_mode)) { 1174 flags |= AT_REMOVEDIR; 1175 } 1176 1177 err = local_unlinkat_common(ctx, dirfd, name, flags); 1178 err_out: 1179 close_preserve_errno(dirfd); 1180 out: 1181 g_free(name); 1182 g_free(dirpath); 1183 return err; 1184 } 1185 1186 static int local_fsync(FsContext *ctx, int fid_type, 1187 V9fsFidOpenState *fs, int datasync) 1188 { 1189 int fd = local_fid_fd(fid_type, fs); 1190 1191 if (datasync) { 1192 return qemu_fdatasync(fd); 1193 } else { 1194 return fsync(fd); 1195 } 1196 } 1197 1198 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) 1199 { 1200 int fd, ret; 1201 1202 fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0); 1203 if (fd == -1) { 1204 return -1; 1205 } 1206 ret = fstatfs(fd, stbuf); 1207 close_preserve_errno(fd); 1208 return ret; 1209 } 1210 1211 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, 1212 const char *name, void *value, size_t size) 1213 { 1214 char *path = fs_path->data; 1215 1216 return v9fs_get_xattr(ctx, path, name, value, size); 1217 } 1218 1219 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, 1220 void *value, size_t size) 1221 { 1222 char *path = fs_path->data; 1223 1224 return v9fs_list_xattr(ctx, path, value, size); 1225 } 1226 1227 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, 1228 void *value, size_t size, int flags) 1229 { 1230 char *path = fs_path->data; 1231 1232 return v9fs_set_xattr(ctx, path, name, value, size, flags); 1233 } 1234 1235 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, 1236 const char *name) 1237 { 1238 char *path = fs_path->data; 1239 1240 return v9fs_remove_xattr(ctx, path, name); 1241 } 1242 1243 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, 1244 const char *name, V9fsPath *target) 1245 { 1246 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1247 local_is_mapped_file_metadata(ctx, name)) { 1248 errno = EINVAL; 1249 return -1; 1250 } 1251 1252 if (dir_path) { 1253 if (!strcmp(name, ".")) { 1254 /* "." relative to "foo/bar" is "foo/bar" */ 1255 v9fs_path_copy(target, dir_path); 1256 } else if (!strcmp(name, "..")) { 1257 if (!strcmp(dir_path->data, ".")) { 1258 /* ".." relative to the root is "." */ 1259 v9fs_path_sprintf(target, "."); 1260 } else { 1261 char *tmp = g_path_get_dirname(dir_path->data); 1262 /* Symbolic links are resolved by the client. We can assume 1263 * that ".." relative to "foo/bar" is equivalent to "foo" 1264 */ 1265 v9fs_path_sprintf(target, "%s", tmp); 1266 g_free(tmp); 1267 } 1268 } else { 1269 assert(!strchr(name, '/')); 1270 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); 1271 } 1272 } else if (!strcmp(name, "/") || !strcmp(name, ".") || 1273 !strcmp(name, "..")) { 1274 /* This is the root fid */ 1275 v9fs_path_sprintf(target, "."); 1276 } else { 1277 assert(!strchr(name, '/')); 1278 v9fs_path_sprintf(target, "./%s", name); 1279 } 1280 return 0; 1281 } 1282 1283 static int local_renameat(FsContext *ctx, V9fsPath *olddir, 1284 const char *old_name, V9fsPath *newdir, 1285 const char *new_name) 1286 { 1287 int ret; 1288 int odirfd, ndirfd; 1289 1290 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1291 (local_is_mapped_file_metadata(ctx, old_name) || 1292 local_is_mapped_file_metadata(ctx, new_name))) { 1293 errno = EINVAL; 1294 return -1; 1295 } 1296 1297 odirfd = local_opendir_nofollow(ctx, olddir->data); 1298 if (odirfd == -1) { 1299 return -1; 1300 } 1301 1302 ndirfd = local_opendir_nofollow(ctx, newdir->data); 1303 if (ndirfd == -1) { 1304 close_preserve_errno(odirfd); 1305 return -1; 1306 } 1307 1308 ret = qemu_renameat(odirfd, old_name, ndirfd, new_name); 1309 if (ret < 0) { 1310 goto out; 1311 } 1312 1313 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1314 int omap_dirfd, nmap_dirfd; 1315 1316 ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 1317 if (ret < 0 && errno != EEXIST) { 1318 goto err_undo_rename; 1319 } 1320 1321 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 1322 if (omap_dirfd == -1) { 1323 goto err; 1324 } 1325 1326 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1327 if (nmap_dirfd == -1) { 1328 close_preserve_errno(omap_dirfd); 1329 goto err; 1330 } 1331 1332 /* rename the .virtfs_metadata files */ 1333 ret = qemu_renameat(omap_dirfd, old_name, nmap_dirfd, new_name); 1334 close_preserve_errno(nmap_dirfd); 1335 close_preserve_errno(omap_dirfd); 1336 if (ret < 0 && errno != ENOENT) { 1337 goto err_undo_rename; 1338 } 1339 1340 ret = 0; 1341 } 1342 goto out; 1343 1344 err: 1345 ret = -1; 1346 err_undo_rename: 1347 renameat_preserve_errno(ndirfd, new_name, odirfd, old_name); 1348 out: 1349 close_preserve_errno(ndirfd); 1350 close_preserve_errno(odirfd); 1351 return ret; 1352 } 1353 1354 static void v9fs_path_init_dirname(V9fsPath *path, const char *str) 1355 { 1356 path->data = g_path_get_dirname(str); 1357 path->size = strlen(path->data) + 1; 1358 } 1359 1360 static int local_rename(FsContext *ctx, const char *oldpath, 1361 const char *newpath) 1362 { 1363 int err; 1364 char *oname = g_path_get_basename(oldpath); 1365 char *nname = g_path_get_basename(newpath); 1366 V9fsPath olddir, newdir; 1367 1368 v9fs_path_init_dirname(&olddir, oldpath); 1369 v9fs_path_init_dirname(&newdir, newpath); 1370 1371 err = local_renameat(ctx, &olddir, oname, &newdir, nname); 1372 1373 v9fs_path_free(&newdir); 1374 v9fs_path_free(&olddir); 1375 g_free(nname); 1376 g_free(oname); 1377 1378 return err; 1379 } 1380 1381 static int local_unlinkat(FsContext *ctx, V9fsPath *dir, 1382 const char *name, int flags) 1383 { 1384 int ret; 1385 int dirfd; 1386 1387 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1388 local_is_mapped_file_metadata(ctx, name)) { 1389 errno = EINVAL; 1390 return -1; 1391 } 1392 1393 dirfd = local_opendir_nofollow(ctx, dir->data); 1394 if (dirfd == -1) { 1395 return -1; 1396 } 1397 1398 ret = local_unlinkat_common(ctx, dirfd, name, flags); 1399 close_preserve_errno(dirfd); 1400 return ret; 1401 } 1402 1403 #ifdef FS_IOC_GETVERSION 1404 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, 1405 mode_t st_mode, uint64_t *st_gen) 1406 { 1407 int err; 1408 V9fsFidOpenState fid_open; 1409 1410 /* 1411 * Do not try to open special files like device nodes, fifos etc 1412 * We can get fd for regular files and directories only 1413 */ 1414 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { 1415 errno = ENOTTY; 1416 return -1; 1417 } 1418 err = local_open(ctx, path, O_RDONLY, &fid_open); 1419 if (err < 0) { 1420 return err; 1421 } 1422 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); 1423 local_close(ctx, &fid_open); 1424 return err; 1425 } 1426 #endif 1427 1428 static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp) 1429 { 1430 #ifdef FS_IOC_GETVERSION 1431 struct statfs stbuf; 1432 1433 /* 1434 * use ioc_getversion only if the ioctl is defined 1435 */ 1436 if (fstatfs(data->mountfd, &stbuf) < 0) { 1437 error_setg_errno(errp, errno, 1438 "failed to stat file system at '%s'", ctx->fs_root); 1439 return -1; 1440 } 1441 switch (stbuf.f_type) { 1442 case EXT2_SUPER_MAGIC: 1443 case BTRFS_SUPER_MAGIC: 1444 case REISERFS_SUPER_MAGIC: 1445 case XFS_SUPER_MAGIC: 1446 ctx->exops.get_st_gen = local_ioc_getversion; 1447 break; 1448 } 1449 #endif 1450 return 0; 1451 } 1452 1453 static int local_init(FsContext *ctx, Error **errp) 1454 { 1455 LocalData *data = g_malloc(sizeof(*data)); 1456 1457 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); 1458 if (data->mountfd == -1) { 1459 error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); 1460 goto err; 1461 } 1462 1463 if (local_ioc_getversion_init(ctx, data, errp) < 0) { 1464 close(data->mountfd); 1465 goto err; 1466 } 1467 1468 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { 1469 ctx->xops = passthrough_xattr_ops; 1470 } else if (ctx->export_flags & V9FS_SM_MAPPED) { 1471 ctx->xops = mapped_xattr_ops; 1472 } else if (ctx->export_flags & V9FS_SM_NONE) { 1473 ctx->xops = none_xattr_ops; 1474 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1475 /* 1476 * xattr operation for mapped-file and passthrough 1477 * remain same. 1478 */ 1479 ctx->xops = passthrough_xattr_ops; 1480 } 1481 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; 1482 1483 ctx->private = data; 1484 return 0; 1485 1486 err: 1487 g_free(data); 1488 return -1; 1489 } 1490 1491 static void local_cleanup(FsContext *ctx) 1492 { 1493 LocalData *data = ctx->private; 1494 1495 if (!data) { 1496 return; 1497 } 1498 1499 close(data->mountfd); 1500 g_free(data); 1501 } 1502 1503 static void error_append_security_model_hint(Error *const *errp) 1504 { 1505 error_append_hint(errp, "Valid options are: security_model=" 1506 "[passthrough|mapped-xattr|mapped-file|none]\n"); 1507 } 1508 1509 static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) 1510 { 1511 ERRP_GUARD(); 1512 const char *sec_model = qemu_opt_get(opts, "security_model"); 1513 const char *path = qemu_opt_get(opts, "path"); 1514 const char *multidevs = qemu_opt_get(opts, "multidevs"); 1515 1516 if (!sec_model) { 1517 error_setg(errp, "security_model property not set"); 1518 error_append_security_model_hint(errp); 1519 return -1; 1520 } 1521 1522 if (!strcmp(sec_model, "passthrough")) { 1523 fse->export_flags |= V9FS_SM_PASSTHROUGH; 1524 } else if (!strcmp(sec_model, "mapped") || 1525 !strcmp(sec_model, "mapped-xattr")) { 1526 fse->export_flags |= V9FS_SM_MAPPED; 1527 } else if (!strcmp(sec_model, "none")) { 1528 fse->export_flags |= V9FS_SM_NONE; 1529 } else if (!strcmp(sec_model, "mapped-file")) { 1530 fse->export_flags |= V9FS_SM_MAPPED_FILE; 1531 } else { 1532 error_setg(errp, "invalid security_model property '%s'", sec_model); 1533 error_append_security_model_hint(errp); 1534 return -1; 1535 } 1536 1537 if (multidevs) { 1538 if (!strcmp(multidevs, "remap")) { 1539 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; 1540 fse->export_flags |= V9FS_REMAP_INODES; 1541 } else if (!strcmp(multidevs, "forbid")) { 1542 fse->export_flags &= ~V9FS_REMAP_INODES; 1543 fse->export_flags |= V9FS_FORBID_MULTIDEVS; 1544 } else if (!strcmp(multidevs, "warn")) { 1545 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; 1546 fse->export_flags &= ~V9FS_REMAP_INODES; 1547 } else { 1548 error_setg(errp, "invalid multidevs property '%s'", 1549 multidevs); 1550 error_append_hint(errp, "Valid options are: multidevs=" 1551 "[remap|forbid|warn]\n"); 1552 return -1; 1553 } 1554 } else { 1555 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; 1556 fse->export_flags |= V9FS_REMAP_INODES; 1557 } 1558 1559 if (!path) { 1560 error_setg(errp, "path property not set"); 1561 return -1; 1562 } 1563 1564 if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { 1565 error_prepend(errp, "invalid throttle configuration: "); 1566 return -1; 1567 } 1568 1569 if (fse->export_flags & V9FS_SM_MAPPED || 1570 fse->export_flags & V9FS_SM_MAPPED_FILE) { 1571 fse->fmode = 1572 qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; 1573 fse->dmode = 1574 qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; 1575 } else { 1576 if (qemu_opt_find(opts, "fmode")) { 1577 error_setg(errp, "fmode is only valid for mapped security modes"); 1578 return -1; 1579 } 1580 if (qemu_opt_find(opts, "dmode")) { 1581 error_setg(errp, "dmode is only valid for mapped security modes"); 1582 return -1; 1583 } 1584 } 1585 1586 fse->path = g_strdup(path); 1587 1588 return 0; 1589 } 1590 1591 static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) 1592 { 1593 return 1594 (fid_type == P9_FID_FILE && fs->fd != -1) || 1595 (fid_type == P9_FID_DIR && fs->dir.stream != NULL); 1596 } 1597 1598 FileOperations local_ops = { 1599 .parse_opts = local_parse_opts, 1600 .init = local_init, 1601 .cleanup = local_cleanup, 1602 .lstat = local_lstat, 1603 .readlink = local_readlink, 1604 .close = local_close, 1605 .closedir = local_closedir, 1606 .open = local_open, 1607 .opendir = local_opendir, 1608 .rewinddir = local_rewinddir, 1609 .telldir = local_telldir, 1610 .readdir = local_readdir, 1611 .seekdir = local_seekdir, 1612 .preadv = local_preadv, 1613 .pwritev = local_pwritev, 1614 .chmod = local_chmod, 1615 .mknod = local_mknod, 1616 .mkdir = local_mkdir, 1617 .fstat = local_fstat, 1618 .open2 = local_open2, 1619 .symlink = local_symlink, 1620 .link = local_link, 1621 .truncate = local_truncate, 1622 .rename = local_rename, 1623 .chown = local_chown, 1624 .utimensat = local_utimensat, 1625 .remove = local_remove, 1626 .fsync = local_fsync, 1627 .statfs = local_statfs, 1628 .lgetxattr = local_lgetxattr, 1629 .llistxattr = local_llistxattr, 1630 .lsetxattr = local_lsetxattr, 1631 .lremovexattr = local_lremovexattr, 1632 .name_to_path = local_name_to_path, 1633 .renameat = local_renameat, 1634 .unlinkat = local_unlinkat, 1635 .has_valid_file_handle = local_has_valid_file_handle, 1636 .ftruncate = local_ftruncate, 1637 .futimens = local_futimens, 1638 }; 1639