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