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 return -1; 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 = -1; 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 goto err_out; 1098 } 1099 ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); 1100 close_preserve_errno(fd); 1101 if (ret < 0 && errno != ENOENT) { 1102 goto err_out; 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 goto err_out; 1111 } 1112 } else if (errno != ENOENT) { 1113 goto err_out; 1114 } 1115 } 1116 1117 ret = unlinkat(dirfd, name, flags); 1118 err_out: 1119 return ret; 1120 } 1121 1122 static int local_remove(FsContext *ctx, const char *path) 1123 { 1124 struct stat stbuf; 1125 char *dirpath = g_path_get_dirname(path); 1126 char *name = g_path_get_basename(path); 1127 int flags = 0; 1128 int dirfd; 1129 int err = -1; 1130 1131 dirfd = local_opendir_nofollow(ctx, dirpath); 1132 if (dirfd == -1) { 1133 goto out; 1134 } 1135 1136 if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { 1137 goto err_out; 1138 } 1139 1140 if (S_ISDIR(stbuf.st_mode)) { 1141 flags |= AT_REMOVEDIR; 1142 } 1143 1144 err = local_unlinkat_common(ctx, dirfd, name, flags); 1145 err_out: 1146 close_preserve_errno(dirfd); 1147 out: 1148 g_free(name); 1149 g_free(dirpath); 1150 return err; 1151 } 1152 1153 static int local_fsync(FsContext *ctx, int fid_type, 1154 V9fsFidOpenState *fs, int datasync) 1155 { 1156 int fd; 1157 1158 if (fid_type == P9_FID_DIR) { 1159 fd = dirfd(fs->dir.stream); 1160 } else { 1161 fd = fs->fd; 1162 } 1163 1164 if (datasync) { 1165 return qemu_fdatasync(fd); 1166 } else { 1167 return fsync(fd); 1168 } 1169 } 1170 1171 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) 1172 { 1173 int fd, ret; 1174 1175 fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0); 1176 if (fd == -1) { 1177 return -1; 1178 } 1179 ret = fstatfs(fd, stbuf); 1180 close_preserve_errno(fd); 1181 return ret; 1182 } 1183 1184 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, 1185 const char *name, void *value, size_t size) 1186 { 1187 char *path = fs_path->data; 1188 1189 return v9fs_get_xattr(ctx, path, name, value, size); 1190 } 1191 1192 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, 1193 void *value, size_t size) 1194 { 1195 char *path = fs_path->data; 1196 1197 return v9fs_list_xattr(ctx, path, value, size); 1198 } 1199 1200 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, 1201 void *value, size_t size, int flags) 1202 { 1203 char *path = fs_path->data; 1204 1205 return v9fs_set_xattr(ctx, path, name, value, size, flags); 1206 } 1207 1208 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, 1209 const char *name) 1210 { 1211 char *path = fs_path->data; 1212 1213 return v9fs_remove_xattr(ctx, path, name); 1214 } 1215 1216 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, 1217 const char *name, V9fsPath *target) 1218 { 1219 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1220 local_is_mapped_file_metadata(ctx, name)) { 1221 errno = EINVAL; 1222 return -1; 1223 } 1224 1225 if (dir_path) { 1226 if (!strcmp(name, ".")) { 1227 /* "." relative to "foo/bar" is "foo/bar" */ 1228 v9fs_path_copy(target, dir_path); 1229 } else if (!strcmp(name, "..")) { 1230 if (!strcmp(dir_path->data, ".")) { 1231 /* ".." relative to the root is "." */ 1232 v9fs_path_sprintf(target, "."); 1233 } else { 1234 char *tmp = g_path_get_dirname(dir_path->data); 1235 /* Symbolic links are resolved by the client. We can assume 1236 * that ".." relative to "foo/bar" is equivalent to "foo" 1237 */ 1238 v9fs_path_sprintf(target, "%s", tmp); 1239 g_free(tmp); 1240 } 1241 } else { 1242 assert(!strchr(name, '/')); 1243 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); 1244 } 1245 } else if (!strcmp(name, "/") || !strcmp(name, ".") || 1246 !strcmp(name, "..")) { 1247 /* This is the root fid */ 1248 v9fs_path_sprintf(target, "."); 1249 } else { 1250 assert(!strchr(name, '/')); 1251 v9fs_path_sprintf(target, "./%s", name); 1252 } 1253 return 0; 1254 } 1255 1256 static int local_renameat(FsContext *ctx, V9fsPath *olddir, 1257 const char *old_name, V9fsPath *newdir, 1258 const char *new_name) 1259 { 1260 int ret; 1261 int odirfd, ndirfd; 1262 1263 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1264 (local_is_mapped_file_metadata(ctx, old_name) || 1265 local_is_mapped_file_metadata(ctx, new_name))) { 1266 errno = EINVAL; 1267 return -1; 1268 } 1269 1270 odirfd = local_opendir_nofollow(ctx, olddir->data); 1271 if (odirfd == -1) { 1272 return -1; 1273 } 1274 1275 ndirfd = local_opendir_nofollow(ctx, newdir->data); 1276 if (ndirfd == -1) { 1277 close_preserve_errno(odirfd); 1278 return -1; 1279 } 1280 1281 ret = renameat(odirfd, old_name, ndirfd, new_name); 1282 if (ret < 0) { 1283 goto out; 1284 } 1285 1286 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1287 int omap_dirfd, nmap_dirfd; 1288 1289 ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 1290 if (ret < 0 && errno != EEXIST) { 1291 goto err_undo_rename; 1292 } 1293 1294 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 1295 if (omap_dirfd == -1) { 1296 goto err; 1297 } 1298 1299 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1300 if (nmap_dirfd == -1) { 1301 close_preserve_errno(omap_dirfd); 1302 goto err; 1303 } 1304 1305 /* rename the .virtfs_metadata files */ 1306 ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name); 1307 close_preserve_errno(nmap_dirfd); 1308 close_preserve_errno(omap_dirfd); 1309 if (ret < 0 && errno != ENOENT) { 1310 goto err_undo_rename; 1311 } 1312 1313 ret = 0; 1314 } 1315 goto out; 1316 1317 err: 1318 ret = -1; 1319 err_undo_rename: 1320 renameat_preserve_errno(ndirfd, new_name, odirfd, old_name); 1321 out: 1322 close_preserve_errno(ndirfd); 1323 close_preserve_errno(odirfd); 1324 return ret; 1325 } 1326 1327 static void v9fs_path_init_dirname(V9fsPath *path, const char *str) 1328 { 1329 path->data = g_path_get_dirname(str); 1330 path->size = strlen(path->data) + 1; 1331 } 1332 1333 static int local_rename(FsContext *ctx, const char *oldpath, 1334 const char *newpath) 1335 { 1336 int err; 1337 char *oname = g_path_get_basename(oldpath); 1338 char *nname = g_path_get_basename(newpath); 1339 V9fsPath olddir, newdir; 1340 1341 v9fs_path_init_dirname(&olddir, oldpath); 1342 v9fs_path_init_dirname(&newdir, newpath); 1343 1344 err = local_renameat(ctx, &olddir, oname, &newdir, nname); 1345 1346 v9fs_path_free(&newdir); 1347 v9fs_path_free(&olddir); 1348 g_free(nname); 1349 g_free(oname); 1350 1351 return err; 1352 } 1353 1354 static int local_unlinkat(FsContext *ctx, V9fsPath *dir, 1355 const char *name, int flags) 1356 { 1357 int ret; 1358 int dirfd; 1359 1360 if (ctx->export_flags & V9FS_SM_MAPPED_FILE && 1361 local_is_mapped_file_metadata(ctx, name)) { 1362 errno = EINVAL; 1363 return -1; 1364 } 1365 1366 dirfd = local_opendir_nofollow(ctx, dir->data); 1367 if (dirfd == -1) { 1368 return -1; 1369 } 1370 1371 ret = local_unlinkat_common(ctx, dirfd, name, flags); 1372 close_preserve_errno(dirfd); 1373 return ret; 1374 } 1375 1376 #ifdef FS_IOC_GETVERSION 1377 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, 1378 mode_t st_mode, uint64_t *st_gen) 1379 { 1380 int err; 1381 V9fsFidOpenState fid_open; 1382 1383 /* 1384 * Do not try to open special files like device nodes, fifos etc 1385 * We can get fd for regular files and directories only 1386 */ 1387 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { 1388 errno = ENOTTY; 1389 return -1; 1390 } 1391 err = local_open(ctx, path, O_RDONLY, &fid_open); 1392 if (err < 0) { 1393 return err; 1394 } 1395 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); 1396 local_close(ctx, &fid_open); 1397 return err; 1398 } 1399 #endif 1400 1401 static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp) 1402 { 1403 #ifdef FS_IOC_GETVERSION 1404 struct statfs stbuf; 1405 1406 /* 1407 * use ioc_getversion only if the ioctl is definied 1408 */ 1409 if (fstatfs(data->mountfd, &stbuf) < 0) { 1410 error_setg_errno(errp, errno, 1411 "failed to stat file system at '%s'", ctx->fs_root); 1412 return -1; 1413 } 1414 switch (stbuf.f_type) { 1415 case EXT2_SUPER_MAGIC: 1416 case BTRFS_SUPER_MAGIC: 1417 case REISERFS_SUPER_MAGIC: 1418 case XFS_SUPER_MAGIC: 1419 ctx->exops.get_st_gen = local_ioc_getversion; 1420 break; 1421 } 1422 #endif 1423 return 0; 1424 } 1425 1426 static int local_init(FsContext *ctx, Error **errp) 1427 { 1428 LocalData *data = g_malloc(sizeof(*data)); 1429 1430 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); 1431 if (data->mountfd == -1) { 1432 error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); 1433 goto err; 1434 } 1435 1436 if (local_ioc_getversion_init(ctx, data, errp) < 0) { 1437 close(data->mountfd); 1438 goto err; 1439 } 1440 1441 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { 1442 ctx->xops = passthrough_xattr_ops; 1443 } else if (ctx->export_flags & V9FS_SM_MAPPED) { 1444 ctx->xops = mapped_xattr_ops; 1445 } else if (ctx->export_flags & V9FS_SM_NONE) { 1446 ctx->xops = none_xattr_ops; 1447 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1448 /* 1449 * xattr operation for mapped-file and passthrough 1450 * remain same. 1451 */ 1452 ctx->xops = passthrough_xattr_ops; 1453 } 1454 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; 1455 1456 ctx->private = data; 1457 return 0; 1458 1459 err: 1460 g_free(data); 1461 return -1; 1462 } 1463 1464 static void local_cleanup(FsContext *ctx) 1465 { 1466 LocalData *data = ctx->private; 1467 1468 close(data->mountfd); 1469 g_free(data); 1470 } 1471 1472 static void error_append_security_model_hint(Error **errp) 1473 { 1474 error_append_hint(errp, "Valid options are: security_model=" 1475 "[passthrough|mapped-xattr|mapped-file|none]\n"); 1476 } 1477 1478 static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) 1479 { 1480 const char *sec_model = qemu_opt_get(opts, "security_model"); 1481 const char *path = qemu_opt_get(opts, "path"); 1482 Error *local_err = NULL; 1483 1484 if (!sec_model) { 1485 error_setg(errp, "security_model property not set"); 1486 error_append_security_model_hint(errp); 1487 return -1; 1488 } 1489 1490 if (!strcmp(sec_model, "passthrough")) { 1491 fse->export_flags |= V9FS_SM_PASSTHROUGH; 1492 } else if (!strcmp(sec_model, "mapped") || 1493 !strcmp(sec_model, "mapped-xattr")) { 1494 fse->export_flags |= V9FS_SM_MAPPED; 1495 } else if (!strcmp(sec_model, "none")) { 1496 fse->export_flags |= V9FS_SM_NONE; 1497 } else if (!strcmp(sec_model, "mapped-file")) { 1498 fse->export_flags |= V9FS_SM_MAPPED_FILE; 1499 } else { 1500 error_setg(errp, "invalid security_model property '%s'", sec_model); 1501 error_append_security_model_hint(errp); 1502 return -1; 1503 } 1504 1505 if (!path) { 1506 error_setg(errp, "path property not set"); 1507 return -1; 1508 } 1509 1510 fsdev_throttle_parse_opts(opts, &fse->fst, &local_err); 1511 if (local_err) { 1512 error_propagate_prepend(errp, local_err, 1513 "invalid throttle configuration: "); 1514 return -1; 1515 } 1516 1517 if (fse->export_flags & V9FS_SM_MAPPED || 1518 fse->export_flags & V9FS_SM_MAPPED_FILE) { 1519 fse->fmode = 1520 qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; 1521 fse->dmode = 1522 qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; 1523 } else { 1524 if (qemu_opt_find(opts, "fmode")) { 1525 error_setg(errp, "fmode is only valid for mapped security modes"); 1526 return -1; 1527 } 1528 if (qemu_opt_find(opts, "dmode")) { 1529 error_setg(errp, "dmode is only valid for mapped security modes"); 1530 return -1; 1531 } 1532 } 1533 1534 fse->path = g_strdup(path); 1535 1536 return 0; 1537 } 1538 1539 FileOperations local_ops = { 1540 .parse_opts = local_parse_opts, 1541 .init = local_init, 1542 .cleanup = local_cleanup, 1543 .lstat = local_lstat, 1544 .readlink = local_readlink, 1545 .close = local_close, 1546 .closedir = local_closedir, 1547 .open = local_open, 1548 .opendir = local_opendir, 1549 .rewinddir = local_rewinddir, 1550 .telldir = local_telldir, 1551 .readdir = local_readdir, 1552 .seekdir = local_seekdir, 1553 .preadv = local_preadv, 1554 .pwritev = local_pwritev, 1555 .chmod = local_chmod, 1556 .mknod = local_mknod, 1557 .mkdir = local_mkdir, 1558 .fstat = local_fstat, 1559 .open2 = local_open2, 1560 .symlink = local_symlink, 1561 .link = local_link, 1562 .truncate = local_truncate, 1563 .rename = local_rename, 1564 .chown = local_chown, 1565 .utimensat = local_utimensat, 1566 .remove = local_remove, 1567 .fsync = local_fsync, 1568 .statfs = local_statfs, 1569 .lgetxattr = local_lgetxattr, 1570 .llistxattr = local_llistxattr, 1571 .lsetxattr = local_lsetxattr, 1572 .lremovexattr = local_lremovexattr, 1573 .name_to_path = local_name_to_path, 1574 .renameat = local_renameat, 1575 .unlinkat = local_unlinkat, 1576 }; 1577