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 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 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 = 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 = 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 (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 do { 474 tsize = read(fd, (void *)buf, bufsz); 475 } while (tsize == -1 && errno == EINTR); 476 close_preserve_errno(fd); 477 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 478 (fs_ctx->export_flags & V9FS_SM_NONE)) { 479 char *dirpath = g_path_get_dirname(fs_path->data); 480 char *name = g_path_get_basename(fs_path->data); 481 int dirfd; 482 483 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 484 if (dirfd == -1) { 485 goto out; 486 } 487 488 tsize = readlinkat(dirfd, name, buf, bufsz); 489 close_preserve_errno(dirfd); 490 out: 491 g_free(name); 492 g_free(dirpath); 493 } 494 return tsize; 495 } 496 497 static int local_close(FsContext *ctx, V9fsFidOpenState *fs) 498 { 499 return close(fs->fd); 500 } 501 502 static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) 503 { 504 return closedir(fs->dir.stream); 505 } 506 507 static int local_open(FsContext *ctx, V9fsPath *fs_path, 508 int flags, V9fsFidOpenState *fs) 509 { 510 int fd; 511 512 fd = local_open_nofollow(ctx, fs_path->data, flags, 0); 513 if (fd == -1) { 514 return -1; 515 } 516 fs->fd = fd; 517 return fs->fd; 518 } 519 520 static int local_opendir(FsContext *ctx, 521 V9fsPath *fs_path, V9fsFidOpenState *fs) 522 { 523 int dirfd; 524 DIR *stream; 525 526 dirfd = local_opendir_nofollow(ctx, fs_path->data); 527 if (dirfd == -1) { 528 return -1; 529 } 530 531 stream = fdopendir(dirfd); 532 if (!stream) { 533 close(dirfd); 534 return -1; 535 } 536 fs->dir.stream = stream; 537 return 0; 538 } 539 540 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) 541 { 542 rewinddir(fs->dir.stream); 543 } 544 545 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) 546 { 547 return telldir(fs->dir.stream); 548 } 549 550 static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name) 551 { 552 return 553 !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE); 554 } 555 556 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) 557 { 558 struct dirent *entry; 559 560 again: 561 entry = readdir(fs->dir.stream); 562 if (!entry) { 563 return NULL; 564 } 565 566 if (ctx->export_flags & V9FS_SM_MAPPED) { 567 entry->d_type = DT_UNKNOWN; 568 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 569 if (local_is_mapped_file_metadata(ctx, entry->d_name)) { 570 /* skip the meta data */ 571 goto again; 572 } 573 entry->d_type = DT_UNKNOWN; 574 } 575 576 return entry; 577 } 578 579 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) 580 { 581 seekdir(fs->dir.stream, off); 582 } 583 584 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, 585 const struct iovec *iov, 586 int iovcnt, off_t offset) 587 { 588 #ifdef CONFIG_PREADV 589 return preadv(fs->fd, iov, iovcnt, offset); 590 #else 591 int err = lseek(fs->fd, offset, SEEK_SET); 592 if (err == -1) { 593 return err; 594 } else { 595 return readv(fs->fd, iov, iovcnt); 596 } 597 #endif 598 } 599 600 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, 601 const struct iovec *iov, 602 int iovcnt, off_t offset) 603 { 604 ssize_t ret; 605 #ifdef CONFIG_PREADV 606 ret = pwritev(fs->fd, iov, iovcnt, offset); 607 #else 608 int err = lseek(fs->fd, offset, SEEK_SET); 609 if (err == -1) { 610 return err; 611 } else { 612 ret = writev(fs->fd, iov, iovcnt); 613 } 614 #endif 615 #ifdef CONFIG_SYNC_FILE_RANGE 616 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { 617 /* 618 * Initiate a writeback. This is not a data integrity sync. 619 * We want to ensure that we don't leave dirty pages in the cache 620 * after write when writeout=immediate is sepcified. 621 */ 622 sync_file_range(fs->fd, offset, ret, 623 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); 624 } 625 #endif 626 return ret; 627 } 628 629 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 630 { 631 char *dirpath = g_path_get_dirname(fs_path->data); 632 char *name = g_path_get_basename(fs_path->data); 633 int ret = -1; 634 int dirfd; 635 636 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 637 if (dirfd == -1) { 638 goto out; 639 } 640 641 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 642 ret = local_set_xattrat(dirfd, name, credp); 643 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 644 ret = local_set_mapped_file_attrat(dirfd, name, credp); 645 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 646 fs_ctx->export_flags & V9FS_SM_NONE) { 647 ret = fchmodat_nofollow(dirfd, name, credp->fc_mode); 648 } 649 close_preserve_errno(dirfd); 650 651 out: 652 g_free(dirpath); 653 g_free(name); 654 return ret; 655 } 656 657 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, 658 const char *name, FsCred *credp) 659 { 660 int err = -1; 661 int dirfd; 662 663 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 664 local_is_mapped_file_metadata(fs_ctx, name)) { 665 errno = EINVAL; 666 return -1; 667 } 668 669 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 670 if (dirfd == -1) { 671 return -1; 672 } 673 674 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 675 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 676 err = mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0); 677 if (err == -1) { 678 goto out; 679 } 680 681 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 682 err = local_set_xattrat(dirfd, name, credp); 683 } else { 684 err = local_set_mapped_file_attrat(dirfd, name, credp); 685 } 686 if (err == -1) { 687 goto err_end; 688 } 689 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 690 fs_ctx->export_flags & V9FS_SM_NONE) { 691 err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); 692 if (err == -1) { 693 goto out; 694 } 695 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 696 if (err == -1) { 697 goto err_end; 698 } 699 } 700 goto out; 701 702 err_end: 703 unlinkat_preserve_errno(dirfd, name, 0); 704 out: 705 close_preserve_errno(dirfd); 706 return err; 707 } 708 709 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, 710 const char *name, FsCred *credp) 711 { 712 int err = -1; 713 int dirfd; 714 715 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 716 local_is_mapped_file_metadata(fs_ctx, name)) { 717 errno = EINVAL; 718 return -1; 719 } 720 721 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 722 if (dirfd == -1) { 723 return -1; 724 } 725 726 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 727 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 728 err = mkdirat(dirfd, name, fs_ctx->dmode); 729 if (err == -1) { 730 goto out; 731 } 732 credp->fc_mode = credp->fc_mode | S_IFDIR; 733 734 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 735 err = local_set_xattrat(dirfd, name, credp); 736 } else { 737 err = local_set_mapped_file_attrat(dirfd, name, credp); 738 } 739 if (err == -1) { 740 goto err_end; 741 } 742 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 743 fs_ctx->export_flags & V9FS_SM_NONE) { 744 err = mkdirat(dirfd, name, credp->fc_mode); 745 if (err == -1) { 746 goto out; 747 } 748 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 749 if (err == -1) { 750 goto err_end; 751 } 752 } 753 goto out; 754 755 err_end: 756 unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR); 757 out: 758 close_preserve_errno(dirfd); 759 return err; 760 } 761 762 static int local_fstat(FsContext *fs_ctx, int fid_type, 763 V9fsFidOpenState *fs, struct stat *stbuf) 764 { 765 int err, fd; 766 767 if (fid_type == P9_FID_DIR) { 768 fd = dirfd(fs->dir.stream); 769 } else { 770 fd = fs->fd; 771 } 772 773 err = fstat(fd, stbuf); 774 if (err) { 775 return err; 776 } 777 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 778 /* Actual credentials are part of extended attrs */ 779 uid_t tmp_uid; 780 gid_t tmp_gid; 781 mode_t tmp_mode; 782 dev_t tmp_dev; 783 784 if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { 785 stbuf->st_uid = le32_to_cpu(tmp_uid); 786 } 787 if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { 788 stbuf->st_gid = le32_to_cpu(tmp_gid); 789 } 790 if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { 791 stbuf->st_mode = le32_to_cpu(tmp_mode); 792 } 793 if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { 794 stbuf->st_rdev = le64_to_cpu(tmp_dev); 795 } 796 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 797 errno = EOPNOTSUPP; 798 return -1; 799 } 800 return err; 801 } 802 803 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, 804 int flags, FsCred *credp, V9fsFidOpenState *fs) 805 { 806 int fd = -1; 807 int err = -1; 808 int dirfd; 809 810 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 811 local_is_mapped_file_metadata(fs_ctx, name)) { 812 errno = EINVAL; 813 return -1; 814 } 815 816 /* 817 * Mark all the open to not follow symlinks 818 */ 819 flags |= O_NOFOLLOW; 820 821 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 822 if (dirfd == -1) { 823 return -1; 824 } 825 826 /* Determine the security model */ 827 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 828 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 829 fd = openat_file(dirfd, name, flags, fs_ctx->fmode); 830 if (fd == -1) { 831 goto out; 832 } 833 credp->fc_mode = credp->fc_mode | S_IFREG; 834 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 835 /* Set cleint credentials in xattr */ 836 err = local_set_xattrat(dirfd, name, credp); 837 } else { 838 err = local_set_mapped_file_attrat(dirfd, name, credp); 839 } 840 if (err == -1) { 841 goto err_end; 842 } 843 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 844 (fs_ctx->export_flags & V9FS_SM_NONE)) { 845 fd = openat_file(dirfd, name, flags, credp->fc_mode); 846 if (fd == -1) { 847 goto out; 848 } 849 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 850 if (err == -1) { 851 goto err_end; 852 } 853 } 854 err = fd; 855 fs->fd = fd; 856 goto out; 857 858 err_end: 859 unlinkat_preserve_errno(dirfd, name, 860 flags & O_DIRECTORY ? AT_REMOVEDIR : 0); 861 close_preserve_errno(fd); 862 out: 863 close_preserve_errno(dirfd); 864 return err; 865 } 866 867 868 static int local_symlink(FsContext *fs_ctx, const char *oldpath, 869 V9fsPath *dir_path, const char *name, FsCred *credp) 870 { 871 int err = -1; 872 int dirfd; 873 874 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && 875 local_is_mapped_file_metadata(fs_ctx, name)) { 876 errno = EINVAL; 877 return -1; 878 } 879 880 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 881 if (dirfd == -1) { 882 return -1; 883 } 884 885 /* Determine the security model */ 886 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 887 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 888 int fd; 889 ssize_t oldpath_size, write_size; 890 891 fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR, 892 fs_ctx->fmode); 893 if (fd == -1) { 894 goto out; 895 } 896 /* Write the oldpath (target) to the file. */ 897 oldpath_size = strlen(oldpath); 898 do { 899 write_size = write(fd, (void *)oldpath, oldpath_size); 900 } while (write_size == -1 && errno == EINTR); 901 close_preserve_errno(fd); 902 903 if (write_size != oldpath_size) { 904 goto err_end; 905 } 906 /* Set cleint credentials in symlink's xattr */ 907 credp->fc_mode = credp->fc_mode | S_IFLNK; 908 909 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 910 err = local_set_xattrat(dirfd, name, credp); 911 } else { 912 err = local_set_mapped_file_attrat(dirfd, name, credp); 913 } 914 if (err == -1) { 915 goto err_end; 916 } 917 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 918 fs_ctx->export_flags & V9FS_SM_NONE) { 919 err = symlinkat(oldpath, dirfd, name); 920 if (err) { 921 goto out; 922 } 923 err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 924 AT_SYMLINK_NOFOLLOW); 925 if (err == -1) { 926 /* 927 * If we fail to change ownership and if we are 928 * using security model none. Ignore the error 929 */ 930 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 931 goto err_end; 932 } else { 933 err = 0; 934 } 935 } 936 } 937 goto out; 938 939 err_end: 940 unlinkat_preserve_errno(dirfd, name, 0); 941 out: 942 close_preserve_errno(dirfd); 943 return err; 944 } 945 946 static int local_link(FsContext *ctx, V9fsPath *oldpath, 947 V9fsPath *dirpath, const char *name) 948 { 949 char *odirpath = g_path_get_dirname(oldpath->data); 950 char *oname = g_path_get_basename(oldpath->data); 951 int ret = -1; 952 int odirfd, ndirfd; 953 954 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 955 local_is_mapped_file_metadata(ctx, name)) { 956 errno = EINVAL; 957 goto out; 958 } 959 960 odirfd = local_opendir_nofollow(ctx, odirpath); 961 if (odirfd == -1) { 962 goto out; 963 } 964 965 ndirfd = local_opendir_nofollow(ctx, dirpath->data); 966 if (ndirfd == -1) { 967 close_preserve_errno(odirfd); 968 goto out; 969 } 970 971 ret = linkat(odirfd, oname, ndirfd, name, 0); 972 if (ret < 0) { 973 goto out_close; 974 } 975 976 /* now link the virtfs_metadata files */ 977 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 978 int omap_dirfd, nmap_dirfd; 979 980 ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 981 if (ret < 0 && errno != EEXIST) { 982 goto err_undo_link; 983 } 984 985 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 986 if (omap_dirfd == -1) { 987 goto err; 988 } 989 990 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 991 if (nmap_dirfd == -1) { 992 close_preserve_errno(omap_dirfd); 993 goto err; 994 } 995 996 ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0); 997 close_preserve_errno(nmap_dirfd); 998 close_preserve_errno(omap_dirfd); 999 if (ret < 0 && errno != ENOENT) { 1000 goto err_undo_link; 1001 } 1002 1003 ret = 0; 1004 } 1005 goto out_close; 1006 1007 err: 1008 ret = -1; 1009 err_undo_link: 1010 unlinkat_preserve_errno(ndirfd, name, 0); 1011 out_close: 1012 close_preserve_errno(ndirfd); 1013 close_preserve_errno(odirfd); 1014 out: 1015 g_free(oname); 1016 g_free(odirpath); 1017 return ret; 1018 } 1019 1020 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) 1021 { 1022 int fd, ret; 1023 1024 fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); 1025 if (fd == -1) { 1026 return -1; 1027 } 1028 ret = ftruncate(fd, size); 1029 close_preserve_errno(fd); 1030 return ret; 1031 } 1032 1033 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 1034 { 1035 char *dirpath = g_path_get_dirname(fs_path->data); 1036 char *name = g_path_get_basename(fs_path->data); 1037 int ret = -1; 1038 int dirfd; 1039 1040 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 1041 if (dirfd == -1) { 1042 goto out; 1043 } 1044 1045 if ((credp->fc_uid == -1 && credp->fc_gid == -1) || 1046 (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 1047 (fs_ctx->export_flags & V9FS_SM_NONE)) { 1048 ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 1049 AT_SYMLINK_NOFOLLOW); 1050 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 1051 ret = local_set_xattrat(dirfd, name, credp); 1052 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1053 ret = local_set_mapped_file_attrat(dirfd, name, credp); 1054 } 1055 1056 close_preserve_errno(dirfd); 1057 out: 1058 g_free(name); 1059 g_free(dirpath); 1060 return ret; 1061 } 1062 1063 static int local_utimensat(FsContext *s, V9fsPath *fs_path, 1064 const struct timespec *buf) 1065 { 1066 char *dirpath = g_path_get_dirname(fs_path->data); 1067 char *name = g_path_get_basename(fs_path->data); 1068 int dirfd, ret = -1; 1069 1070 dirfd = local_opendir_nofollow(s, dirpath); 1071 if (dirfd == -1) { 1072 goto out; 1073 } 1074 1075 ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); 1076 close_preserve_errno(dirfd); 1077 out: 1078 g_free(dirpath); 1079 g_free(name); 1080 return ret; 1081 } 1082 1083 static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, 1084 int flags) 1085 { 1086 int ret; 1087 1088 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1089 int map_dirfd; 1090 1091 /* We need to remove the metadata as well: 1092 * - the metadata directory if we're removing a directory 1093 * - the metadata file in the parent's metadata directory 1094 * 1095 * If any of these are missing (ie, ENOENT) then we're probably 1096 * trying to remove something that wasn't created in mapped-file 1097 * mode. We just ignore the error. 1098 */ 1099 if (flags == AT_REMOVEDIR) { 1100 int fd; 1101 1102 fd = openat_dir(dirfd, name); 1103 if (fd == -1) { 1104 return -1; 1105 } 1106 ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); 1107 close_preserve_errno(fd); 1108 if (ret < 0 && errno != ENOENT) { 1109 return -1; 1110 } 1111 } 1112 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 1113 if (map_dirfd != -1) { 1114 ret = unlinkat(map_dirfd, name, 0); 1115 close_preserve_errno(map_dirfd); 1116 if (ret < 0 && errno != ENOENT) { 1117 return -1; 1118 } 1119 } else if (errno != ENOENT) { 1120 return -1; 1121 } 1122 } 1123 1124 return unlinkat(dirfd, name, flags); 1125 } 1126 1127 static int local_remove(FsContext *ctx, const char *path) 1128 { 1129 struct stat stbuf; 1130 char *dirpath = g_path_get_dirname(path); 1131 char *name = g_path_get_basename(path); 1132 int flags = 0; 1133 int dirfd; 1134 int err = -1; 1135 1136 dirfd = local_opendir_nofollow(ctx, dirpath); 1137 if (dirfd == -1) { 1138 goto out; 1139 } 1140 1141 if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { 1142 goto err_out; 1143 } 1144 1145 if (S_ISDIR(stbuf.st_mode)) { 1146 flags |= AT_REMOVEDIR; 1147 } 1148 1149 err = local_unlinkat_common(ctx, dirfd, name, flags); 1150 err_out: 1151 close_preserve_errno(dirfd); 1152 out: 1153 g_free(name); 1154 g_free(dirpath); 1155 return err; 1156 } 1157 1158 static int local_fsync(FsContext *ctx, int fid_type, 1159 V9fsFidOpenState *fs, int datasync) 1160 { 1161 int fd; 1162 1163 if (fid_type == P9_FID_DIR) { 1164 fd = dirfd(fs->dir.stream); 1165 } else { 1166 fd = fs->fd; 1167 } 1168 1169 if (datasync) { 1170 return qemu_fdatasync(fd); 1171 } else { 1172 return fsync(fd); 1173 } 1174 } 1175 1176 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) 1177 { 1178 int fd, ret; 1179 1180 fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0); 1181 if (fd == -1) { 1182 return -1; 1183 } 1184 ret = fstatfs(fd, stbuf); 1185 close_preserve_errno(fd); 1186 return ret; 1187 } 1188 1189 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, 1190 const char *name, void *value, size_t size) 1191 { 1192 char *path = fs_path->data; 1193 1194 return v9fs_get_xattr(ctx, path, name, value, size); 1195 } 1196 1197 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, 1198 void *value, size_t size) 1199 { 1200 char *path = fs_path->data; 1201 1202 return v9fs_list_xattr(ctx, path, value, size); 1203 } 1204 1205 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, 1206 void *value, size_t size, int flags) 1207 { 1208 char *path = fs_path->data; 1209 1210 return v9fs_set_xattr(ctx, path, name, value, size, flags); 1211 } 1212 1213 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, 1214 const char *name) 1215 { 1216 char *path = fs_path->data; 1217 1218 return v9fs_remove_xattr(ctx, path, name); 1219 } 1220 1221 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, 1222 const char *name, V9fsPath *target) 1223 { 1224 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1225 local_is_mapped_file_metadata(ctx, name)) { 1226 errno = EINVAL; 1227 return -1; 1228 } 1229 1230 if (dir_path) { 1231 if (!strcmp(name, ".")) { 1232 /* "." relative to "foo/bar" is "foo/bar" */ 1233 v9fs_path_copy(target, dir_path); 1234 } else if (!strcmp(name, "..")) { 1235 if (!strcmp(dir_path->data, ".")) { 1236 /* ".." relative to the root is "." */ 1237 v9fs_path_sprintf(target, "."); 1238 } else { 1239 char *tmp = g_path_get_dirname(dir_path->data); 1240 /* Symbolic links are resolved by the client. We can assume 1241 * that ".." relative to "foo/bar" is equivalent to "foo" 1242 */ 1243 v9fs_path_sprintf(target, "%s", tmp); 1244 g_free(tmp); 1245 } 1246 } else { 1247 assert(!strchr(name, '/')); 1248 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); 1249 } 1250 } else if (!strcmp(name, "/") || !strcmp(name, ".") || 1251 !strcmp(name, "..")) { 1252 /* This is the root fid */ 1253 v9fs_path_sprintf(target, "."); 1254 } else { 1255 assert(!strchr(name, '/')); 1256 v9fs_path_sprintf(target, "./%s", name); 1257 } 1258 return 0; 1259 } 1260 1261 static int local_renameat(FsContext *ctx, V9fsPath *olddir, 1262 const char *old_name, V9fsPath *newdir, 1263 const char *new_name) 1264 { 1265 int ret; 1266 int odirfd, ndirfd; 1267 1268 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1269 (local_is_mapped_file_metadata(ctx, old_name) || 1270 local_is_mapped_file_metadata(ctx, new_name))) { 1271 errno = EINVAL; 1272 return -1; 1273 } 1274 1275 odirfd = local_opendir_nofollow(ctx, olddir->data); 1276 if (odirfd == -1) { 1277 return -1; 1278 } 1279 1280 ndirfd = local_opendir_nofollow(ctx, newdir->data); 1281 if (ndirfd == -1) { 1282 close_preserve_errno(odirfd); 1283 return -1; 1284 } 1285 1286 ret = renameat(odirfd, old_name, ndirfd, new_name); 1287 if (ret < 0) { 1288 goto out; 1289 } 1290 1291 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1292 int omap_dirfd, nmap_dirfd; 1293 1294 ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 1295 if (ret < 0 && errno != EEXIST) { 1296 goto err_undo_rename; 1297 } 1298 1299 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 1300 if (omap_dirfd == -1) { 1301 goto err; 1302 } 1303 1304 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1305 if (nmap_dirfd == -1) { 1306 close_preserve_errno(omap_dirfd); 1307 goto err; 1308 } 1309 1310 /* rename the .virtfs_metadata files */ 1311 ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name); 1312 close_preserve_errno(nmap_dirfd); 1313 close_preserve_errno(omap_dirfd); 1314 if (ret < 0 && errno != ENOENT) { 1315 goto err_undo_rename; 1316 } 1317 1318 ret = 0; 1319 } 1320 goto out; 1321 1322 err: 1323 ret = -1; 1324 err_undo_rename: 1325 renameat_preserve_errno(ndirfd, new_name, odirfd, old_name); 1326 out: 1327 close_preserve_errno(ndirfd); 1328 close_preserve_errno(odirfd); 1329 return ret; 1330 } 1331 1332 static void v9fs_path_init_dirname(V9fsPath *path, const char *str) 1333 { 1334 path->data = g_path_get_dirname(str); 1335 path->size = strlen(path->data) + 1; 1336 } 1337 1338 static int local_rename(FsContext *ctx, const char *oldpath, 1339 const char *newpath) 1340 { 1341 int err; 1342 char *oname = g_path_get_basename(oldpath); 1343 char *nname = g_path_get_basename(newpath); 1344 V9fsPath olddir, newdir; 1345 1346 v9fs_path_init_dirname(&olddir, oldpath); 1347 v9fs_path_init_dirname(&newdir, newpath); 1348 1349 err = local_renameat(ctx, &olddir, oname, &newdir, nname); 1350 1351 v9fs_path_free(&newdir); 1352 v9fs_path_free(&olddir); 1353 g_free(nname); 1354 g_free(oname); 1355 1356 return err; 1357 } 1358 1359 static int local_unlinkat(FsContext *ctx, V9fsPath *dir, 1360 const char *name, int flags) 1361 { 1362 int ret; 1363 int dirfd; 1364 1365 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1366 local_is_mapped_file_metadata(ctx, name)) { 1367 errno = EINVAL; 1368 return -1; 1369 } 1370 1371 dirfd = local_opendir_nofollow(ctx, dir->data); 1372 if (dirfd == -1) { 1373 return -1; 1374 } 1375 1376 ret = local_unlinkat_common(ctx, dirfd, name, flags); 1377 close_preserve_errno(dirfd); 1378 return ret; 1379 } 1380 1381 #ifdef FS_IOC_GETVERSION 1382 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, 1383 mode_t st_mode, uint64_t *st_gen) 1384 { 1385 int err; 1386 V9fsFidOpenState fid_open; 1387 1388 /* 1389 * Do not try to open special files like device nodes, fifos etc 1390 * We can get fd for regular files and directories only 1391 */ 1392 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { 1393 errno = ENOTTY; 1394 return -1; 1395 } 1396 err = local_open(ctx, path, O_RDONLY, &fid_open); 1397 if (err < 0) { 1398 return err; 1399 } 1400 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); 1401 local_close(ctx, &fid_open); 1402 return err; 1403 } 1404 #endif 1405 1406 static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp) 1407 { 1408 #ifdef FS_IOC_GETVERSION 1409 struct statfs stbuf; 1410 1411 /* 1412 * use ioc_getversion only if the ioctl is definied 1413 */ 1414 if (fstatfs(data->mountfd, &stbuf) < 0) { 1415 error_setg_errno(errp, errno, 1416 "failed to stat file system at '%s'", ctx->fs_root); 1417 return -1; 1418 } 1419 switch (stbuf.f_type) { 1420 case EXT2_SUPER_MAGIC: 1421 case BTRFS_SUPER_MAGIC: 1422 case REISERFS_SUPER_MAGIC: 1423 case XFS_SUPER_MAGIC: 1424 ctx->exops.get_st_gen = local_ioc_getversion; 1425 break; 1426 } 1427 #endif 1428 return 0; 1429 } 1430 1431 static int local_init(FsContext *ctx, Error **errp) 1432 { 1433 LocalData *data = g_malloc(sizeof(*data)); 1434 1435 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); 1436 if (data->mountfd == -1) { 1437 error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); 1438 goto err; 1439 } 1440 1441 if (local_ioc_getversion_init(ctx, data, errp) < 0) { 1442 close(data->mountfd); 1443 goto err; 1444 } 1445 1446 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { 1447 ctx->xops = passthrough_xattr_ops; 1448 } else if (ctx->export_flags & V9FS_SM_MAPPED) { 1449 ctx->xops = mapped_xattr_ops; 1450 } else if (ctx->export_flags & V9FS_SM_NONE) { 1451 ctx->xops = none_xattr_ops; 1452 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1453 /* 1454 * xattr operation for mapped-file and passthrough 1455 * remain same. 1456 */ 1457 ctx->xops = passthrough_xattr_ops; 1458 } 1459 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; 1460 1461 ctx->private = data; 1462 return 0; 1463 1464 err: 1465 g_free(data); 1466 return -1; 1467 } 1468 1469 static void local_cleanup(FsContext *ctx) 1470 { 1471 LocalData *data = ctx->private; 1472 1473 if (!data) { 1474 return; 1475 } 1476 1477 close(data->mountfd); 1478 g_free(data); 1479 } 1480 1481 static void error_append_security_model_hint(Error *const *errp) 1482 { 1483 error_append_hint(errp, "Valid options are: security_model=" 1484 "[passthrough|mapped-xattr|mapped-file|none]\n"); 1485 } 1486 1487 static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) 1488 { 1489 ERRP_GUARD(); 1490 const char *sec_model = qemu_opt_get(opts, "security_model"); 1491 const char *path = qemu_opt_get(opts, "path"); 1492 const char *multidevs = qemu_opt_get(opts, "multidevs"); 1493 1494 if (!sec_model) { 1495 error_setg(errp, "security_model property not set"); 1496 error_append_security_model_hint(errp); 1497 return -1; 1498 } 1499 1500 if (!strcmp(sec_model, "passthrough")) { 1501 fse->export_flags |= V9FS_SM_PASSTHROUGH; 1502 } else if (!strcmp(sec_model, "mapped") || 1503 !strcmp(sec_model, "mapped-xattr")) { 1504 fse->export_flags |= V9FS_SM_MAPPED; 1505 } else if (!strcmp(sec_model, "none")) { 1506 fse->export_flags |= V9FS_SM_NONE; 1507 } else if (!strcmp(sec_model, "mapped-file")) { 1508 fse->export_flags |= V9FS_SM_MAPPED_FILE; 1509 } else { 1510 error_setg(errp, "invalid security_model property '%s'", sec_model); 1511 error_append_security_model_hint(errp); 1512 return -1; 1513 } 1514 1515 if (multidevs) { 1516 if (!strcmp(multidevs, "remap")) { 1517 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; 1518 fse->export_flags |= V9FS_REMAP_INODES; 1519 } else if (!strcmp(multidevs, "forbid")) { 1520 fse->export_flags &= ~V9FS_REMAP_INODES; 1521 fse->export_flags |= V9FS_FORBID_MULTIDEVS; 1522 } else if (!strcmp(multidevs, "warn")) { 1523 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; 1524 fse->export_flags &= ~V9FS_REMAP_INODES; 1525 } else { 1526 error_setg(errp, "invalid multidevs property '%s'", 1527 multidevs); 1528 error_append_hint(errp, "Valid options are: multidevs=" 1529 "[remap|forbid|warn]\n"); 1530 return -1; 1531 } 1532 } 1533 1534 if (!path) { 1535 error_setg(errp, "path property not set"); 1536 return -1; 1537 } 1538 1539 if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { 1540 error_prepend(errp, "invalid throttle configuration: "); 1541 return -1; 1542 } 1543 1544 if (fse->export_flags & V9FS_SM_MAPPED || 1545 fse->export_flags & V9FS_SM_MAPPED_FILE) { 1546 fse->fmode = 1547 qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; 1548 fse->dmode = 1549 qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; 1550 } else { 1551 if (qemu_opt_find(opts, "fmode")) { 1552 error_setg(errp, "fmode is only valid for mapped security modes"); 1553 return -1; 1554 } 1555 if (qemu_opt_find(opts, "dmode")) { 1556 error_setg(errp, "dmode is only valid for mapped security modes"); 1557 return -1; 1558 } 1559 } 1560 1561 fse->path = g_strdup(path); 1562 1563 return 0; 1564 } 1565 1566 FileOperations local_ops = { 1567 .parse_opts = local_parse_opts, 1568 .init = local_init, 1569 .cleanup = local_cleanup, 1570 .lstat = local_lstat, 1571 .readlink = local_readlink, 1572 .close = local_close, 1573 .closedir = local_closedir, 1574 .open = local_open, 1575 .opendir = local_opendir, 1576 .rewinddir = local_rewinddir, 1577 .telldir = local_telldir, 1578 .readdir = local_readdir, 1579 .seekdir = local_seekdir, 1580 .preadv = local_preadv, 1581 .pwritev = local_pwritev, 1582 .chmod = local_chmod, 1583 .mknod = local_mknod, 1584 .mkdir = local_mkdir, 1585 .fstat = local_fstat, 1586 .open2 = local_open2, 1587 .symlink = local_symlink, 1588 .link = local_link, 1589 .truncate = local_truncate, 1590 .rename = local_rename, 1591 .chown = local_chown, 1592 .utimensat = local_utimensat, 1593 .remove = local_remove, 1594 .fsync = local_fsync, 1595 .statfs = local_statfs, 1596 .lgetxattr = local_lgetxattr, 1597 .llistxattr = local_llistxattr, 1598 .lsetxattr = local_lsetxattr, 1599 .lremovexattr = local_lremovexattr, 1600 .name_to_path = local_name_to_path, 1601 .renameat = local_renameat, 1602 .unlinkat = local_unlinkat, 1603 }; 1604