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