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