1 /* 2 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #include <fs/devfs.h> 11 12 #include <errno.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/stat.h> 17 18 #include <Drivers.h> 19 #include <KernelExport.h> 20 #include <NodeMonitor.h> 21 22 #include <arch/cpu.h> 23 #include <AutoDeleter.h> 24 #include <boot/kernel_args.h> 25 #include <boot_device.h> 26 #include <debug.h> 27 #include <elf.h> 28 #include <FindDirectory.h> 29 #include <fs/KPath.h> 30 #include <fs/node_monitor.h> 31 #include <kdevice_manager.h> 32 #include <lock.h> 33 #include <Notifications.h> 34 #include <util/AutoLock.h> 35 #include <util/khash.h> 36 #include <vfs.h> 37 #include <vm/vm.h> 38 39 #include "BaseDevice.h" 40 #include "FileDevice.h" 41 #include "IORequest.h" 42 #include "legacy_drivers.h" 43 44 45 //#define TRACE_DEVFS 46 #ifdef TRACE_DEVFS 47 # define TRACE(x) dprintf x 48 #else 49 # define TRACE(x) 50 #endif 51 52 53 struct devfs_partition { 54 struct devfs_vnode* raw_device; 55 partition_info info; 56 }; 57 58 struct driver_entry; 59 60 enum { 61 kNotScanned = 0, 62 kBootScan, 63 kNormalScan, 64 }; 65 66 struct devfs_stream { 67 mode_t type; 68 union { 69 struct stream_dir { 70 struct devfs_vnode* dir_head; 71 struct list cookies; 72 mutex scan_lock; 73 int32 scanned; 74 } dir; 75 struct stream_dev { 76 BaseDevice* device; 77 struct devfs_partition* partition; 78 } dev; 79 struct stream_symlink { 80 const char* path; 81 size_t length; 82 } symlink; 83 } u; 84 }; 85 86 struct devfs_vnode { 87 struct devfs_vnode* all_next; 88 ino_t id; 89 char* name; 90 timespec modification_time; 91 timespec creation_time; 92 uid_t uid; 93 gid_t gid; 94 struct devfs_vnode* parent; 95 struct devfs_vnode* dir_next; 96 struct devfs_stream stream; 97 }; 98 99 #define DEVFS_HASH_SIZE 16 100 101 struct devfs { 102 dev_t id; 103 fs_volume* volume; 104 recursive_lock lock; 105 int32 next_vnode_id; 106 hash_table* vnode_hash; 107 struct devfs_vnode* root_vnode; 108 }; 109 110 struct devfs_dir_cookie { 111 struct list_link link; 112 struct devfs_vnode* current; 113 int32 state; // iteration state 114 }; 115 116 struct devfs_cookie { 117 void* device_cookie; 118 }; 119 120 struct synchronous_io_cookie { 121 BaseDevice* device; 122 void* cookie; 123 }; 124 125 // directory iteration states 126 enum { 127 ITERATION_STATE_DOT = 0, 128 ITERATION_STATE_DOT_DOT = 1, 129 ITERATION_STATE_OTHERS = 2, 130 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT, 131 }; 132 133 // extern and in a private namespace only to make forward declaration possible 134 namespace { 135 extern fs_volume_ops kVolumeOps; 136 extern fs_vnode_ops kVnodeOps; 137 } 138 139 140 static status_t get_node_for_path(struct devfs *fs, const char *path, 141 struct devfs_vnode **_node); 142 static void get_device_name(struct devfs_vnode *vnode, char *buffer, 143 size_t size); 144 static status_t unpublish_node(struct devfs *fs, devfs_vnode *node, 145 mode_t type); 146 static status_t publish_device(struct devfs *fs, const char *path, 147 BaseDevice* device); 148 149 150 /* the one and only allowed devfs instance */ 151 static struct devfs* sDeviceFileSystem = NULL; 152 153 154 // #pragma mark - devfs private 155 156 157 static timespec 158 current_timespec() 159 { 160 bigtime_t time = real_time_clock_usecs(); 161 162 timespec tv; 163 tv.tv_sec = time / 1000000; 164 tv.tv_nsec = (time % 1000000) * 1000; 165 return tv; 166 } 167 168 169 static int32 170 scan_mode(void) 171 { 172 // We may scan every device twice: 173 // - once before there is a boot device, 174 // - and once when there is one 175 176 return gBootDevice >= 0 ? kNormalScan : kBootScan; 177 } 178 179 180 static status_t 181 scan_for_drivers_if_needed(devfs_vnode* dir) 182 { 183 ASSERT(S_ISDIR(dir->stream.type)); 184 185 MutexLocker _(dir->stream.u.dir.scan_lock); 186 187 if (dir->stream.u.dir.scanned >= scan_mode()) 188 return B_OK; 189 190 KPath path; 191 if (path.InitCheck() != B_OK) 192 return B_NO_MEMORY; 193 194 get_device_name(dir, path.LockBuffer(), path.BufferSize()); 195 path.UnlockBuffer(); 196 197 TRACE(("scan_for_drivers_if_needed: mode %ld: %s\n", scan_mode(), 198 path.Path())); 199 200 // scan for drivers at this path 201 static int32 updateCycle = 1; 202 device_manager_probe(path.Path(), updateCycle++); 203 legacy_driver_probe(path.Path()); 204 205 dir->stream.u.dir.scanned = scan_mode(); 206 return B_OK; 207 } 208 209 210 static uint32 211 devfs_vnode_hash(void* _vnode, const void* _key, uint32 range) 212 { 213 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode; 214 const ino_t* key = (const ino_t*)_key; 215 216 if (vnode != NULL) 217 return vnode->id % range; 218 219 return (uint64)*key % range; 220 } 221 222 223 static int 224 devfs_vnode_compare(void* _vnode, const void* _key) 225 { 226 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode; 227 const ino_t* key = (const ino_t*)_key; 228 229 if (vnode->id == *key) 230 return 0; 231 232 return -1; 233 } 234 235 236 static void 237 init_directory_vnode(struct devfs_vnode* vnode, int permissions) 238 { 239 vnode->stream.type = S_IFDIR | permissions; 240 mutex_init(&vnode->stream.u.dir.scan_lock, "devfs scan"); 241 vnode->stream.u.dir.dir_head = NULL; 242 list_init(&vnode->stream.u.dir.cookies); 243 } 244 245 246 static struct devfs_vnode* 247 devfs_create_vnode(struct devfs* fs, devfs_vnode* parent, const char* name) 248 { 249 struct devfs_vnode* vnode; 250 251 vnode = (struct devfs_vnode*)malloc(sizeof(struct devfs_vnode)); 252 if (vnode == NULL) 253 return NULL; 254 255 memset(vnode, 0, sizeof(struct devfs_vnode)); 256 vnode->id = fs->next_vnode_id++; 257 258 vnode->name = strdup(name); 259 if (vnode->name == NULL) { 260 free(vnode); 261 return NULL; 262 } 263 264 vnode->creation_time = vnode->modification_time = current_timespec(); 265 vnode->uid = geteuid(); 266 vnode->gid = parent ? parent->gid : getegid(); 267 // inherit group from parent if possible 268 269 return vnode; 270 } 271 272 273 static status_t 274 devfs_delete_vnode(struct devfs* fs, struct devfs_vnode* vnode, 275 bool forceDelete) 276 { 277 // cant delete it if it's in a directory or is a directory 278 // and has children 279 if (!forceDelete && ((S_ISDIR(vnode->stream.type) 280 && vnode->stream.u.dir.dir_head != NULL) 281 || vnode->dir_next != NULL)) 282 return B_NOT_ALLOWED; 283 284 // remove it from the global hash table 285 hash_remove(fs->vnode_hash, vnode); 286 287 if (S_ISCHR(vnode->stream.type)) { 288 if (vnode->stream.u.dev.partition == NULL) { 289 // pass the call through to the underlying device 290 vnode->stream.u.dev.device->Removed(); 291 } else { 292 // for partitions, we have to release the raw device but must 293 // not free the device info as it was inherited from the raw 294 // device and is still in use there 295 put_vnode(fs->volume, vnode->stream.u.dev.partition->raw_device->id); 296 } 297 } else if (S_ISDIR(vnode->stream.type)) { 298 mutex_destroy(&vnode->stream.u.dir.scan_lock); 299 } 300 301 free(vnode->name); 302 free(vnode); 303 304 return B_OK; 305 } 306 307 308 /*! Makes sure none of the dircookies point to the vnode passed in */ 309 static void 310 update_dir_cookies(struct devfs_vnode* dir, struct devfs_vnode* vnode) 311 { 312 struct devfs_dir_cookie* cookie = NULL; 313 314 while ((cookie = (devfs_dir_cookie*)list_get_next_item( 315 &dir->stream.u.dir.cookies, cookie)) != NULL) { 316 if (cookie->current == vnode) 317 cookie->current = vnode->dir_next; 318 } 319 } 320 321 322 static struct devfs_vnode* 323 devfs_find_in_dir(struct devfs_vnode* dir, const char* path) 324 { 325 struct devfs_vnode* vnode; 326 327 if (!S_ISDIR(dir->stream.type)) 328 return NULL; 329 330 if (!strcmp(path, ".")) 331 return dir; 332 if (!strcmp(path, "..")) 333 return dir->parent; 334 335 for (vnode = dir->stream.u.dir.dir_head; vnode; vnode = vnode->dir_next) { 336 //TRACE(("devfs_find_in_dir: looking at entry '%s'\n", vnode->name)); 337 if (strcmp(vnode->name, path) == 0) { 338 //TRACE(("devfs_find_in_dir: found it at %p\n", vnode)); 339 return vnode; 340 } 341 } 342 return NULL; 343 } 344 345 346 static status_t 347 devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode, 348 bool notify = true) 349 { 350 if (!S_ISDIR(dir->stream.type)) 351 return B_BAD_VALUE; 352 353 // make sure the directory stays sorted alphabetically 354 355 devfs_vnode* node = dir->stream.u.dir.dir_head; 356 devfs_vnode* last = NULL; 357 while (node && strcmp(node->name, vnode->name) < 0) { 358 last = node; 359 node = node->dir_next; 360 } 361 if (last == NULL) { 362 // the new vnode is the first entry in the list 363 vnode->dir_next = dir->stream.u.dir.dir_head; 364 dir->stream.u.dir.dir_head = vnode; 365 } else { 366 // insert after that node 367 vnode->dir_next = last->dir_next; 368 last->dir_next = vnode; 369 } 370 371 vnode->parent = dir; 372 dir->modification_time = current_timespec(); 373 374 if (notify) { 375 notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name, 376 vnode->id); 377 notify_stat_changed(sDeviceFileSystem->id, dir->id, 378 B_STAT_MODIFICATION_TIME); 379 } 380 return B_OK; 381 } 382 383 384 static status_t 385 devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode, 386 bool notify = true) 387 { 388 struct devfs_vnode *vnode = dir->stream.u.dir.dir_head; 389 struct devfs_vnode *lastNode = NULL; 390 391 for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) { 392 if (vnode == removeNode) { 393 // make sure no dircookies point to this vnode 394 update_dir_cookies(dir, vnode); 395 396 if (lastNode) 397 lastNode->dir_next = vnode->dir_next; 398 else 399 dir->stream.u.dir.dir_head = vnode->dir_next; 400 vnode->dir_next = NULL; 401 dir->modification_time = current_timespec(); 402 403 if (notify) { 404 notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name, 405 vnode->id); 406 notify_stat_changed(sDeviceFileSystem->id, dir->id, 407 B_STAT_MODIFICATION_TIME); 408 } 409 return B_OK; 410 } 411 } 412 return B_ENTRY_NOT_FOUND; 413 } 414 415 416 static status_t 417 add_partition(struct devfs* fs, struct devfs_vnode* device, const char* name, 418 const partition_info& info) 419 { 420 struct devfs_vnode* partitionNode; 421 status_t status; 422 423 if (!S_ISCHR(device->stream.type)) 424 return B_BAD_VALUE; 425 426 // we don't support nested partitions 427 if (device->stream.u.dev.partition != NULL) 428 return B_BAD_VALUE; 429 430 // reduce checks to a minimum - things like negative offsets could be useful 431 if (info.size < 0) 432 return B_BAD_VALUE; 433 434 // create partition 435 struct devfs_partition* partition = (struct devfs_partition*)malloc( 436 sizeof(struct devfs_partition)); 437 if (partition == NULL) 438 return B_NO_MEMORY; 439 440 memcpy(&partition->info, &info, sizeof(partition_info)); 441 442 RecursiveLocker locker(fs->lock); 443 444 // you cannot change a partition once set 445 if (devfs_find_in_dir(device->parent, name)) { 446 status = B_BAD_VALUE; 447 goto err1; 448 } 449 450 // increase reference count of raw device - 451 // the partition device really needs it 452 status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device); 453 if (status < B_OK) 454 goto err1; 455 456 // now create the partition vnode 457 partitionNode = devfs_create_vnode(fs, device->parent, name); 458 if (partitionNode == NULL) { 459 status = B_NO_MEMORY; 460 goto err2; 461 } 462 463 partitionNode->stream.type = device->stream.type; 464 partitionNode->stream.u.dev.device = device->stream.u.dev.device; 465 partitionNode->stream.u.dev.partition = partition; 466 467 hash_insert(fs->vnode_hash, partitionNode); 468 devfs_insert_in_dir(device->parent, partitionNode); 469 470 TRACE(("add_partition(name = %s, offset = %Ld, size = %Ld)\n", 471 name, info.offset, info.size)); 472 return B_OK; 473 474 err2: 475 put_vnode(fs->volume, device->id); 476 err1: 477 free(partition); 478 return status; 479 } 480 481 482 static inline void 483 translate_partition_access(devfs_partition* partition, off_t& offset, 484 size_t& size) 485 { 486 ASSERT(offset >= 0); 487 ASSERT(offset < partition->info.size); 488 489 size = min_c(size, partition->info.size - offset); 490 offset += partition->info.offset; 491 } 492 493 494 static inline void 495 translate_partition_access(devfs_partition* partition, io_request* request) 496 { 497 off_t offset = request->Offset(); 498 499 ASSERT(offset >= 0); 500 ASSERT(offset + request->Length() <= partition->info.size); 501 502 request->SetOffset(offset + partition->info.offset); 503 } 504 505 506 static status_t 507 get_node_for_path(struct devfs *fs, const char *path, 508 struct devfs_vnode **_node) 509 { 510 return vfs_get_fs_node_from_path(fs->volume, path, false, true, 511 (void **)_node); 512 } 513 514 515 static status_t 516 unpublish_node(struct devfs *fs, devfs_vnode *node, mode_t type) 517 { 518 if ((node->stream.type & S_IFMT) != type) 519 return B_BAD_TYPE; 520 521 recursive_lock_lock(&fs->lock); 522 523 status_t status = devfs_remove_from_dir(node->parent, node); 524 if (status < B_OK) 525 goto out; 526 527 status = remove_vnode(fs->volume, node->id); 528 529 out: 530 recursive_lock_unlock(&fs->lock); 531 return status; 532 } 533 534 535 static void 536 publish_node(devfs* fs, devfs_vnode* dirNode, struct devfs_vnode* node) 537 { 538 hash_insert(fs->vnode_hash, node); 539 devfs_insert_in_dir(dirNode, node); 540 } 541 542 543 static status_t 544 publish_directory(struct devfs *fs, const char *path) 545 { 546 ASSERT_LOCKED_RECURSIVE(&fs->lock); 547 548 // copy the path over to a temp buffer so we can munge it 549 KPath tempPath(path); 550 if (tempPath.InitCheck() != B_OK) 551 return B_NO_MEMORY; 552 553 TRACE(("devfs: publish directory \"%s\"\n", path)); 554 char *temp = tempPath.LockBuffer(); 555 556 // create the path leading to the device 557 // parse the path passed in, stripping out '/' 558 559 struct devfs_vnode *dir = fs->root_vnode; 560 struct devfs_vnode *vnode = NULL; 561 status_t status = B_OK; 562 int32 i = 0, last = 0; 563 564 while (temp[last]) { 565 if (temp[i] == '/') { 566 temp[i] = '\0'; 567 i++; 568 } else if (temp[i] != '\0') { 569 i++; 570 continue; 571 } 572 573 //TRACE(("\tpath component '%s'\n", &temp[last])); 574 575 // we have a path component 576 vnode = devfs_find_in_dir(dir, &temp[last]); 577 if (vnode) { 578 if (S_ISDIR(vnode->stream.type)) { 579 last = i; 580 dir = vnode; 581 continue; 582 } 583 584 // we hit something on our path that's not a directory 585 status = B_FILE_EXISTS; 586 goto out; 587 } else { 588 vnode = devfs_create_vnode(fs, dir, &temp[last]); 589 if (!vnode) { 590 status = B_NO_MEMORY; 591 goto out; 592 } 593 } 594 595 // set up the new directory 596 init_directory_vnode(vnode, 0755); 597 publish_node(sDeviceFileSystem, dir, vnode); 598 599 last = i; 600 dir = vnode; 601 } 602 603 out: 604 return status; 605 } 606 607 608 static status_t 609 new_node(struct devfs *fs, const char *path, struct devfs_vnode **_node, 610 struct devfs_vnode **_dir) 611 { 612 ASSERT_LOCKED_RECURSIVE(&fs->lock); 613 614 // copy the path over to a temp buffer so we can munge it 615 KPath tempPath(path); 616 if (tempPath.InitCheck() != B_OK) 617 return B_NO_MEMORY; 618 619 char *temp = tempPath.LockBuffer(); 620 621 // create the path leading to the device 622 // parse the path passed in, stripping out '/' 623 624 struct devfs_vnode *dir = fs->root_vnode; 625 struct devfs_vnode *vnode = NULL; 626 status_t status = B_OK; 627 int32 i = 0, last = 0; 628 bool atLeaf = false; 629 630 for (;;) { 631 if (temp[i] == '\0') { 632 atLeaf = true; // we'll be done after this one 633 } else if (temp[i] == '/') { 634 temp[i] = '\0'; 635 i++; 636 } else { 637 i++; 638 continue; 639 } 640 641 //TRACE(("\tpath component '%s'\n", &temp[last])); 642 643 // we have a path component 644 vnode = devfs_find_in_dir(dir, &temp[last]); 645 if (vnode) { 646 if (!atLeaf) { 647 // we are not at the leaf of the path, so as long as 648 // this is a dir we're okay 649 if (S_ISDIR(vnode->stream.type)) { 650 last = i; 651 dir = vnode; 652 continue; 653 } 654 } 655 // we are at the leaf and hit another node 656 // or we aren't but hit a non-dir node. 657 // we're screwed 658 status = B_FILE_EXISTS; 659 goto out; 660 } else { 661 vnode = devfs_create_vnode(fs, dir, &temp[last]); 662 if (!vnode) { 663 status = B_NO_MEMORY; 664 goto out; 665 } 666 } 667 668 // set up the new vnode 669 if (!atLeaf) { 670 // this is a dir 671 init_directory_vnode(vnode, 0755); 672 publish_node(fs, dir, vnode); 673 } else { 674 // this is the last component 675 // Note: We do not yet insert the node into the directory, as it 676 // is not yet fully initialized. Instead we return the directory 677 // vnode so that the calling function can insert it after all 678 // initialization is done. This ensures that no create notification 679 // is sent out for a vnode that is not yet fully valid. 680 *_node = vnode; 681 *_dir = dir; 682 break; 683 } 684 685 last = i; 686 dir = vnode; 687 } 688 689 out: 690 return status; 691 } 692 693 694 static status_t 695 publish_device(struct devfs* fs, const char* path, BaseDevice* device) 696 { 697 TRACE(("publish_device(path = \"%s\", device = %p)\n", path, device)); 698 699 if (sDeviceFileSystem == NULL) { 700 panic("publish_device() called before devfs mounted\n"); 701 return B_ERROR; 702 } 703 704 if (device == NULL || path == NULL || path[0] == '\0' || path[0] == '/') 705 return B_BAD_VALUE; 706 707 // TODO: this has to be done in the BaseDevice sub classes! 708 #if 0 709 // are the provided device hooks okay? 710 if (info->device_open == NULL || info->device_close == NULL 711 || info->device_free == NULL 712 || ((info->device_read == NULL || info->device_write == NULL) 713 && info->device_io == NULL)) 714 return B_BAD_VALUE; 715 #endif 716 717 struct devfs_vnode* node; 718 struct devfs_vnode* dirNode; 719 status_t status; 720 721 RecursiveLocker locker(&fs->lock); 722 723 status = new_node(fs, path, &node, &dirNode); 724 if (status != B_OK) 725 return status; 726 727 // all went fine, let's initialize the node 728 node->stream.type = S_IFCHR | 0644; 729 node->stream.u.dev.device = device; 730 device->SetID(node->id); 731 732 // the node is now fully valid and we may insert it into the dir 733 publish_node(fs, dirNode, node); 734 return B_OK; 735 } 736 737 738 /*! Construct complete device name (as used for device_open()). 739 This is safe to use only when the device is in use (and therefore 740 cannot be unpublished during the iteration). 741 */ 742 static void 743 get_device_name(struct devfs_vnode* vnode, char* buffer, size_t size) 744 { 745 RecursiveLocker _(sDeviceFileSystem->lock); 746 747 struct devfs_vnode* leaf = vnode; 748 size_t offset = 0; 749 750 // count levels 751 752 for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) { 753 offset += strlen(vnode->name) + 1; 754 } 755 756 // construct full path name 757 758 for (vnode = leaf; vnode->parent && vnode->parent != vnode; 759 vnode = vnode->parent) { 760 size_t length = strlen(vnode->name); 761 size_t start = offset - length - 1; 762 763 if (size >= offset) { 764 strcpy(buffer + start, vnode->name); 765 if (vnode != leaf) 766 buffer[offset - 1] = '/'; 767 } 768 769 offset = start; 770 } 771 } 772 773 774 static status_t 775 device_read(void* _cookie, off_t offset, void* buffer, size_t* length) 776 { 777 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie; 778 return cookie->device->Read(cookie->cookie, offset, buffer, length); 779 } 780 781 782 static status_t 783 device_write(void* _cookie, off_t offset, void* buffer, size_t* length) 784 { 785 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie; 786 return cookie->device->Write(cookie->cookie, offset, buffer, length); 787 } 788 789 790 static int 791 dump_node(int argc, char **argv) 792 { 793 if (argc != 2) { 794 print_debugger_command_usage(argv[0]); 795 return 0; 796 } 797 798 struct devfs_vnode *vnode = (struct devfs_vnode *)parse_expression(argv[1]); 799 if (vnode == NULL) { 800 kprintf("invalid node address\n"); 801 return 0; 802 } 803 804 kprintf("DEVFS NODE: %p\n", vnode); 805 kprintf(" id: %Ld\n", vnode->id); 806 kprintf(" name: \"%s\"\n", vnode->name); 807 kprintf(" type: %x\n", vnode->stream.type); 808 kprintf(" parent: %p\n", vnode->parent); 809 kprintf(" dir next: %p\n", vnode->dir_next); 810 811 if (S_ISDIR(vnode->stream.type)) { 812 kprintf(" dir scanned: %ld\n", vnode->stream.u.dir.scanned); 813 kprintf(" contents:\n"); 814 815 devfs_vnode *children = vnode->stream.u.dir.dir_head; 816 while (children != NULL) { 817 kprintf(" %p, id %Ld\n", children, children->id); 818 children = children->dir_next; 819 } 820 } else if (S_ISLNK(vnode->stream.type)) { 821 kprintf(" symlink to: %s\n", vnode->stream.u.symlink.path); 822 } else { 823 kprintf(" device: %p\n", vnode->stream.u.dev.device); 824 kprintf(" partition: %p\n", vnode->stream.u.dev.partition); 825 if (vnode->stream.u.dev.partition != NULL) { 826 partition_info& info = vnode->stream.u.dev.partition->info; 827 kprintf(" raw device node: %p\n", 828 vnode->stream.u.dev.partition->raw_device); 829 kprintf(" offset: %Ld\n", info.offset); 830 kprintf(" size: %Ld\n", info.size); 831 kprintf(" block size: %ld\n", info.logical_block_size); 832 kprintf(" session: %ld\n", info.session); 833 kprintf(" partition: %ld\n", info.partition); 834 kprintf(" device: %s\n", info.device); 835 set_debug_variable("_raw", 836 (addr_t)vnode->stream.u.dev.partition->raw_device); 837 } 838 } 839 840 return 0; 841 } 842 843 844 static int 845 dump_cookie(int argc, char** argv) 846 { 847 if (argc != 2) { 848 print_debugger_command_usage(argv[0]); 849 return 0; 850 } 851 852 uint64 address; 853 if (!evaluate_debug_expression(argv[1], &address, false)) 854 return 0; 855 856 struct devfs_cookie* cookie = (devfs_cookie*)(addr_t)address; 857 858 kprintf("DEVFS COOKIE: %p\n", cookie); 859 kprintf(" device_cookie: %p\n", cookie->device_cookie); 860 861 return 0; 862 } 863 864 865 // #pragma mark - file system interface 866 867 868 static status_t 869 devfs_mount(fs_volume *volume, const char *devfs, uint32 flags, 870 const char *args, ino_t *root_vnid) 871 { 872 struct devfs_vnode *vnode; 873 struct devfs *fs; 874 status_t err; 875 876 TRACE(("devfs_mount: entry\n")); 877 878 if (sDeviceFileSystem) { 879 TRACE(("double mount of devfs attempted\n")); 880 err = B_ERROR; 881 goto err; 882 } 883 884 fs = (struct devfs *)malloc(sizeof(struct devfs)); 885 if (fs == NULL) { 886 err = B_NO_MEMORY; 887 goto err; 888 } 889 890 volume->private_volume = fs; 891 volume->ops = &kVolumeOps; 892 fs->volume = volume; 893 fs->id = volume->id; 894 fs->next_vnode_id = 0; 895 896 recursive_lock_init(&fs->lock, "devfs lock"); 897 898 fs->vnode_hash = hash_init(DEVFS_HASH_SIZE, offsetof(devfs_vnode, all_next), 899 //(addr_t)&vnode->all_next - (addr_t)vnode, 900 &devfs_vnode_compare, &devfs_vnode_hash); 901 if (fs->vnode_hash == NULL) { 902 err = B_NO_MEMORY; 903 goto err2; 904 } 905 906 // create a vnode 907 vnode = devfs_create_vnode(fs, NULL, ""); 908 if (vnode == NULL) { 909 err = B_NO_MEMORY; 910 goto err3; 911 } 912 913 // set it up 914 vnode->parent = vnode; 915 916 // create a dir stream for it to hold 917 init_directory_vnode(vnode, 0755); 918 fs->root_vnode = vnode; 919 920 hash_insert(fs->vnode_hash, vnode); 921 publish_vnode(volume, vnode->id, vnode, &kVnodeOps, vnode->stream.type, 0); 922 923 *root_vnid = vnode->id; 924 sDeviceFileSystem = fs; 925 return B_OK; 926 927 err3: 928 hash_uninit(fs->vnode_hash); 929 err2: 930 recursive_lock_destroy(&fs->lock); 931 free(fs); 932 err: 933 return err; 934 } 935 936 937 static status_t 938 devfs_unmount(fs_volume *_volume) 939 { 940 struct devfs *fs = (struct devfs *)_volume->private_volume; 941 struct devfs_vnode *vnode; 942 struct hash_iterator i; 943 944 TRACE(("devfs_unmount: entry fs = %p\n", fs)); 945 946 recursive_lock_lock(&fs->lock); 947 948 // release the reference to the root 949 put_vnode(fs->volume, fs->root_vnode->id); 950 951 // delete all of the vnodes 952 hash_open(fs->vnode_hash, &i); 953 while ((vnode = (devfs_vnode *)hash_next(fs->vnode_hash, &i)) != NULL) { 954 devfs_delete_vnode(fs, vnode, true); 955 } 956 hash_close(fs->vnode_hash, &i, false); 957 hash_uninit(fs->vnode_hash); 958 959 recursive_lock_destroy(&fs->lock); 960 free(fs); 961 962 return B_OK; 963 } 964 965 966 static status_t 967 devfs_sync(fs_volume *_volume) 968 { 969 TRACE(("devfs_sync: entry\n")); 970 971 return B_OK; 972 } 973 974 975 static status_t 976 devfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) 977 { 978 struct devfs *fs = (struct devfs *)_volume->private_volume; 979 struct devfs_vnode *dir = (struct devfs_vnode *)_dir->private_node; 980 struct devfs_vnode *vnode; 981 status_t status; 982 983 TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name)); 984 985 if (!S_ISDIR(dir->stream.type)) 986 return B_NOT_A_DIRECTORY; 987 988 // Make sure the directory contents are up to date 989 scan_for_drivers_if_needed(dir); 990 991 RecursiveLocker locker(&fs->lock); 992 993 // look it up 994 vnode = devfs_find_in_dir(dir, name); 995 if (vnode == NULL) { 996 // We don't have to rescan here, because thanks to node monitoring 997 // we already know it does not exist 998 return B_ENTRY_NOT_FOUND; 999 } 1000 1001 status = get_vnode(fs->volume, vnode->id, NULL); 1002 if (status < B_OK) 1003 return status; 1004 1005 *_id = vnode->id; 1006 1007 return B_OK; 1008 } 1009 1010 1011 static status_t 1012 devfs_get_vnode_name(fs_volume *_volume, fs_vnode *_vnode, char *buffer, 1013 size_t bufferSize) 1014 { 1015 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1016 1017 TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode)); 1018 1019 strlcpy(buffer, vnode->name, bufferSize); 1020 return B_OK; 1021 } 1022 1023 1024 static status_t 1025 devfs_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_vnode, int *_type, 1026 uint32 *_flags, bool reenter) 1027 { 1028 struct devfs *fs = (struct devfs *)_volume->private_volume; 1029 1030 TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter)); 1031 1032 RecursiveLocker _(fs->lock); 1033 1034 struct devfs_vnode *vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id); 1035 if (vnode == NULL) 1036 return B_ENTRY_NOT_FOUND; 1037 1038 TRACE(("devfs_get_vnode: looked it up at %p\n", vnode)); 1039 1040 _vnode->private_node = vnode; 1041 _vnode->ops = &kVnodeOps; 1042 *_type = vnode->stream.type; 1043 *_flags = 0; 1044 return B_OK; 1045 } 1046 1047 1048 static status_t 1049 devfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter) 1050 { 1051 #ifdef TRACE_DEVFS 1052 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1053 1054 TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", 1055 vnode, vnode->id, reenter)); 1056 #endif 1057 1058 return B_OK; 1059 } 1060 1061 1062 static status_t 1063 devfs_remove_vnode(fs_volume *_volume, fs_vnode *_v, bool reenter) 1064 { 1065 struct devfs *fs = (struct devfs *)_volume->private_volume; 1066 struct devfs_vnode *vnode = (struct devfs_vnode *)_v->private_node; 1067 1068 TRACE(("devfs_removevnode: remove %p (%Ld), reenter %d\n", vnode, vnode->id, reenter)); 1069 1070 RecursiveLocker locker(&fs->lock); 1071 1072 if (vnode->dir_next) { 1073 // can't remove node if it's linked to the dir 1074 panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode); 1075 } 1076 1077 devfs_delete_vnode(fs, vnode, false); 1078 1079 return B_OK; 1080 } 1081 1082 1083 static status_t 1084 devfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int openMode, 1085 int perms, void** _cookie, ino_t* _newVnodeID) 1086 { 1087 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 1088 struct devfs* fs = (struct devfs*)_volume->private_volume; 1089 struct devfs_cookie* cookie; 1090 struct devfs_vnode* vnode; 1091 status_t status = B_OK; 1092 1093 TRACE(("devfs_create: dir %p, name \"%s\", openMode 0x%x, fs_cookie %p \n", dir, name, openMode, _cookie)); 1094 1095 RecursiveLocker locker(fs->lock); 1096 1097 // look it up 1098 vnode = devfs_find_in_dir(dir, name); 1099 if (vnode == NULL) 1100 return EROFS; 1101 1102 if (openMode & O_EXCL) 1103 return B_FILE_EXISTS; 1104 1105 status = get_vnode(fs->volume, vnode->id, NULL); 1106 if (status < B_OK) 1107 return status; 1108 1109 locker.Unlock(); 1110 1111 *_newVnodeID = vnode->id; 1112 1113 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1114 if (cookie == NULL) { 1115 status = B_NO_MEMORY; 1116 goto err1; 1117 } 1118 1119 if (S_ISCHR(vnode->stream.type)) { 1120 BaseDevice* device = vnode->stream.u.dev.device; 1121 status = device->InitDevice(); 1122 if (status < B_OK) 1123 return status; 1124 1125 char path[B_FILE_NAME_LENGTH]; 1126 get_device_name(vnode, path, sizeof(path)); 1127 1128 status = device->Open(path, openMode, &cookie->device_cookie); 1129 if (status != B_OK) 1130 device->UninitDevice(); 1131 } 1132 if (status < B_OK) 1133 goto err2; 1134 1135 *_cookie = cookie; 1136 return B_OK; 1137 1138 err2: 1139 free(cookie); 1140 err1: 1141 put_vnode(fs->volume, vnode->id); 1142 return status; 1143 } 1144 1145 1146 static status_t 1147 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode, 1148 void** _cookie) 1149 { 1150 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1151 struct devfs_cookie* cookie; 1152 status_t status = B_OK; 1153 1154 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1155 if (cookie == NULL) 1156 return B_NO_MEMORY; 1157 1158 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode, 1159 cookie)); 1160 1161 cookie->device_cookie = NULL; 1162 1163 if (S_ISCHR(vnode->stream.type)) { 1164 BaseDevice* device = vnode->stream.u.dev.device; 1165 status = device->InitDevice(); 1166 if (status != B_OK) 1167 return status; 1168 1169 char path[B_FILE_NAME_LENGTH]; 1170 get_device_name(vnode, path, sizeof(path)); 1171 1172 status = device->Open(path, openMode, &cookie->device_cookie); 1173 if (status != B_OK) 1174 device->UninitDevice(); 1175 } 1176 1177 if (status != B_OK) 1178 free(cookie); 1179 else 1180 *_cookie = cookie; 1181 1182 return status; 1183 } 1184 1185 1186 static status_t 1187 devfs_close(fs_volume *_volume, fs_vnode *_vnode, void *_cookie) 1188 { 1189 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1190 struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; 1191 1192 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie)); 1193 1194 if (S_ISCHR(vnode->stream.type)) { 1195 // pass the call through to the underlying device 1196 return vnode->stream.u.dev.device->Close(cookie->device_cookie); 1197 } 1198 1199 return B_OK; 1200 } 1201 1202 1203 static status_t 1204 devfs_free_cookie(fs_volume *_volume, fs_vnode *_vnode, void *_cookie) 1205 { 1206 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1207 struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; 1208 1209 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1210 1211 if (S_ISCHR(vnode->stream.type)) { 1212 // pass the call through to the underlying device 1213 vnode->stream.u.dev.device->Free(cookie->device_cookie); 1214 vnode->stream.u.dev.device->UninitDevice(); 1215 } 1216 1217 free(cookie); 1218 return B_OK; 1219 } 1220 1221 1222 static status_t 1223 devfs_fsync(fs_volume *_volume, fs_vnode *_v) 1224 { 1225 return B_OK; 1226 } 1227 1228 1229 static status_t 1230 devfs_read_link(fs_volume *_volume, fs_vnode *_link, char *buffer, 1231 size_t *_bufferSize) 1232 { 1233 struct devfs_vnode *link = (struct devfs_vnode *)_link->private_node; 1234 1235 if (!S_ISLNK(link->stream.type)) 1236 return B_BAD_VALUE; 1237 1238 if (link->stream.u.symlink.length < *_bufferSize) 1239 *_bufferSize = link->stream.u.symlink.length; 1240 1241 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize); 1242 return B_OK; 1243 } 1244 1245 1246 static status_t 1247 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1248 void* buffer, size_t* _length) 1249 { 1250 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1251 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1252 1253 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n", 1254 // vnode, cookie, pos, _length)); 1255 1256 if (!S_ISCHR(vnode->stream.type)) 1257 return B_BAD_VALUE; 1258 1259 if (pos < 0) 1260 return B_BAD_VALUE; 1261 1262 if (vnode->stream.u.dev.partition != NULL) { 1263 if (pos >= vnode->stream.u.dev.partition->info.size) 1264 return B_BAD_VALUE; 1265 1266 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1267 } 1268 1269 if (*_length == 0) 1270 return B_OK; 1271 1272 // pass the call through to the device 1273 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer, 1274 _length); 1275 } 1276 1277 1278 static status_t 1279 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1280 const void* buffer, size_t* _length) 1281 { 1282 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1283 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1284 1285 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n", 1286 // vnode, cookie, pos, _length)); 1287 1288 if (!S_ISCHR(vnode->stream.type)) 1289 return B_BAD_VALUE; 1290 1291 if (pos < 0) 1292 return B_BAD_VALUE; 1293 1294 if (vnode->stream.u.dev.partition != NULL) { 1295 if (pos >= vnode->stream.u.dev.partition->info.size) 1296 return B_BAD_VALUE; 1297 1298 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1299 } 1300 1301 if (*_length == 0) 1302 return B_OK; 1303 1304 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer, 1305 _length); 1306 } 1307 1308 1309 static status_t 1310 devfs_create_dir(fs_volume *_volume, fs_vnode *_dir, const char *name, 1311 int perms) 1312 { 1313 struct devfs *fs = (struct devfs *)_volume->private_volume; 1314 struct devfs_vnode *dir = (struct devfs_vnode *)_dir->private_node; 1315 1316 struct devfs_vnode *vnode = devfs_find_in_dir(dir, name); 1317 if (vnode != NULL) { 1318 return EEXIST; 1319 } 1320 1321 vnode = devfs_create_vnode(fs, dir, name); 1322 if (vnode == NULL) { 1323 return B_NO_MEMORY; 1324 } 1325 1326 // set up the new directory 1327 init_directory_vnode(vnode, perms); 1328 publish_node(sDeviceFileSystem, dir, vnode); 1329 1330 return B_OK; 1331 } 1332 1333 1334 static status_t 1335 devfs_open_dir(fs_volume *_volume, fs_vnode *_vnode, void **_cookie) 1336 { 1337 struct devfs *fs = (struct devfs *)_volume->private_volume; 1338 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1339 struct devfs_dir_cookie *cookie; 1340 1341 TRACE(("devfs_open_dir: vnode %p\n", vnode)); 1342 1343 if (!S_ISDIR(vnode->stream.type)) 1344 return B_BAD_VALUE; 1345 1346 cookie = (devfs_dir_cookie *)malloc(sizeof(devfs_dir_cookie)); 1347 if (cookie == NULL) 1348 return B_NO_MEMORY; 1349 1350 // make sure the directory has up-to-date contents 1351 scan_for_drivers_if_needed(vnode); 1352 1353 RecursiveLocker locker(&fs->lock); 1354 1355 cookie->current = vnode->stream.u.dir.dir_head; 1356 cookie->state = ITERATION_STATE_BEGIN; 1357 1358 list_add_item(&vnode->stream.u.dir.cookies, cookie); 1359 *_cookie = cookie; 1360 1361 return B_OK; 1362 } 1363 1364 1365 static status_t 1366 devfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_vnode, void *_cookie) 1367 { 1368 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1369 struct devfs_dir_cookie *cookie = (devfs_dir_cookie *)_cookie; 1370 struct devfs *fs = (struct devfs *)_volume->private_volume; 1371 1372 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1373 1374 RecursiveLocker locker(&fs->lock); 1375 1376 list_remove_item(&vnode->stream.u.dir.cookies, cookie); 1377 free(cookie); 1378 return B_OK; 1379 } 1380 1381 1382 static status_t 1383 devfs_read_dir(fs_volume *_volume, fs_vnode *_vnode, void *_cookie, 1384 struct dirent *dirent, size_t bufferSize, uint32 *_num) 1385 { 1386 struct devfs_vnode *vnode = (devfs_vnode *)_vnode->private_node; 1387 struct devfs_dir_cookie *cookie = (devfs_dir_cookie *)_cookie; 1388 struct devfs *fs = (struct devfs *)_volume->private_volume; 1389 status_t status = B_OK; 1390 struct devfs_vnode *childNode = NULL; 1391 const char *name = NULL; 1392 struct devfs_vnode *nextChildNode = NULL; 1393 int32 nextState = cookie->state; 1394 1395 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n", 1396 _vnode, cookie, dirent, bufferSize)); 1397 1398 if (!S_ISDIR(vnode->stream.type)) 1399 return B_BAD_VALUE; 1400 1401 RecursiveLocker locker(&fs->lock); 1402 1403 switch (cookie->state) { 1404 case ITERATION_STATE_DOT: 1405 childNode = vnode; 1406 name = "."; 1407 nextChildNode = vnode->stream.u.dir.dir_head; 1408 nextState = cookie->state + 1; 1409 break; 1410 case ITERATION_STATE_DOT_DOT: 1411 childNode = vnode->parent; 1412 name = ".."; 1413 nextChildNode = vnode->stream.u.dir.dir_head; 1414 nextState = cookie->state + 1; 1415 break; 1416 default: 1417 childNode = cookie->current; 1418 if (childNode) { 1419 name = childNode->name; 1420 nextChildNode = childNode->dir_next; 1421 } 1422 break; 1423 } 1424 1425 if (!childNode) { 1426 *_num = 0; 1427 return B_OK; 1428 } 1429 1430 dirent->d_dev = fs->id; 1431 dirent->d_ino = childNode->id; 1432 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 1433 1434 if (dirent->d_reclen > bufferSize) 1435 return ENOBUFS; 1436 1437 status = user_strlcpy(dirent->d_name, name, 1438 bufferSize - sizeof(struct dirent)); 1439 if (status < B_OK) 1440 return status; 1441 1442 cookie->current = nextChildNode; 1443 cookie->state = nextState; 1444 *_num = 1; 1445 1446 return B_OK; 1447 } 1448 1449 1450 static status_t 1451 devfs_rewind_dir(fs_volume *_volume, fs_vnode *_vnode, void *_cookie) 1452 { 1453 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1454 struct devfs_dir_cookie *cookie = (devfs_dir_cookie *)_cookie; 1455 struct devfs *fs = (struct devfs *)_volume->private_volume; 1456 1457 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie)); 1458 1459 if (!S_ISDIR(vnode->stream.type)) 1460 return B_BAD_VALUE; 1461 1462 RecursiveLocker locker(&fs->lock); 1463 1464 cookie->current = vnode->stream.u.dir.dir_head; 1465 cookie->state = ITERATION_STATE_BEGIN; 1466 1467 return B_OK; 1468 } 1469 1470 1471 /*! Forwards the opcode to the device driver, but also handles some devfs 1472 specific functionality, like partitions. 1473 */ 1474 static status_t 1475 devfs_ioctl(fs_volume *_volume, fs_vnode *_vnode, void *_cookie, uint32 op, 1476 void *buffer, size_t length) 1477 { 1478 struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode->private_node; 1479 struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; 1480 1481 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", 1482 vnode, cookie, op, buffer, length)); 1483 1484 // we are actually checking for a *device* here, we don't make the 1485 // distinction between char and block devices 1486 if (S_ISCHR(vnode->stream.type)) { 1487 switch (op) { 1488 case B_GET_GEOMETRY: 1489 { 1490 struct devfs_partition* partition 1491 = vnode->stream.u.dev.partition; 1492 if (partition == NULL) 1493 break; 1494 1495 device_geometry geometry; 1496 status_t status = vnode->stream.u.dev.device->Control( 1497 cookie->device_cookie, op, &geometry, length); 1498 if (status < B_OK) 1499 return status; 1500 1501 // patch values to match partition size 1502 geometry.sectors_per_track = 0; 1503 if (geometry.bytes_per_sector == 0) 1504 geometry.bytes_per_sector = 512; 1505 geometry.sectors_per_track = partition->info.size 1506 / geometry.bytes_per_sector; 1507 geometry.head_count = 1; 1508 geometry.cylinder_count = 1; 1509 1510 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 1511 } 1512 1513 case B_GET_DRIVER_FOR_DEVICE: 1514 { 1515 #if 0 1516 const char* path; 1517 if (!vnode->stream.u.dev.driver) 1518 return B_ENTRY_NOT_FOUND; 1519 path = vnode->stream.u.dev.driver->path; 1520 if (path == NULL) 1521 return B_ENTRY_NOT_FOUND; 1522 1523 return user_strlcpy((char *)buffer, path, B_FILE_NAME_LENGTH); 1524 #endif 1525 return B_ERROR; 1526 } 1527 1528 case B_GET_PARTITION_INFO: 1529 { 1530 struct devfs_partition* partition 1531 = vnode->stream.u.dev.partition; 1532 if (!S_ISCHR(vnode->stream.type) 1533 || partition == NULL 1534 || length != sizeof(partition_info)) 1535 return B_BAD_VALUE; 1536 1537 return user_memcpy(buffer, &partition->info, 1538 sizeof(partition_info)); 1539 } 1540 1541 case B_SET_PARTITION: 1542 return B_NOT_ALLOWED; 1543 1544 case B_GET_PATH_FOR_DEVICE: 1545 { 1546 char path[256]; 1547 /* TODO: we might want to actually find the mountpoint 1548 * of that instance of devfs... 1549 * but for now we assume it's mounted on /dev 1550 */ 1551 strcpy(path, "/dev/"); 1552 get_device_name(vnode, path + 5, sizeof(path) - 5); 1553 if (length && (length <= strlen(path))) 1554 return ERANGE; 1555 return user_strlcpy((char *)buffer, path, sizeof(path)); 1556 } 1557 1558 // old unsupported R5 private stuff 1559 1560 case B_GET_NEXT_OPEN_DEVICE: 1561 dprintf("devfs: unsupported legacy ioctl B_GET_NEXT_OPEN_DEVICE\n"); 1562 return B_NOT_SUPPORTED; 1563 case B_ADD_FIXED_DRIVER: 1564 dprintf("devfs: unsupported legacy ioctl B_ADD_FIXED_DRIVER\n"); 1565 return B_NOT_SUPPORTED; 1566 case B_REMOVE_FIXED_DRIVER: 1567 dprintf("devfs: unsupported legacy ioctl B_REMOVE_FIXED_DRIVER\n"); 1568 return B_NOT_SUPPORTED; 1569 1570 } 1571 1572 return vnode->stream.u.dev.device->Control(cookie->device_cookie, 1573 op, buffer, length); 1574 } 1575 1576 return B_BAD_VALUE; 1577 } 1578 1579 1580 static status_t 1581 devfs_set_flags(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1582 int flags) 1583 { 1584 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1585 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1586 1587 // we need to pass the O_NONBLOCK flag to the underlying device 1588 1589 if (!S_ISCHR(vnode->stream.type)) 1590 return B_NOT_ALLOWED; 1591 1592 return vnode->stream.u.dev.device->Control(cookie->device_cookie, 1593 flags & O_NONBLOCK ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0); 1594 } 1595 1596 1597 static status_t 1598 devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1599 uint8 event, selectsync* sync) 1600 { 1601 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1602 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1603 1604 if (!S_ISCHR(vnode->stream.type)) 1605 return B_NOT_ALLOWED; 1606 1607 // If the device has no select() hook, notify select() now. 1608 if (!vnode->stream.u.dev.device->HasSelect()) 1609 return notify_select_event((selectsync*)sync, event); 1610 1611 return vnode->stream.u.dev.device->Select(cookie->device_cookie, event, 1612 (selectsync*)sync); 1613 } 1614 1615 1616 static status_t 1617 devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1618 uint8 event, selectsync* sync) 1619 { 1620 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1621 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1622 1623 if (!S_ISCHR(vnode->stream.type)) 1624 return B_NOT_ALLOWED; 1625 1626 // If the device has no select() hook, notify select() now. 1627 if (!vnode->stream.u.dev.device->HasDeselect()) 1628 return B_OK; 1629 1630 return vnode->stream.u.dev.device->Deselect(cookie->device_cookie, event, 1631 (selectsync*)sync); 1632 } 1633 1634 1635 static bool 1636 devfs_can_page(fs_volume *_volume, fs_vnode *_vnode, void *cookie) 1637 { 1638 #if 0 1639 struct devfs_vnode *vnode = (devfs_vnode *)_vnode->private_node; 1640 1641 //TRACE(("devfs_canpage: vnode %p\n", vnode)); 1642 1643 if (!S_ISCHR(vnode->stream.type) 1644 || vnode->stream.u.dev.device->Node() == NULL 1645 || cookie == NULL) 1646 return false; 1647 1648 return vnode->stream.u.dev.device->HasRead() 1649 || vnode->stream.u.dev.device->HasIO(); 1650 #endif 1651 // TODO: Obsolete hook! 1652 return false; 1653 } 1654 1655 1656 static status_t 1657 devfs_read_pages(fs_volume *_volume, fs_vnode *_vnode, void *_cookie, 1658 off_t pos, const iovec *vecs, size_t count, size_t *_numBytes) 1659 { 1660 struct devfs_vnode *vnode = (devfs_vnode *)_vnode->private_node; 1661 struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; 1662 1663 //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes)); 1664 1665 if (!S_ISCHR(vnode->stream.type) 1666 || (!vnode->stream.u.dev.device->HasRead() 1667 && !vnode->stream.u.dev.device->HasIO()) 1668 || cookie == NULL) 1669 return B_NOT_ALLOWED; 1670 1671 if (pos < 0) 1672 return B_BAD_VALUE; 1673 1674 if (vnode->stream.u.dev.partition != NULL) { 1675 if (pos >= vnode->stream.u.dev.partition->info.size) 1676 return B_BAD_VALUE; 1677 1678 translate_partition_access(vnode->stream.u.dev.partition, pos, 1679 *_numBytes); 1680 } 1681 1682 if (vnode->stream.u.dev.device->HasIO()) { 1683 // TODO: use io_requests for this! 1684 } 1685 1686 // emulate read_pages() using read() 1687 1688 status_t error = B_OK; 1689 size_t bytesTransferred = 0; 1690 1691 size_t remainingBytes = *_numBytes; 1692 for (size_t i = 0; i < count && remainingBytes > 0; i++) { 1693 size_t toRead = min_c(vecs[i].iov_len, remainingBytes); 1694 size_t length = toRead; 1695 1696 error = vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, 1697 vecs[i].iov_base, &length); 1698 if (error != B_OK) 1699 break; 1700 1701 pos += length; 1702 bytesTransferred += length; 1703 remainingBytes -= length; 1704 1705 if (length < toRead) 1706 break; 1707 } 1708 1709 *_numBytes = bytesTransferred; 1710 1711 return bytesTransferred > 0 ? B_OK : error; 1712 } 1713 1714 1715 static status_t 1716 devfs_write_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1717 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 1718 { 1719 struct devfs_vnode *vnode = (devfs_vnode *)_vnode->private_node; 1720 struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie; 1721 1722 //TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes)); 1723 1724 if (!S_ISCHR(vnode->stream.type) 1725 || (!vnode->stream.u.dev.device->HasWrite() 1726 && !vnode->stream.u.dev.device->HasIO()) 1727 || cookie == NULL) 1728 return B_NOT_ALLOWED; 1729 1730 if (pos < 0) 1731 return B_BAD_VALUE; 1732 1733 if (vnode->stream.u.dev.partition != NULL) { 1734 if (pos >= vnode->stream.u.dev.partition->info.size) 1735 return B_BAD_VALUE; 1736 1737 translate_partition_access(vnode->stream.u.dev.partition, pos, 1738 *_numBytes); 1739 } 1740 1741 if (vnode->stream.u.dev.device->HasIO()) { 1742 // TODO: use io_requests for this! 1743 } 1744 1745 // emulate write_pages() using write() 1746 1747 status_t error = B_OK; 1748 size_t bytesTransferred = 0; 1749 1750 size_t remainingBytes = *_numBytes; 1751 for (size_t i = 0; i < count && remainingBytes > 0; i++) { 1752 size_t toWrite = min_c(vecs[i].iov_len, remainingBytes); 1753 size_t length = toWrite; 1754 1755 error = vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, 1756 vecs[i].iov_base, &length); 1757 if (error != B_OK) 1758 break; 1759 1760 pos += length; 1761 bytesTransferred += length; 1762 remainingBytes -= length; 1763 1764 if (length < toWrite) 1765 break; 1766 } 1767 1768 *_numBytes = bytesTransferred; 1769 1770 return bytesTransferred > 0 ? B_OK : error; 1771 } 1772 1773 1774 static status_t 1775 devfs_io(fs_volume *volume, fs_vnode *_vnode, void *_cookie, 1776 io_request *request) 1777 { 1778 TRACE(("[%ld] devfs_io(request: %p)\n", find_thread(NULL), request)); 1779 1780 devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1781 devfs_cookie* cookie = (devfs_cookie*)_cookie; 1782 1783 bool isWrite = request->IsWrite(); 1784 1785 if (!S_ISCHR(vnode->stream.type) 1786 || (((isWrite && !vnode->stream.u.dev.device->HasWrite()) 1787 || (!isWrite && !vnode->stream.u.dev.device->HasRead())) 1788 && !vnode->stream.u.dev.device->HasIO()) 1789 || cookie == NULL) { 1790 request->SetStatusAndNotify(B_NOT_ALLOWED); 1791 return B_NOT_ALLOWED; 1792 } 1793 1794 if (vnode->stream.u.dev.partition != NULL) { 1795 if (request->Offset() + request->Length() 1796 > vnode->stream.u.dev.partition->info.size) { 1797 request->SetStatusAndNotify(B_BAD_VALUE); 1798 return B_BAD_VALUE; 1799 } 1800 translate_partition_access(vnode->stream.u.dev.partition, request); 1801 } 1802 1803 if (vnode->stream.u.dev.device->HasIO()) 1804 return vnode->stream.u.dev.device->IO(cookie->device_cookie, request); 1805 1806 synchronous_io_cookie synchronousCookie = { 1807 vnode->stream.u.dev.device, 1808 cookie->device_cookie 1809 }; 1810 1811 return vfs_synchronous_io(request, 1812 request->IsWrite() ? &device_write : &device_read, &synchronousCookie); 1813 } 1814 1815 1816 static status_t 1817 devfs_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* stat) 1818 { 1819 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1820 1821 TRACE(("devfs_read_stat: vnode %p (%Ld), stat %p\n", vnode, vnode->id, 1822 stat)); 1823 1824 stat->st_ino = vnode->id; 1825 stat->st_size = 0; 1826 stat->st_mode = vnode->stream.type; 1827 1828 stat->st_nlink = 1; 1829 stat->st_blksize = 65536; 1830 stat->st_blocks = 0; 1831 1832 stat->st_uid = vnode->uid; 1833 stat->st_gid = vnode->gid; 1834 1835 stat->st_atim = current_timespec(); 1836 stat->st_mtim = stat->st_ctim = vnode->modification_time; 1837 stat->st_crtim = vnode->creation_time; 1838 1839 // TODO: this only works for partitions right now - if we should decide 1840 // to keep this feature, we should have a better solution 1841 if (S_ISCHR(vnode->stream.type)) { 1842 //device_geometry geometry; 1843 1844 // if it's a real block device, then let's report a useful size 1845 if (vnode->stream.u.dev.partition != NULL) { 1846 stat->st_size = vnode->stream.u.dev.partition->info.size; 1847 #if 0 1848 } else if (vnode->stream.u.dev.info->control(cookie->device_cookie, 1849 B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) { 1850 stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count 1851 * geometry.sectors_per_track * geometry.bytes_per_sector; 1852 #endif 1853 } 1854 1855 // is this a real block device? then let's have it reported like that 1856 if (stat->st_size != 0) 1857 stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK); 1858 } else if (S_ISLNK(vnode->stream.type)) { 1859 stat->st_size = vnode->stream.u.symlink.length; 1860 } 1861 1862 return B_OK; 1863 } 1864 1865 1866 static status_t 1867 devfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat, 1868 uint32 statMask) 1869 { 1870 struct devfs* fs = (struct devfs*)_volume->private_volume; 1871 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1872 1873 TRACE(("devfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, 1874 stat)); 1875 1876 // we cannot change the size of anything 1877 if (statMask & B_STAT_SIZE) 1878 return B_BAD_VALUE; 1879 1880 RecursiveLocker locker(&fs->lock); 1881 1882 if (statMask & B_STAT_MODE) { 1883 vnode->stream.type = (vnode->stream.type & ~S_IUMSK) 1884 | (stat->st_mode & S_IUMSK); 1885 } 1886 1887 if (statMask & B_STAT_UID) 1888 vnode->uid = stat->st_uid; 1889 if (statMask & B_STAT_GID) 1890 vnode->gid = stat->st_gid; 1891 1892 if (statMask & B_STAT_MODIFICATION_TIME) 1893 vnode->modification_time = stat->st_mtim; 1894 if (statMask & B_STAT_CREATION_TIME) 1895 vnode->creation_time = stat->st_crtim; 1896 1897 notify_stat_changed(fs->id, vnode->id, statMask); 1898 return B_OK; 1899 } 1900 1901 1902 static status_t 1903 devfs_std_ops(int32 op, ...) 1904 { 1905 switch (op) { 1906 case B_MODULE_INIT: 1907 add_debugger_command_etc("devfs_node", &dump_node, 1908 "Print info on a private devfs node", 1909 "<address>\n" 1910 "Prints information on a devfs node given by <address>.\n", 1911 0); 1912 add_debugger_command_etc("devfs_cookie", &dump_cookie, 1913 "Print info on a private devfs cookie", 1914 "<address>\n" 1915 "Prints information on a devfs cookie given by <address>.\n", 1916 0); 1917 1918 legacy_driver_init(); 1919 return B_OK; 1920 1921 case B_MODULE_UNINIT: 1922 remove_debugger_command("devfs_node", &dump_node); 1923 remove_debugger_command("devfs_cookie", &dump_cookie); 1924 return B_OK; 1925 1926 default: 1927 return B_ERROR; 1928 } 1929 } 1930 1931 namespace { 1932 1933 fs_volume_ops kVolumeOps = { 1934 &devfs_unmount, 1935 NULL, 1936 NULL, 1937 &devfs_sync, 1938 &devfs_get_vnode, 1939 1940 // the other operations are not supported (attributes, indices, queries) 1941 NULL, 1942 }; 1943 1944 fs_vnode_ops kVnodeOps = { 1945 &devfs_lookup, 1946 &devfs_get_vnode_name, 1947 1948 &devfs_put_vnode, 1949 &devfs_remove_vnode, 1950 1951 &devfs_can_page, 1952 &devfs_read_pages, 1953 &devfs_write_pages, 1954 1955 &devfs_io, 1956 NULL, // cancel_io() 1957 1958 NULL, // get_file_map 1959 1960 /* common */ 1961 &devfs_ioctl, 1962 &devfs_set_flags, 1963 &devfs_select, 1964 &devfs_deselect, 1965 &devfs_fsync, 1966 1967 &devfs_read_link, 1968 NULL, // symlink 1969 NULL, // link 1970 NULL, // unlink 1971 NULL, // rename 1972 1973 NULL, // access 1974 &devfs_read_stat, 1975 &devfs_write_stat, 1976 1977 /* file */ 1978 &devfs_create, 1979 &devfs_open, 1980 &devfs_close, 1981 &devfs_free_cookie, 1982 &devfs_read, 1983 &devfs_write, 1984 1985 /* directory */ 1986 &devfs_create_dir, 1987 NULL, // remove_dir 1988 &devfs_open_dir, 1989 &devfs_close, 1990 // same as for files - it does nothing for directories, anyway 1991 &devfs_free_dir_cookie, 1992 &devfs_read_dir, 1993 &devfs_rewind_dir, 1994 1995 // attributes operations are not supported 1996 NULL, 1997 }; 1998 1999 } // namespace 2000 2001 file_system_module_info gDeviceFileSystem = { 2002 { 2003 "file_systems/devfs" B_CURRENT_FS_API_VERSION, 2004 0, 2005 devfs_std_ops, 2006 }, 2007 2008 "devfs", // short_name 2009 "Device File System", // pretty_name 2010 0, // DDM flags 2011 2012 NULL, // identify_partition() 2013 NULL, // scan_partition() 2014 NULL, // free_identify_partition_cookie() 2015 NULL, // free_partition_content_cookie() 2016 2017 &devfs_mount, 2018 }; 2019 2020 2021 // #pragma mark - kernel private API 2022 2023 2024 extern "C" status_t 2025 devfs_unpublish_file_device(const char *path) 2026 { 2027 // get the device node 2028 devfs_vnode* node; 2029 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2030 if (status != B_OK) 2031 return status; 2032 2033 if (!S_ISCHR(node->stream.type)) { 2034 put_vnode(sDeviceFileSystem->volume, node->id); 2035 return B_BAD_VALUE; 2036 } 2037 2038 // if it is indeed a file device, unpublish it 2039 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device); 2040 if (device == NULL) { 2041 put_vnode(sDeviceFileSystem->volume, node->id); 2042 return B_BAD_VALUE; 2043 } 2044 2045 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2046 2047 put_vnode(sDeviceFileSystem->volume, node->id); 2048 return status; 2049 } 2050 2051 2052 extern "C" status_t 2053 devfs_publish_file_device(const char *path, const char *filePath) 2054 { 2055 // create a FileDevice for the file 2056 FileDevice* device = new(std::nothrow) FileDevice; 2057 if (device == NULL) 2058 return B_NO_MEMORY; 2059 ObjectDeleter<FileDevice> deviceDeleter(device); 2060 2061 status_t error = device->Init(filePath); 2062 if (error != B_OK) 2063 return error; 2064 2065 // publish the device 2066 error = publish_device(sDeviceFileSystem, path, device); 2067 if (error != B_OK) 2068 return error; 2069 2070 deviceDeleter.Detach(); 2071 return B_OK; 2072 } 2073 2074 2075 extern "C" status_t 2076 devfs_unpublish_partition(const char* path) 2077 { 2078 devfs_vnode *node; 2079 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2080 if (status != B_OK) 2081 return status; 2082 2083 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2084 put_vnode(sDeviceFileSystem->volume, node->id); 2085 return status; 2086 } 2087 2088 2089 extern "C" status_t 2090 devfs_publish_partition(const char* name, const partition_info* info) 2091 { 2092 if (name == NULL || info == NULL) 2093 return B_BAD_VALUE; 2094 TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n", 2095 name, info->device, info->offset, info->size)); 2096 2097 devfs_vnode* device; 2098 status_t status = get_node_for_path(sDeviceFileSystem, info->device, 2099 &device); 2100 if (status != B_OK) 2101 return status; 2102 2103 status = add_partition(sDeviceFileSystem, device, name, *info); 2104 2105 put_vnode(sDeviceFileSystem->volume, device->id); 2106 return status; 2107 } 2108 2109 2110 extern "C" status_t 2111 devfs_rename_partition(const char *devicePath, const char* oldName, 2112 const char *newName) 2113 { 2114 status_t status; 2115 devfs_vnode *device, *node; 2116 if (oldName == NULL || newName == NULL) 2117 return B_BAD_VALUE; 2118 2119 status = get_node_for_path(sDeviceFileSystem, devicePath, &device); 2120 if (status != B_OK) 2121 return status; 2122 2123 RecursiveLocker locker(sDeviceFileSystem->lock); 2124 node = devfs_find_in_dir(device->parent, oldName); 2125 if (node == NULL) 2126 return B_ENTRY_NOT_FOUND; 2127 2128 // check if the new path already exists 2129 if (devfs_find_in_dir(device->parent, newName)) 2130 return B_BAD_VALUE; 2131 2132 char *name = strdup(newName); 2133 if (name == NULL) 2134 return B_NO_MEMORY; 2135 2136 devfs_remove_from_dir(device->parent, node, false); 2137 2138 free(node->name); 2139 node->name = name; 2140 2141 devfs_insert_in_dir(device->parent, node, false); 2142 2143 notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName, 2144 device->parent->id, newName, node->id); 2145 notify_stat_changed(sDeviceFileSystem->id, device->parent->id, 2146 B_STAT_MODIFICATION_TIME); 2147 2148 return B_OK; 2149 } 2150 2151 2152 extern "C" status_t 2153 devfs_publish_directory(const char* path) 2154 { 2155 RecursiveLocker locker(&sDeviceFileSystem->lock); 2156 2157 return publish_directory(sDeviceFileSystem, path); 2158 } 2159 2160 2161 extern "C" status_t 2162 devfs_unpublish_device(const char* path, bool disconnect) 2163 { 2164 devfs_vnode* node; 2165 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2166 if (status != B_OK) 2167 return status; 2168 2169 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2170 2171 if (status == B_OK && disconnect) 2172 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id); 2173 2174 put_vnode(sDeviceFileSystem->volume, node->id); 2175 return status; 2176 } 2177 2178 2179 // #pragma mark - device_manager private API 2180 2181 2182 status_t 2183 devfs_publish_device(const char* path, BaseDevice* device) 2184 { 2185 return publish_device(sDeviceFileSystem, path, device); 2186 } 2187 2188 2189 status_t 2190 devfs_unpublish_device(BaseDevice* device, bool disconnect) 2191 { 2192 devfs_vnode* node; 2193 status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(), 2194 (void**)&node); 2195 if (status != B_OK) 2196 return status; 2197 2198 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2199 2200 if (status == B_OK && disconnect) 2201 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id); 2202 2203 put_vnode(sDeviceFileSystem->volume, node->id); 2204 return status; 2205 } 2206 2207 2208 // #pragma mark - support API for legacy drivers 2209 2210 2211 extern "C" status_t 2212 devfs_rescan_driver(const char* driverName) 2213 { 2214 TRACE(("devfs_rescan_driver: %s\n", driverName)); 2215 2216 return legacy_driver_rescan(driverName); 2217 } 2218 2219 2220 extern "C" status_t 2221 devfs_publish_device(const char* path, device_hooks* hooks) 2222 { 2223 return legacy_driver_publish(path, hooks); 2224 } 2225 2226 2227