1 /* 2 * 9p synthetic file system support 3 * 4 * Copyright IBM, Corp. 2011 5 * 6 * Authors: 7 * Malahal Naineni <malahal@us.ibm.com> 8 * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2. See 11 * the COPYING file in the top-level directory. 12 * 13 */ 14 15 #include "qemu/osdep.h" 16 #include "9p.h" 17 #include "fsdev/qemu-fsdev.h" 18 #include "9p-synth.h" 19 #include "qemu/rcu.h" 20 #include "qemu/rcu_queue.h" 21 #include "qemu/cutils.h" 22 #include "sysemu/qtest.h" 23 24 /* Root node for synth file system */ 25 static V9fsSynthNode synth_root = { 26 .name = "/", 27 .actual_attr = { 28 .mode = 0555 | S_IFDIR, 29 .nlink = 1, 30 }, 31 .attr = &synth_root.actual_attr, 32 }; 33 34 static QemuMutex synth_mutex; 35 static int synth_node_count; 36 /* set to 1 when the synth fs is ready */ 37 static int synth_fs; 38 39 static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, 40 const char *name, 41 V9fsSynthNodeAttr *attr, int inode) 42 { 43 V9fsSynthNode *node; 44 45 /* Add directory type and remove write bits */ 46 mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); 47 node = g_malloc0(sizeof(V9fsSynthNode)); 48 if (attr) { 49 /* We are adding .. or . entries */ 50 node->attr = attr; 51 node->attr->nlink++; 52 } else { 53 node->attr = &node->actual_attr; 54 node->attr->inode = inode; 55 node->attr->nlink = 1; 56 /* We don't allow write to directories */ 57 node->attr->mode = mode; 58 node->attr->write = NULL; 59 node->attr->read = NULL; 60 } 61 node->private = node; 62 pstrcpy(node->name, sizeof(node->name), name); 63 QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); 64 return node; 65 } 66 67 int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, 68 const char *name, V9fsSynthNode **result) 69 { 70 int ret; 71 V9fsSynthNode *node, *tmp; 72 73 if (!synth_fs) { 74 return EAGAIN; 75 } 76 if (!name || (strlen(name) >= NAME_MAX)) { 77 return EINVAL; 78 } 79 if (!parent) { 80 parent = &synth_root; 81 } 82 QEMU_LOCK_GUARD(&synth_mutex); 83 QLIST_FOREACH(tmp, &parent->child, sibling) { 84 if (!strcmp(tmp->name, name)) { 85 ret = EEXIST; 86 return ret; 87 } 88 } 89 /* Add the name */ 90 node = v9fs_add_dir_node(parent, mode, name, NULL, synth_node_count++); 91 v9fs_add_dir_node(node, parent->attr->mode, "..", 92 parent->attr, parent->attr->inode); 93 v9fs_add_dir_node(node, node->attr->mode, ".", 94 node->attr, node->attr->inode); 95 *result = node; 96 ret = 0; 97 return ret; 98 } 99 100 int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, 101 const char *name, v9fs_synth_read read, 102 v9fs_synth_write write, void *arg) 103 { 104 int ret; 105 V9fsSynthNode *node, *tmp; 106 107 if (!synth_fs) { 108 return EAGAIN; 109 } 110 if (!name || (strlen(name) >= NAME_MAX)) { 111 return EINVAL; 112 } 113 if (!parent) { 114 parent = &synth_root; 115 } 116 117 QEMU_LOCK_GUARD(&synth_mutex); 118 QLIST_FOREACH(tmp, &parent->child, sibling) { 119 if (!strcmp(tmp->name, name)) { 120 ret = EEXIST; 121 return ret; 122 } 123 } 124 /* Add file type and remove write bits */ 125 mode = ((mode & 0777) | S_IFREG); 126 node = g_malloc0(sizeof(V9fsSynthNode)); 127 node->attr = &node->actual_attr; 128 node->attr->inode = synth_node_count++; 129 node->attr->nlink = 1; 130 node->attr->read = read; 131 node->attr->write = write; 132 node->attr->mode = mode; 133 node->private = arg; 134 pstrcpy(node->name, sizeof(node->name), name); 135 QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); 136 ret = 0; 137 return ret; 138 } 139 140 static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) 141 { 142 stbuf->st_dev = 0; 143 stbuf->st_ino = node->attr->inode; 144 stbuf->st_mode = node->attr->mode; 145 stbuf->st_nlink = node->attr->nlink; 146 stbuf->st_uid = 0; 147 stbuf->st_gid = 0; 148 stbuf->st_rdev = 0; 149 stbuf->st_size = 0; 150 stbuf->st_blksize = 0; 151 stbuf->st_blocks = 0; 152 stbuf->st_atime = 0; 153 stbuf->st_mtime = 0; 154 stbuf->st_ctime = 0; 155 } 156 157 static int synth_lstat(FsContext *fs_ctx, 158 V9fsPath *fs_path, struct stat *stbuf) 159 { 160 V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; 161 162 synth_fill_statbuf(node, stbuf); 163 return 0; 164 } 165 166 static int synth_fstat(FsContext *fs_ctx, int fid_type, 167 V9fsFidOpenState *fs, struct stat *stbuf) 168 { 169 V9fsSynthOpenState *synth_open = fs->private; 170 synth_fill_statbuf(synth_open->node, stbuf); 171 return 0; 172 } 173 174 static int synth_opendir(FsContext *ctx, 175 V9fsPath *fs_path, V9fsFidOpenState *fs) 176 { 177 V9fsSynthOpenState *synth_open; 178 V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; 179 180 synth_open = g_malloc(sizeof(*synth_open)); 181 synth_open->node = node; 182 node->open_count++; 183 fs->private = synth_open; 184 return 0; 185 } 186 187 static int synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) 188 { 189 V9fsSynthOpenState *synth_open = fs->private; 190 V9fsSynthNode *node = synth_open->node; 191 192 node->open_count--; 193 g_free(synth_open); 194 fs->private = NULL; 195 return 0; 196 } 197 198 static off_t synth_telldir(FsContext *ctx, V9fsFidOpenState *fs) 199 { 200 V9fsSynthOpenState *synth_open = fs->private; 201 return synth_open->offset; 202 } 203 204 static void synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) 205 { 206 V9fsSynthOpenState *synth_open = fs->private; 207 synth_open->offset = off; 208 } 209 210 static void synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) 211 { 212 synth_seekdir(ctx, fs, 0); 213 } 214 215 static void synth_direntry(V9fsSynthNode *node, 216 struct dirent *entry, off_t off) 217 { 218 strcpy(entry->d_name, node->name); 219 entry->d_ino = node->attr->inode; 220 entry->d_off = off + 1; 221 } 222 223 static struct dirent *synth_get_dentry(V9fsSynthNode *dir, 224 struct dirent *entry, off_t off) 225 { 226 int i = 0; 227 V9fsSynthNode *node; 228 229 rcu_read_lock(); 230 QLIST_FOREACH(node, &dir->child, sibling) { 231 /* This is the off child of the directory */ 232 if (i == off) { 233 break; 234 } 235 i++; 236 } 237 rcu_read_unlock(); 238 if (!node) { 239 /* end of directory */ 240 return NULL; 241 } 242 synth_direntry(node, entry, off); 243 return entry; 244 } 245 246 static struct dirent *synth_readdir(FsContext *ctx, V9fsFidOpenState *fs) 247 { 248 struct dirent *entry; 249 V9fsSynthOpenState *synth_open = fs->private; 250 V9fsSynthNode *node = synth_open->node; 251 entry = synth_get_dentry(node, &synth_open->dent, synth_open->offset); 252 if (entry) { 253 synth_open->offset++; 254 } 255 return entry; 256 } 257 258 static int synth_open(FsContext *ctx, V9fsPath *fs_path, 259 int flags, V9fsFidOpenState *fs) 260 { 261 V9fsSynthOpenState *synth_open; 262 V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; 263 264 synth_open = g_malloc(sizeof(*synth_open)); 265 synth_open->node = node; 266 node->open_count++; 267 fs->private = synth_open; 268 return 0; 269 } 270 271 static int synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, 272 const char *name, int flags, 273 FsCred *credp, V9fsFidOpenState *fs) 274 { 275 errno = ENOSYS; 276 return -1; 277 } 278 279 static int synth_close(FsContext *ctx, V9fsFidOpenState *fs) 280 { 281 V9fsSynthOpenState *synth_open = fs->private; 282 V9fsSynthNode *node = synth_open->node; 283 284 node->open_count--; 285 g_free(synth_open); 286 fs->private = NULL; 287 return 0; 288 } 289 290 static ssize_t synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs, 291 const struct iovec *iov, 292 int iovcnt, off_t offset) 293 { 294 int i, count = 0, wcount; 295 V9fsSynthOpenState *synth_open = fs->private; 296 V9fsSynthNode *node = synth_open->node; 297 if (!node->attr->write) { 298 errno = EPERM; 299 return -1; 300 } 301 for (i = 0; i < iovcnt; i++) { 302 wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, 303 offset, node->private); 304 offset += wcount; 305 count += wcount; 306 /* If we wrote less than requested. we are done */ 307 if (wcount < iov[i].iov_len) { 308 break; 309 } 310 } 311 return count; 312 } 313 314 static ssize_t synth_preadv(FsContext *ctx, V9fsFidOpenState *fs, 315 const struct iovec *iov, 316 int iovcnt, off_t offset) 317 { 318 int i, count = 0, rcount; 319 V9fsSynthOpenState *synth_open = fs->private; 320 V9fsSynthNode *node = synth_open->node; 321 if (!node->attr->read) { 322 errno = EPERM; 323 return -1; 324 } 325 for (i = 0; i < iovcnt; i++) { 326 rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, 327 offset, node->private); 328 offset += rcount; 329 count += rcount; 330 /* If we read less than requested. we are done */ 331 if (rcount < iov[i].iov_len) { 332 break; 333 } 334 } 335 return count; 336 } 337 338 static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) 339 { 340 errno = ENOSYS; 341 return -1; 342 } 343 344 static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) 345 { 346 errno = EPERM; 347 return -1; 348 } 349 350 static int synth_mknod(FsContext *fs_ctx, V9fsPath *path, 351 const char *buf, FsCred *credp) 352 { 353 errno = EPERM; 354 return -1; 355 } 356 357 static int synth_mkdir(FsContext *fs_ctx, V9fsPath *path, 358 const char *buf, FsCred *credp) 359 { 360 errno = EPERM; 361 return -1; 362 } 363 364 static ssize_t synth_readlink(FsContext *fs_ctx, V9fsPath *path, 365 char *buf, size_t bufsz) 366 { 367 errno = ENOSYS; 368 return -1; 369 } 370 371 static int synth_symlink(FsContext *fs_ctx, const char *oldpath, 372 V9fsPath *newpath, const char *buf, FsCred *credp) 373 { 374 errno = EPERM; 375 return -1; 376 } 377 378 static int synth_link(FsContext *fs_ctx, V9fsPath *oldpath, 379 V9fsPath *newpath, const char *buf) 380 { 381 errno = EPERM; 382 return -1; 383 } 384 385 static int synth_rename(FsContext *ctx, const char *oldpath, 386 const char *newpath) 387 { 388 errno = EPERM; 389 return -1; 390 } 391 392 static int synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) 393 { 394 errno = EPERM; 395 return -1; 396 } 397 398 static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path, 399 const struct timespec *buf) 400 { 401 errno = EPERM; 402 return 0; 403 } 404 405 static int synth_remove(FsContext *ctx, const char *path) 406 { 407 errno = EPERM; 408 return -1; 409 } 410 411 static int synth_fsync(FsContext *ctx, int fid_type, 412 V9fsFidOpenState *fs, int datasync) 413 { 414 errno = ENOSYS; 415 return 0; 416 } 417 418 static int synth_statfs(FsContext *s, V9fsPath *fs_path, 419 struct statfs *stbuf) 420 { 421 stbuf->f_type = 0xABCD; 422 stbuf->f_bsize = 512; 423 stbuf->f_blocks = 0; 424 stbuf->f_files = synth_node_count; 425 stbuf->f_namelen = NAME_MAX; 426 return 0; 427 } 428 429 static ssize_t synth_lgetxattr(FsContext *ctx, V9fsPath *path, 430 const char *name, void *value, size_t size) 431 { 432 errno = ENOTSUP; 433 return -1; 434 } 435 436 static ssize_t synth_llistxattr(FsContext *ctx, V9fsPath *path, 437 void *value, size_t size) 438 { 439 errno = ENOTSUP; 440 return -1; 441 } 442 443 static int synth_lsetxattr(FsContext *ctx, V9fsPath *path, 444 const char *name, void *value, 445 size_t size, int flags) 446 { 447 errno = ENOTSUP; 448 return -1; 449 } 450 451 static int synth_lremovexattr(FsContext *ctx, 452 V9fsPath *path, const char *name) 453 { 454 errno = ENOTSUP; 455 return -1; 456 } 457 458 static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, 459 const char *name, V9fsPath *target) 460 { 461 V9fsSynthNode *node; 462 V9fsSynthNode *dir_node; 463 464 /* "." and ".." are not allowed */ 465 if (!strcmp(name, ".") || !strcmp(name, "..")) { 466 errno = EINVAL; 467 return -1; 468 469 } 470 if (!dir_path) { 471 dir_node = &synth_root; 472 } else { 473 dir_node = *(V9fsSynthNode **)dir_path->data; 474 } 475 if (!strcmp(name, "/")) { 476 node = dir_node; 477 goto out; 478 } 479 /* search for the name in the childern */ 480 rcu_read_lock(); 481 QLIST_FOREACH(node, &dir_node->child, sibling) { 482 if (!strcmp(node->name, name)) { 483 break; 484 } 485 } 486 rcu_read_unlock(); 487 488 if (!node) { 489 errno = ENOENT; 490 return -1; 491 } 492 out: 493 /* Copy the node pointer to fid */ 494 g_free(target->data); 495 target->data = g_memdup(&node, sizeof(void *)); 496 target->size = sizeof(void *); 497 return 0; 498 } 499 500 static int synth_renameat(FsContext *ctx, V9fsPath *olddir, 501 const char *old_name, V9fsPath *newdir, 502 const char *new_name) 503 { 504 errno = EPERM; 505 return -1; 506 } 507 508 static int synth_unlinkat(FsContext *ctx, V9fsPath *dir, 509 const char *name, int flags) 510 { 511 errno = EPERM; 512 return -1; 513 } 514 515 static ssize_t v9fs_synth_qtest_write(void *buf, int len, off_t offset, 516 void *arg) 517 { 518 return 1; 519 } 520 521 static ssize_t v9fs_synth_qtest_flush_write(void *buf, int len, off_t offset, 522 void *arg) 523 { 524 bool should_block = !!*(uint8_t *)buf; 525 526 if (should_block) { 527 /* This will cause the server to call us again until we're cancelled */ 528 errno = EINTR; 529 return -1; 530 } 531 532 return 1; 533 } 534 535 static int synth_init(FsContext *ctx, Error **errp) 536 { 537 QLIST_INIT(&synth_root.child); 538 qemu_mutex_init(&synth_mutex); 539 540 /* Add "." and ".." entries for root */ 541 v9fs_add_dir_node(&synth_root, synth_root.attr->mode, 542 "..", synth_root.attr, synth_root.attr->inode); 543 v9fs_add_dir_node(&synth_root, synth_root.attr->mode, 544 ".", synth_root.attr, synth_root.attr->inode); 545 546 /* Mark the subsystem is ready for use */ 547 synth_fs = 1; 548 549 if (qtest_enabled()) { 550 V9fsSynthNode *node = NULL; 551 int i, ret; 552 553 /* Directory hierarchy for WALK test */ 554 for (i = 0; i < P9_MAXWELEM; i++) { 555 char *name = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); 556 557 ret = qemu_v9fs_synth_mkdir(node, 0700, name, &node); 558 assert(!ret); 559 g_free(name); 560 } 561 562 /* File for LOPEN test */ 563 ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_LOPEN_FILE, 564 NULL, NULL, ctx); 565 assert(!ret); 566 567 /* File for WRITE test */ 568 ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_WRITE_FILE, 569 NULL, v9fs_synth_qtest_write, ctx); 570 assert(!ret); 571 572 /* File for FLUSH test */ 573 ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_FLUSH_FILE, 574 NULL, v9fs_synth_qtest_flush_write, 575 ctx); 576 assert(!ret); 577 578 /* Directory for READDIR test */ 579 { 580 V9fsSynthNode *dir = NULL; 581 ret = qemu_v9fs_synth_mkdir( 582 NULL, 0700, QTEST_V9FS_SYNTH_READDIR_DIR, &dir 583 ); 584 assert(!ret); 585 for (i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) { 586 char *name = g_strdup_printf( 587 QTEST_V9FS_SYNTH_READDIR_FILE, i 588 ); 589 ret = qemu_v9fs_synth_add_file( 590 dir, 0, name, NULL, NULL, ctx 591 ); 592 assert(!ret); 593 g_free(name); 594 } 595 } 596 } 597 598 return 0; 599 } 600 601 FileOperations synth_ops = { 602 .init = synth_init, 603 .lstat = synth_lstat, 604 .readlink = synth_readlink, 605 .close = synth_close, 606 .closedir = synth_closedir, 607 .open = synth_open, 608 .opendir = synth_opendir, 609 .rewinddir = synth_rewinddir, 610 .telldir = synth_telldir, 611 .readdir = synth_readdir, 612 .seekdir = synth_seekdir, 613 .preadv = synth_preadv, 614 .pwritev = synth_pwritev, 615 .chmod = synth_chmod, 616 .mknod = synth_mknod, 617 .mkdir = synth_mkdir, 618 .fstat = synth_fstat, 619 .open2 = synth_open2, 620 .symlink = synth_symlink, 621 .link = synth_link, 622 .truncate = synth_truncate, 623 .rename = synth_rename, 624 .chown = synth_chown, 625 .utimensat = synth_utimensat, 626 .remove = synth_remove, 627 .fsync = synth_fsync, 628 .statfs = synth_statfs, 629 .lgetxattr = synth_lgetxattr, 630 .llistxattr = synth_llistxattr, 631 .lsetxattr = synth_lsetxattr, 632 .lremovexattr = synth_lremovexattr, 633 .name_to_path = synth_name_to_path, 634 .renameat = synth_renameat, 635 .unlinkat = synth_unlinkat, 636 }; 637