1 /* 2 * Copyright 2002-2011, 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 // Can't 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 + (off_t)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* _rootNodeID) 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 *_rootNodeID = 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", 1094 dir, name, openMode, _cookie)); 1095 1096 RecursiveLocker locker(fs->lock); 1097 1098 // look it up 1099 vnode = devfs_find_in_dir(dir, name); 1100 if (vnode == NULL) 1101 return EROFS; 1102 1103 if ((openMode & O_EXCL) != 0) 1104 return B_FILE_EXISTS; 1105 1106 status = get_vnode(fs->volume, vnode->id, NULL); 1107 if (status != B_OK) 1108 return status; 1109 1110 locker.Unlock(); 1111 1112 *_newVnodeID = vnode->id; 1113 1114 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1115 if (cookie == NULL) { 1116 status = B_NO_MEMORY; 1117 goto err1; 1118 } 1119 1120 if (S_ISCHR(vnode->stream.type)) { 1121 BaseDevice* device = vnode->stream.u.dev.device; 1122 status = device->InitDevice(); 1123 if (status != B_OK) 1124 goto err2; 1125 1126 char path[B_FILE_NAME_LENGTH]; 1127 get_device_name(vnode, path, sizeof(path)); 1128 1129 status = device->Open(path, openMode, &cookie->device_cookie); 1130 if (status != B_OK) { 1131 device->UninitDevice(); 1132 goto err2; 1133 } 1134 } 1135 1136 *_cookie = cookie; 1137 return B_OK; 1138 1139 err2: 1140 free(cookie); 1141 err1: 1142 put_vnode(fs->volume, vnode->id); 1143 return status; 1144 } 1145 1146 1147 static status_t 1148 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode, 1149 void** _cookie) 1150 { 1151 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1152 struct devfs_cookie* cookie; 1153 status_t status = B_OK; 1154 1155 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1156 if (cookie == NULL) 1157 return B_NO_MEMORY; 1158 1159 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode, 1160 cookie)); 1161 1162 cookie->device_cookie = NULL; 1163 1164 if (S_ISCHR(vnode->stream.type)) { 1165 BaseDevice* device = vnode->stream.u.dev.device; 1166 status = device->InitDevice(); 1167 if (status != B_OK) 1168 return status; 1169 1170 char path[B_FILE_NAME_LENGTH]; 1171 get_device_name(vnode, path, sizeof(path)); 1172 1173 status = device->Open(path, openMode, &cookie->device_cookie); 1174 if (status != B_OK) 1175 device->UninitDevice(); 1176 } 1177 1178 if (status != B_OK) 1179 free(cookie); 1180 else 1181 *_cookie = cookie; 1182 1183 return status; 1184 } 1185 1186 1187 static status_t 1188 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1189 { 1190 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1191 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1192 1193 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie)); 1194 1195 if (S_ISCHR(vnode->stream.type)) { 1196 // pass the call through to the underlying device 1197 return vnode->stream.u.dev.device->Close(cookie->device_cookie); 1198 } 1199 1200 return B_OK; 1201 } 1202 1203 1204 static status_t 1205 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1206 { 1207 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1208 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1209 1210 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1211 1212 if (S_ISCHR(vnode->stream.type)) { 1213 // pass the call through to the underlying device 1214 vnode->stream.u.dev.device->Free(cookie->device_cookie); 1215 vnode->stream.u.dev.device->UninitDevice(); 1216 } 1217 1218 free(cookie); 1219 return B_OK; 1220 } 1221 1222 1223 static status_t 1224 devfs_fsync(fs_volume* _volume, fs_vnode* _v) 1225 { 1226 return B_OK; 1227 } 1228 1229 1230 static status_t 1231 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer, 1232 size_t* _bufferSize) 1233 { 1234 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node; 1235 1236 if (!S_ISLNK(link->stream.type)) 1237 return B_BAD_VALUE; 1238 1239 if (link->stream.u.symlink.length < *_bufferSize) 1240 *_bufferSize = link->stream.u.symlink.length; 1241 1242 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize); 1243 return B_OK; 1244 } 1245 1246 1247 static status_t 1248 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1249 void* buffer, size_t* _length) 1250 { 1251 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1252 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1253 1254 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n", 1255 // vnode, cookie, pos, _length)); 1256 1257 if (!S_ISCHR(vnode->stream.type)) 1258 return B_BAD_VALUE; 1259 1260 if (pos < 0) 1261 return B_BAD_VALUE; 1262 1263 if (vnode->stream.u.dev.partition != NULL) { 1264 if (pos >= vnode->stream.u.dev.partition->info.size) 1265 return B_BAD_VALUE; 1266 1267 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1268 } 1269 1270 if (*_length == 0) 1271 return B_OK; 1272 1273 // pass the call through to the device 1274 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer, 1275 _length); 1276 } 1277 1278 1279 static status_t 1280 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1281 const void* buffer, size_t* _length) 1282 { 1283 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1284 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1285 1286 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n", 1287 // vnode, cookie, pos, _length)); 1288 1289 if (!S_ISCHR(vnode->stream.type)) 1290 return B_BAD_VALUE; 1291 1292 if (pos < 0) 1293 return B_BAD_VALUE; 1294 1295 if (vnode->stream.u.dev.partition != NULL) { 1296 if (pos >= vnode->stream.u.dev.partition->info.size) 1297 return B_BAD_VALUE; 1298 1299 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1300 } 1301 1302 if (*_length == 0) 1303 return B_OK; 1304 1305 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer, 1306 _length); 1307 } 1308 1309 1310 static status_t 1311 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name, 1312 int perms) 1313 { 1314 struct devfs* fs = (struct devfs*)_volume->private_volume; 1315 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 1316 1317 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name); 1318 if (vnode != NULL) { 1319 return EEXIST; 1320 } 1321 1322 vnode = devfs_create_vnode(fs, dir, name); 1323 if (vnode == NULL) { 1324 return B_NO_MEMORY; 1325 } 1326 1327 // set up the new directory 1328 init_directory_vnode(vnode, perms); 1329 publish_node(sDeviceFileSystem, dir, vnode); 1330 1331 return B_OK; 1332 } 1333 1334 1335 static status_t 1336 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie) 1337 { 1338 struct devfs* fs = (struct devfs*)_volume->private_volume; 1339 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1340 struct devfs_dir_cookie* cookie; 1341 1342 TRACE(("devfs_open_dir: vnode %p\n", vnode)); 1343 1344 if (!S_ISDIR(vnode->stream.type)) 1345 return B_BAD_VALUE; 1346 1347 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie)); 1348 if (cookie == NULL) 1349 return B_NO_MEMORY; 1350 1351 // make sure the directory has up-to-date contents 1352 scan_for_drivers_if_needed(vnode); 1353 1354 RecursiveLocker locker(&fs->lock); 1355 1356 cookie->current = vnode->stream.u.dir.dir_head; 1357 cookie->state = ITERATION_STATE_BEGIN; 1358 1359 list_add_item(&vnode->stream.u.dir.cookies, cookie); 1360 *_cookie = cookie; 1361 1362 return B_OK; 1363 } 1364 1365 1366 static status_t 1367 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1368 { 1369 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1370 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1371 struct devfs* fs = (struct devfs*)_volume->private_volume; 1372 1373 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1374 1375 RecursiveLocker locker(&fs->lock); 1376 1377 list_remove_item(&vnode->stream.u.dir.cookies, cookie); 1378 free(cookie); 1379 return B_OK; 1380 } 1381 1382 1383 static status_t 1384 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1385 struct dirent* dirent, size_t bufferSize, uint32* _num) 1386 { 1387 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1388 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1389 struct devfs* fs = (struct devfs*)_volume->private_volume; 1390 status_t status = B_OK; 1391 struct devfs_vnode* childNode = NULL; 1392 const char* name = NULL; 1393 struct devfs_vnode* nextChildNode = NULL; 1394 int32 nextState = cookie->state; 1395 1396 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n", 1397 _vnode, cookie, dirent, bufferSize)); 1398 1399 if (!S_ISDIR(vnode->stream.type)) 1400 return B_BAD_VALUE; 1401 1402 RecursiveLocker locker(&fs->lock); 1403 1404 switch (cookie->state) { 1405 case ITERATION_STATE_DOT: 1406 childNode = vnode; 1407 name = "."; 1408 nextChildNode = vnode->stream.u.dir.dir_head; 1409 nextState = cookie->state + 1; 1410 break; 1411 case ITERATION_STATE_DOT_DOT: 1412 childNode = vnode->parent; 1413 name = ".."; 1414 nextChildNode = vnode->stream.u.dir.dir_head; 1415 nextState = cookie->state + 1; 1416 break; 1417 default: 1418 childNode = cookie->current; 1419 if (childNode) { 1420 name = childNode->name; 1421 nextChildNode = childNode->dir_next; 1422 } 1423 break; 1424 } 1425 1426 if (!childNode) { 1427 *_num = 0; 1428 return B_OK; 1429 } 1430 1431 dirent->d_dev = fs->id; 1432 dirent->d_ino = childNode->id; 1433 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 1434 1435 if (dirent->d_reclen > bufferSize) 1436 return ENOBUFS; 1437 1438 status = user_strlcpy(dirent->d_name, name, 1439 bufferSize - sizeof(struct dirent)); 1440 if (status < B_OK) 1441 return status; 1442 1443 cookie->current = nextChildNode; 1444 cookie->state = nextState; 1445 *_num = 1; 1446 1447 return B_OK; 1448 } 1449 1450 1451 static status_t 1452 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1453 { 1454 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1455 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1456 struct devfs* fs = (struct devfs*)_volume->private_volume; 1457 1458 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie)); 1459 1460 if (!S_ISDIR(vnode->stream.type)) 1461 return B_BAD_VALUE; 1462 1463 RecursiveLocker locker(&fs->lock); 1464 1465 cookie->current = vnode->stream.u.dir.dir_head; 1466 cookie->state = ITERATION_STATE_BEGIN; 1467 1468 return B_OK; 1469 } 1470 1471 1472 /*! Forwards the opcode to the device driver, but also handles some devfs 1473 specific functionality, like partitions. 1474 */ 1475 static status_t 1476 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op, 1477 void* buffer, size_t length) 1478 { 1479 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1480 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1481 1482 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", 1483 vnode, cookie, op, buffer, length)); 1484 1485 // we are actually checking for a *device* here, we don't make the 1486 // distinction between char and block devices 1487 if (S_ISCHR(vnode->stream.type)) { 1488 switch (op) { 1489 case B_GET_GEOMETRY: 1490 { 1491 struct devfs_partition* partition 1492 = vnode->stream.u.dev.partition; 1493 if (partition == NULL) 1494 break; 1495 1496 device_geometry geometry; 1497 status_t status = vnode->stream.u.dev.device->Control( 1498 cookie->device_cookie, op, &geometry, length); 1499 if (status < B_OK) 1500 return status; 1501 1502 // patch values to match partition size 1503 geometry.sectors_per_track = 0; 1504 if (geometry.bytes_per_sector == 0) 1505 geometry.bytes_per_sector = 512; 1506 geometry.sectors_per_track = partition->info.size 1507 / geometry.bytes_per_sector; 1508 geometry.head_count = 1; 1509 geometry.cylinder_count = 1; 1510 1511 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 1512 } 1513 1514 case B_GET_DRIVER_FOR_DEVICE: 1515 { 1516 #if 0 1517 const char* path; 1518 if (!vnode->stream.u.dev.driver) 1519 return B_ENTRY_NOT_FOUND; 1520 path = vnode->stream.u.dev.driver->path; 1521 if (path == NULL) 1522 return B_ENTRY_NOT_FOUND; 1523 1524 return user_strlcpy((char*)buffer, path, B_FILE_NAME_LENGTH); 1525 #endif 1526 return B_ERROR; 1527 } 1528 1529 case B_GET_PARTITION_INFO: 1530 { 1531 struct devfs_partition* partition 1532 = vnode->stream.u.dev.partition; 1533 if (!S_ISCHR(vnode->stream.type) 1534 || partition == NULL 1535 || length != sizeof(partition_info)) 1536 return B_BAD_VALUE; 1537 1538 return user_memcpy(buffer, &partition->info, 1539 sizeof(partition_info)); 1540 } 1541 1542 case B_SET_PARTITION: 1543 return B_NOT_ALLOWED; 1544 1545 case B_GET_PATH_FOR_DEVICE: 1546 { 1547 char path[256]; 1548 // TODO: we might want to actually find the mountpoint 1549 // of that instance of devfs... 1550 // but for now we assume it's mounted on /dev 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() + (off_t)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 NULL, 1977 1978 /* file */ 1979 &devfs_create, 1980 &devfs_open, 1981 &devfs_close, 1982 &devfs_free_cookie, 1983 &devfs_read, 1984 &devfs_write, 1985 1986 /* directory */ 1987 &devfs_create_dir, 1988 NULL, // remove_dir 1989 &devfs_open_dir, 1990 &devfs_close, 1991 // same as for files - it does nothing for directories, anyway 1992 &devfs_free_dir_cookie, 1993 &devfs_read_dir, 1994 &devfs_rewind_dir, 1995 1996 // attributes operations are not supported 1997 NULL, 1998 }; 1999 2000 } // namespace 2001 2002 file_system_module_info gDeviceFileSystem = { 2003 { 2004 "file_systems/devfs" B_CURRENT_FS_API_VERSION, 2005 0, 2006 devfs_std_ops, 2007 }, 2008 2009 "devfs", // short_name 2010 "Device File System", // pretty_name 2011 0, // DDM flags 2012 2013 NULL, // identify_partition() 2014 NULL, // scan_partition() 2015 NULL, // free_identify_partition_cookie() 2016 NULL, // free_partition_content_cookie() 2017 2018 &devfs_mount, 2019 }; 2020 2021 2022 // #pragma mark - kernel private API 2023 2024 2025 extern "C" status_t 2026 devfs_unpublish_file_device(const char* path) 2027 { 2028 // get the device node 2029 devfs_vnode* node; 2030 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2031 if (status != B_OK) 2032 return status; 2033 2034 if (!S_ISCHR(node->stream.type)) { 2035 put_vnode(sDeviceFileSystem->volume, node->id); 2036 return B_BAD_VALUE; 2037 } 2038 2039 // if it is indeed a file device, unpublish it 2040 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device); 2041 if (device == NULL) { 2042 put_vnode(sDeviceFileSystem->volume, node->id); 2043 return B_BAD_VALUE; 2044 } 2045 2046 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2047 2048 put_vnode(sDeviceFileSystem->volume, node->id); 2049 return status; 2050 } 2051 2052 2053 extern "C" status_t 2054 devfs_publish_file_device(const char* path, const char* filePath) 2055 { 2056 // create a FileDevice for the file 2057 FileDevice* device = new(std::nothrow) FileDevice; 2058 if (device == NULL) 2059 return B_NO_MEMORY; 2060 ObjectDeleter<FileDevice> deviceDeleter(device); 2061 2062 status_t error = device->Init(filePath); 2063 if (error != B_OK) 2064 return error; 2065 2066 // publish the device 2067 error = publish_device(sDeviceFileSystem, path, device); 2068 if (error != B_OK) 2069 return error; 2070 2071 deviceDeleter.Detach(); 2072 return B_OK; 2073 } 2074 2075 2076 extern "C" status_t 2077 devfs_unpublish_partition(const char* path) 2078 { 2079 devfs_vnode* node; 2080 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2081 if (status != B_OK) 2082 return status; 2083 2084 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2085 put_vnode(sDeviceFileSystem->volume, node->id); 2086 return status; 2087 } 2088 2089 2090 extern "C" status_t 2091 devfs_publish_partition(const char* name, const partition_info* info) 2092 { 2093 if (name == NULL || info == NULL) 2094 return B_BAD_VALUE; 2095 TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n", 2096 name, info->device, info->offset, info->size)); 2097 2098 devfs_vnode* device; 2099 status_t status = get_node_for_path(sDeviceFileSystem, info->device, 2100 &device); 2101 if (status != B_OK) 2102 return status; 2103 2104 status = add_partition(sDeviceFileSystem, device, name, *info); 2105 2106 put_vnode(sDeviceFileSystem->volume, device->id); 2107 return status; 2108 } 2109 2110 2111 extern "C" status_t 2112 devfs_rename_partition(const char* devicePath, const char* oldName, 2113 const char* newName) 2114 { 2115 if (oldName == NULL || newName == NULL) 2116 return B_BAD_VALUE; 2117 2118 devfs_vnode* device; 2119 status_t status = get_node_for_path(sDeviceFileSystem, devicePath, &device); 2120 if (status != B_OK) 2121 return status; 2122 2123 RecursiveLocker locker(sDeviceFileSystem->lock); 2124 devfs_vnode* 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