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