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