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