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 free(cookie); 1169 return status; 1170 } 1171 1172 char path[B_FILE_NAME_LENGTH]; 1173 get_device_name(vnode, path, sizeof(path)); 1174 1175 status = device->Open(path, openMode, &cookie->device_cookie); 1176 if (status != B_OK) 1177 device->UninitDevice(); 1178 } 1179 1180 if (status != B_OK) 1181 free(cookie); 1182 else 1183 *_cookie = cookie; 1184 1185 return status; 1186 } 1187 1188 1189 static status_t 1190 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1191 { 1192 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1193 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1194 1195 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie)); 1196 1197 if (S_ISCHR(vnode->stream.type)) { 1198 // pass the call through to the underlying device 1199 return vnode->stream.u.dev.device->Close(cookie->device_cookie); 1200 } 1201 1202 return B_OK; 1203 } 1204 1205 1206 static status_t 1207 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1208 { 1209 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1210 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1211 1212 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1213 1214 if (S_ISCHR(vnode->stream.type)) { 1215 // pass the call through to the underlying device 1216 vnode->stream.u.dev.device->Free(cookie->device_cookie); 1217 vnode->stream.u.dev.device->UninitDevice(); 1218 } 1219 1220 free(cookie); 1221 return B_OK; 1222 } 1223 1224 1225 static status_t 1226 devfs_fsync(fs_volume* _volume, fs_vnode* _v) 1227 { 1228 return B_OK; 1229 } 1230 1231 1232 static status_t 1233 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer, 1234 size_t* _bufferSize) 1235 { 1236 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node; 1237 1238 if (!S_ISLNK(link->stream.type)) 1239 return B_BAD_VALUE; 1240 1241 if (link->stream.u.symlink.length < *_bufferSize) 1242 *_bufferSize = link->stream.u.symlink.length; 1243 1244 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize); 1245 return B_OK; 1246 } 1247 1248 1249 static status_t 1250 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1251 void* buffer, size_t* _length) 1252 { 1253 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1254 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1255 1256 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n", 1257 // vnode, cookie, pos, _length)); 1258 1259 if (!S_ISCHR(vnode->stream.type)) 1260 return B_BAD_VALUE; 1261 1262 if (pos < 0) 1263 return B_BAD_VALUE; 1264 1265 if (vnode->stream.u.dev.partition != NULL) { 1266 if (pos >= vnode->stream.u.dev.partition->info.size) 1267 return B_BAD_VALUE; 1268 1269 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1270 } 1271 1272 if (*_length == 0) 1273 return B_OK; 1274 1275 // pass the call through to the device 1276 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer, 1277 _length); 1278 } 1279 1280 1281 static status_t 1282 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1283 const void* buffer, size_t* _length) 1284 { 1285 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1286 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1287 1288 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n", 1289 // vnode, cookie, pos, _length)); 1290 1291 if (!S_ISCHR(vnode->stream.type)) 1292 return B_BAD_VALUE; 1293 1294 if (pos < 0) 1295 return B_BAD_VALUE; 1296 1297 if (vnode->stream.u.dev.partition != NULL) { 1298 if (pos >= vnode->stream.u.dev.partition->info.size) 1299 return B_BAD_VALUE; 1300 1301 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1302 } 1303 1304 if (*_length == 0) 1305 return B_OK; 1306 1307 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer, 1308 _length); 1309 } 1310 1311 1312 static status_t 1313 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name, 1314 int perms) 1315 { 1316 struct devfs* fs = (struct devfs*)_volume->private_volume; 1317 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 1318 1319 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name); 1320 if (vnode != NULL) { 1321 return EEXIST; 1322 } 1323 1324 vnode = devfs_create_vnode(fs, dir, name); 1325 if (vnode == NULL) { 1326 return B_NO_MEMORY; 1327 } 1328 1329 // set up the new directory 1330 init_directory_vnode(vnode, perms); 1331 publish_node(sDeviceFileSystem, dir, vnode); 1332 1333 return B_OK; 1334 } 1335 1336 1337 static status_t 1338 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie) 1339 { 1340 struct devfs* fs = (struct devfs*)_volume->private_volume; 1341 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1342 struct devfs_dir_cookie* cookie; 1343 1344 TRACE(("devfs_open_dir: vnode %p\n", vnode)); 1345 1346 if (!S_ISDIR(vnode->stream.type)) 1347 return B_BAD_VALUE; 1348 1349 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie)); 1350 if (cookie == NULL) 1351 return B_NO_MEMORY; 1352 1353 // make sure the directory has up-to-date contents 1354 scan_for_drivers_if_needed(vnode); 1355 1356 RecursiveLocker locker(&fs->lock); 1357 1358 cookie->current = vnode->stream.u.dir.dir_head; 1359 cookie->state = ITERATION_STATE_BEGIN; 1360 1361 list_add_item(&vnode->stream.u.dir.cookies, cookie); 1362 *_cookie = cookie; 1363 1364 return B_OK; 1365 } 1366 1367 1368 static status_t 1369 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1370 { 1371 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1372 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1373 struct devfs* fs = (struct devfs*)_volume->private_volume; 1374 1375 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1376 1377 RecursiveLocker locker(&fs->lock); 1378 1379 list_remove_item(&vnode->stream.u.dir.cookies, cookie); 1380 free(cookie); 1381 return B_OK; 1382 } 1383 1384 1385 static status_t 1386 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1387 struct dirent* dirent, size_t bufferSize, uint32* _num) 1388 { 1389 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1390 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1391 struct devfs* fs = (struct devfs*)_volume->private_volume; 1392 status_t status = B_OK; 1393 struct devfs_vnode* childNode = NULL; 1394 const char* name = NULL; 1395 struct devfs_vnode* nextChildNode = NULL; 1396 int32 nextState = cookie->state; 1397 1398 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n", 1399 _vnode, cookie, dirent, bufferSize)); 1400 1401 if (!S_ISDIR(vnode->stream.type)) 1402 return B_BAD_VALUE; 1403 1404 RecursiveLocker locker(&fs->lock); 1405 1406 switch (cookie->state) { 1407 case ITERATION_STATE_DOT: 1408 childNode = vnode; 1409 name = "."; 1410 nextChildNode = vnode->stream.u.dir.dir_head; 1411 nextState = cookie->state + 1; 1412 break; 1413 case ITERATION_STATE_DOT_DOT: 1414 childNode = vnode->parent; 1415 name = ".."; 1416 nextChildNode = vnode->stream.u.dir.dir_head; 1417 nextState = cookie->state + 1; 1418 break; 1419 default: 1420 childNode = cookie->current; 1421 if (childNode) { 1422 name = childNode->name; 1423 nextChildNode = childNode->dir_next; 1424 } 1425 break; 1426 } 1427 1428 if (!childNode) { 1429 *_num = 0; 1430 return B_OK; 1431 } 1432 1433 dirent->d_dev = fs->id; 1434 dirent->d_ino = childNode->id; 1435 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 1436 1437 if (dirent->d_reclen > bufferSize) 1438 return ENOBUFS; 1439 1440 status = user_strlcpy(dirent->d_name, name, 1441 bufferSize - sizeof(struct dirent)); 1442 if (status < B_OK) 1443 return status; 1444 1445 cookie->current = nextChildNode; 1446 cookie->state = nextState; 1447 *_num = 1; 1448 1449 return B_OK; 1450 } 1451 1452 1453 static status_t 1454 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1455 { 1456 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1457 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1458 struct devfs* fs = (struct devfs*)_volume->private_volume; 1459 1460 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie)); 1461 1462 if (!S_ISDIR(vnode->stream.type)) 1463 return B_BAD_VALUE; 1464 1465 RecursiveLocker locker(&fs->lock); 1466 1467 cookie->current = vnode->stream.u.dir.dir_head; 1468 cookie->state = ITERATION_STATE_BEGIN; 1469 1470 return B_OK; 1471 } 1472 1473 1474 /*! Forwards the opcode to the device driver, but also handles some devfs 1475 specific functionality, like partitions. 1476 */ 1477 static status_t 1478 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op, 1479 void* buffer, size_t length) 1480 { 1481 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1482 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1483 1484 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", 1485 vnode, cookie, op, buffer, length)); 1486 1487 // we are actually checking for a *device* here, we don't make the 1488 // distinction between char and block devices 1489 if (S_ISCHR(vnode->stream.type)) { 1490 switch (op) { 1491 case B_GET_GEOMETRY: 1492 { 1493 struct devfs_partition* partition 1494 = vnode->stream.u.dev.partition; 1495 if (partition == NULL) 1496 break; 1497 1498 device_geometry geometry; 1499 status_t status = vnode->stream.u.dev.device->Control( 1500 cookie->device_cookie, op, &geometry, length); 1501 if (status < B_OK) 1502 return status; 1503 1504 // patch values to match partition size 1505 geometry.sectors_per_track = 0; 1506 if (geometry.bytes_per_sector == 0) 1507 geometry.bytes_per_sector = 512; 1508 geometry.sectors_per_track = partition->info.size 1509 / geometry.bytes_per_sector; 1510 geometry.head_count = 1; 1511 geometry.cylinder_count = 1; 1512 1513 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 1514 } 1515 1516 case B_GET_DRIVER_FOR_DEVICE: 1517 { 1518 #if 0 1519 const char* path; 1520 if (!vnode->stream.u.dev.driver) 1521 return B_ENTRY_NOT_FOUND; 1522 path = vnode->stream.u.dev.driver->path; 1523 if (path == NULL) 1524 return B_ENTRY_NOT_FOUND; 1525 1526 return user_strlcpy((char*)buffer, path, B_FILE_NAME_LENGTH); 1527 #endif 1528 return B_ERROR; 1529 } 1530 1531 case B_GET_PARTITION_INFO: 1532 { 1533 struct devfs_partition* partition 1534 = vnode->stream.u.dev.partition; 1535 if (!S_ISCHR(vnode->stream.type) 1536 || partition == NULL 1537 || length != sizeof(partition_info)) 1538 return B_BAD_VALUE; 1539 1540 return user_memcpy(buffer, &partition->info, 1541 sizeof(partition_info)); 1542 } 1543 1544 case B_SET_PARTITION: 1545 return B_NOT_ALLOWED; 1546 1547 case B_GET_PATH_FOR_DEVICE: 1548 { 1549 char path[256]; 1550 // TODO: we might want to actually find the mountpoint 1551 // of that instance of devfs... 1552 // but for now we assume it's mounted on /dev 1553 strcpy(path, "/dev/"); 1554 get_device_name(vnode, path + 5, sizeof(path) - 5); 1555 if (length && (length <= strlen(path))) 1556 return ERANGE; 1557 return user_strlcpy((char*)buffer, path, sizeof(path)); 1558 } 1559 1560 // old unsupported R5 private stuff 1561 1562 case B_GET_NEXT_OPEN_DEVICE: 1563 dprintf("devfs: unsupported legacy ioctl B_GET_NEXT_OPEN_DEVICE\n"); 1564 return B_UNSUPPORTED; 1565 case B_ADD_FIXED_DRIVER: 1566 dprintf("devfs: unsupported legacy ioctl B_ADD_FIXED_DRIVER\n"); 1567 return B_UNSUPPORTED; 1568 case B_REMOVE_FIXED_DRIVER: 1569 dprintf("devfs: unsupported legacy ioctl B_REMOVE_FIXED_DRIVER\n"); 1570 return B_UNSUPPORTED; 1571 1572 } 1573 1574 return vnode->stream.u.dev.device->Control(cookie->device_cookie, 1575 op, buffer, length); 1576 } 1577 1578 return B_BAD_VALUE; 1579 } 1580 1581 1582 static status_t 1583 devfs_set_flags(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1584 int flags) 1585 { 1586 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1587 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1588 1589 // we need to pass the O_NONBLOCK flag to the underlying device 1590 1591 if (!S_ISCHR(vnode->stream.type)) 1592 return B_NOT_ALLOWED; 1593 1594 return vnode->stream.u.dev.device->Control(cookie->device_cookie, 1595 flags & O_NONBLOCK ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0); 1596 } 1597 1598 1599 static status_t 1600 devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1601 uint8 event, selectsync* sync) 1602 { 1603 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1604 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1605 1606 if (!S_ISCHR(vnode->stream.type)) 1607 return B_NOT_ALLOWED; 1608 1609 // If the device has no select() hook, notify select() now. 1610 if (!vnode->stream.u.dev.device->HasSelect()) 1611 return notify_select_event((selectsync*)sync, event); 1612 1613 return vnode->stream.u.dev.device->Select(cookie->device_cookie, event, 1614 (selectsync*)sync); 1615 } 1616 1617 1618 static status_t 1619 devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1620 uint8 event, selectsync* sync) 1621 { 1622 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1623 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1624 1625 if (!S_ISCHR(vnode->stream.type)) 1626 return B_NOT_ALLOWED; 1627 1628 // If the device has no select() hook, notify select() now. 1629 if (!vnode->stream.u.dev.device->HasDeselect()) 1630 return B_OK; 1631 1632 return vnode->stream.u.dev.device->Deselect(cookie->device_cookie, event, 1633 (selectsync*)sync); 1634 } 1635 1636 1637 static bool 1638 devfs_can_page(fs_volume* _volume, fs_vnode* _vnode, void* cookie) 1639 { 1640 #if 0 1641 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1642 1643 //TRACE(("devfs_canpage: vnode %p\n", vnode)); 1644 1645 if (!S_ISCHR(vnode->stream.type) 1646 || vnode->stream.u.dev.device->Node() == NULL 1647 || cookie == NULL) 1648 return false; 1649 1650 return vnode->stream.u.dev.device->HasRead() 1651 || vnode->stream.u.dev.device->HasIO(); 1652 #endif 1653 // TODO: Obsolete hook! 1654 return false; 1655 } 1656 1657 1658 static status_t 1659 devfs_read_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1660 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 1661 { 1662 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1663 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1664 1665 //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes)); 1666 1667 if (!S_ISCHR(vnode->stream.type) 1668 || (!vnode->stream.u.dev.device->HasRead() 1669 && !vnode->stream.u.dev.device->HasIO()) 1670 || cookie == NULL) 1671 return B_NOT_ALLOWED; 1672 1673 if (pos < 0) 1674 return B_BAD_VALUE; 1675 1676 if (vnode->stream.u.dev.partition != NULL) { 1677 if (pos >= vnode->stream.u.dev.partition->info.size) 1678 return B_BAD_VALUE; 1679 1680 translate_partition_access(vnode->stream.u.dev.partition, pos, 1681 *_numBytes); 1682 } 1683 1684 if (vnode->stream.u.dev.device->HasIO()) { 1685 // TODO: use io_requests for this! 1686 } 1687 1688 // emulate read_pages() using read() 1689 1690 status_t error = B_OK; 1691 size_t bytesTransferred = 0; 1692 1693 size_t remainingBytes = *_numBytes; 1694 for (size_t i = 0; i < count && remainingBytes > 0; i++) { 1695 size_t toRead = min_c(vecs[i].iov_len, remainingBytes); 1696 size_t length = toRead; 1697 1698 error = vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, 1699 vecs[i].iov_base, &length); 1700 if (error != B_OK) 1701 break; 1702 1703 pos += length; 1704 bytesTransferred += length; 1705 remainingBytes -= length; 1706 1707 if (length < toRead) 1708 break; 1709 } 1710 1711 *_numBytes = bytesTransferred; 1712 1713 return bytesTransferred > 0 ? B_OK : error; 1714 } 1715 1716 1717 static status_t 1718 devfs_write_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1719 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 1720 { 1721 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1722 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1723 1724 //TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes)); 1725 1726 if (!S_ISCHR(vnode->stream.type) 1727 || (!vnode->stream.u.dev.device->HasWrite() 1728 && !vnode->stream.u.dev.device->HasIO()) 1729 || cookie == NULL) 1730 return B_NOT_ALLOWED; 1731 1732 if (pos < 0) 1733 return B_BAD_VALUE; 1734 1735 if (vnode->stream.u.dev.partition != NULL) { 1736 if (pos >= vnode->stream.u.dev.partition->info.size) 1737 return B_BAD_VALUE; 1738 1739 translate_partition_access(vnode->stream.u.dev.partition, pos, 1740 *_numBytes); 1741 } 1742 1743 if (vnode->stream.u.dev.device->HasIO()) { 1744 // TODO: use io_requests for this! 1745 } 1746 1747 // emulate write_pages() using write() 1748 1749 status_t error = B_OK; 1750 size_t bytesTransferred = 0; 1751 1752 size_t remainingBytes = *_numBytes; 1753 for (size_t i = 0; i < count && remainingBytes > 0; i++) { 1754 size_t toWrite = min_c(vecs[i].iov_len, remainingBytes); 1755 size_t length = toWrite; 1756 1757 error = vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, 1758 vecs[i].iov_base, &length); 1759 if (error != B_OK) 1760 break; 1761 1762 pos += length; 1763 bytesTransferred += length; 1764 remainingBytes -= length; 1765 1766 if (length < toWrite) 1767 break; 1768 } 1769 1770 *_numBytes = bytesTransferred; 1771 1772 return bytesTransferred > 0 ? B_OK : error; 1773 } 1774 1775 1776 static status_t 1777 devfs_io(fs_volume* volume, fs_vnode* _vnode, void* _cookie, 1778 io_request* request) 1779 { 1780 TRACE(("[%ld] devfs_io(request: %p)\n", find_thread(NULL), request)); 1781 1782 devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1783 devfs_cookie* cookie = (devfs_cookie*)_cookie; 1784 1785 bool isWrite = request->IsWrite(); 1786 1787 if (!S_ISCHR(vnode->stream.type) 1788 || (((isWrite && !vnode->stream.u.dev.device->HasWrite()) 1789 || (!isWrite && !vnode->stream.u.dev.device->HasRead())) 1790 && !vnode->stream.u.dev.device->HasIO()) 1791 || cookie == NULL) { 1792 request->SetStatusAndNotify(B_NOT_ALLOWED); 1793 return B_NOT_ALLOWED; 1794 } 1795 1796 if (vnode->stream.u.dev.partition != NULL) { 1797 if (request->Offset() + (off_t)request->Length() 1798 > vnode->stream.u.dev.partition->info.size) { 1799 request->SetStatusAndNotify(B_BAD_VALUE); 1800 return B_BAD_VALUE; 1801 } 1802 translate_partition_access(vnode->stream.u.dev.partition, request); 1803 } 1804 1805 if (vnode->stream.u.dev.device->HasIO()) 1806 return vnode->stream.u.dev.device->IO(cookie->device_cookie, request); 1807 1808 synchronous_io_cookie synchronousCookie = { 1809 vnode->stream.u.dev.device, 1810 cookie->device_cookie 1811 }; 1812 1813 return vfs_synchronous_io(request, 1814 request->IsWrite() ? &device_write : &device_read, &synchronousCookie); 1815 } 1816 1817 1818 static status_t 1819 devfs_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* stat) 1820 { 1821 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1822 1823 TRACE(("devfs_read_stat: vnode %p (%Ld), stat %p\n", vnode, vnode->id, 1824 stat)); 1825 1826 stat->st_ino = vnode->id; 1827 stat->st_size = 0; 1828 stat->st_mode = vnode->stream.type; 1829 1830 stat->st_nlink = 1; 1831 stat->st_blksize = 65536; 1832 stat->st_blocks = 0; 1833 1834 stat->st_uid = vnode->uid; 1835 stat->st_gid = vnode->gid; 1836 1837 stat->st_atim = current_timespec(); 1838 stat->st_mtim = stat->st_ctim = vnode->modification_time; 1839 stat->st_crtim = vnode->creation_time; 1840 1841 // TODO: this only works for partitions right now - if we should decide 1842 // to keep this feature, we should have a better solution 1843 if (S_ISCHR(vnode->stream.type)) { 1844 //device_geometry geometry; 1845 1846 // if it's a real block device, then let's report a useful size 1847 if (vnode->stream.u.dev.partition != NULL) { 1848 stat->st_size = vnode->stream.u.dev.partition->info.size; 1849 #if 0 1850 } else if (vnode->stream.u.dev.info->control(cookie->device_cookie, 1851 B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) { 1852 stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count 1853 * geometry.sectors_per_track * geometry.bytes_per_sector; 1854 #endif 1855 } 1856 1857 // is this a real block device? then let's have it reported like that 1858 if (stat->st_size != 0) 1859 stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK); 1860 } else if (S_ISLNK(vnode->stream.type)) { 1861 stat->st_size = vnode->stream.u.symlink.length; 1862 } 1863 1864 return B_OK; 1865 } 1866 1867 1868 static status_t 1869 devfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat, 1870 uint32 statMask) 1871 { 1872 struct devfs* fs = (struct devfs*)_volume->private_volume; 1873 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1874 1875 TRACE(("devfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, 1876 stat)); 1877 1878 // we cannot change the size of anything 1879 if (statMask & B_STAT_SIZE) 1880 return B_BAD_VALUE; 1881 1882 RecursiveLocker locker(&fs->lock); 1883 1884 if (statMask & B_STAT_MODE) { 1885 vnode->stream.type = (vnode->stream.type & ~S_IUMSK) 1886 | (stat->st_mode & S_IUMSK); 1887 } 1888 1889 if (statMask & B_STAT_UID) 1890 vnode->uid = stat->st_uid; 1891 if (statMask & B_STAT_GID) 1892 vnode->gid = stat->st_gid; 1893 1894 if (statMask & B_STAT_MODIFICATION_TIME) 1895 vnode->modification_time = stat->st_mtim; 1896 if (statMask & B_STAT_CREATION_TIME) 1897 vnode->creation_time = stat->st_crtim; 1898 1899 notify_stat_changed(fs->id, vnode->id, statMask); 1900 return B_OK; 1901 } 1902 1903 1904 static status_t 1905 devfs_std_ops(int32 op, ...) 1906 { 1907 switch (op) { 1908 case B_MODULE_INIT: 1909 add_debugger_command_etc("devfs_node", &dump_node, 1910 "Print info on a private devfs node", 1911 "<address>\n" 1912 "Prints information on a devfs node given by <address>.\n", 1913 0); 1914 add_debugger_command_etc("devfs_cookie", &dump_cookie, 1915 "Print info on a private devfs cookie", 1916 "<address>\n" 1917 "Prints information on a devfs cookie given by <address>.\n", 1918 0); 1919 1920 legacy_driver_init(); 1921 return B_OK; 1922 1923 case B_MODULE_UNINIT: 1924 remove_debugger_command("devfs_node", &dump_node); 1925 remove_debugger_command("devfs_cookie", &dump_cookie); 1926 return B_OK; 1927 1928 default: 1929 return B_ERROR; 1930 } 1931 } 1932 1933 namespace { 1934 1935 fs_volume_ops kVolumeOps = { 1936 &devfs_unmount, 1937 NULL, 1938 NULL, 1939 &devfs_sync, 1940 &devfs_get_vnode, 1941 1942 // the other operations are not supported (attributes, indices, queries) 1943 NULL, 1944 }; 1945 1946 fs_vnode_ops kVnodeOps = { 1947 &devfs_lookup, 1948 &devfs_get_vnode_name, 1949 1950 &devfs_put_vnode, 1951 &devfs_remove_vnode, 1952 1953 &devfs_can_page, 1954 &devfs_read_pages, 1955 &devfs_write_pages, 1956 1957 &devfs_io, 1958 NULL, // cancel_io() 1959 1960 NULL, // get_file_map 1961 1962 /* common */ 1963 &devfs_ioctl, 1964 &devfs_set_flags, 1965 &devfs_select, 1966 &devfs_deselect, 1967 &devfs_fsync, 1968 1969 &devfs_read_link, 1970 NULL, // symlink 1971 NULL, // link 1972 NULL, // unlink 1973 NULL, // rename 1974 1975 NULL, // access 1976 &devfs_read_stat, 1977 &devfs_write_stat, 1978 NULL, 1979 1980 /* file */ 1981 &devfs_create, 1982 &devfs_open, 1983 &devfs_close, 1984 &devfs_free_cookie, 1985 &devfs_read, 1986 &devfs_write, 1987 1988 /* directory */ 1989 &devfs_create_dir, 1990 NULL, // remove_dir 1991 &devfs_open_dir, 1992 &devfs_close, 1993 // same as for files - it does nothing for directories, anyway 1994 &devfs_free_dir_cookie, 1995 &devfs_read_dir, 1996 &devfs_rewind_dir, 1997 1998 // attributes operations are not supported 1999 NULL, 2000 }; 2001 2002 } // namespace 2003 2004 file_system_module_info gDeviceFileSystem = { 2005 { 2006 "file_systems/devfs" B_CURRENT_FS_API_VERSION, 2007 0, 2008 devfs_std_ops, 2009 }, 2010 2011 "devfs", // short_name 2012 "Device File System", // pretty_name 2013 0, // DDM flags 2014 2015 NULL, // identify_partition() 2016 NULL, // scan_partition() 2017 NULL, // free_identify_partition_cookie() 2018 NULL, // free_partition_content_cookie() 2019 2020 &devfs_mount, 2021 }; 2022 2023 2024 // #pragma mark - kernel private API 2025 2026 2027 extern "C" status_t 2028 devfs_unpublish_file_device(const char* path) 2029 { 2030 // get the device node 2031 devfs_vnode* node; 2032 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2033 if (status != B_OK) 2034 return status; 2035 2036 if (!S_ISCHR(node->stream.type)) { 2037 put_vnode(sDeviceFileSystem->volume, node->id); 2038 return B_BAD_VALUE; 2039 } 2040 2041 // if it is indeed a file device, unpublish it 2042 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device); 2043 if (device == NULL) { 2044 put_vnode(sDeviceFileSystem->volume, node->id); 2045 return B_BAD_VALUE; 2046 } 2047 2048 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2049 2050 put_vnode(sDeviceFileSystem->volume, node->id); 2051 return status; 2052 } 2053 2054 2055 extern "C" status_t 2056 devfs_publish_file_device(const char* path, const char* filePath) 2057 { 2058 // create a FileDevice for the file 2059 FileDevice* device = new(std::nothrow) FileDevice; 2060 if (device == NULL) 2061 return B_NO_MEMORY; 2062 ObjectDeleter<FileDevice> deviceDeleter(device); 2063 2064 status_t error = device->Init(filePath); 2065 if (error != B_OK) 2066 return error; 2067 2068 // publish the device 2069 error = publish_device(sDeviceFileSystem, path, device); 2070 if (error != B_OK) 2071 return error; 2072 2073 deviceDeleter.Detach(); 2074 return B_OK; 2075 } 2076 2077 2078 extern "C" status_t 2079 devfs_unpublish_partition(const char* path) 2080 { 2081 devfs_vnode* node; 2082 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2083 if (status != B_OK) 2084 return status; 2085 2086 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2087 put_vnode(sDeviceFileSystem->volume, node->id); 2088 return status; 2089 } 2090 2091 2092 extern "C" status_t 2093 devfs_publish_partition(const char* name, const partition_info* info) 2094 { 2095 if (name == NULL || info == NULL) 2096 return B_BAD_VALUE; 2097 TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n", 2098 name, info->device, info->offset, info->size)); 2099 2100 devfs_vnode* device; 2101 status_t status = get_node_for_path(sDeviceFileSystem, info->device, 2102 &device); 2103 if (status != B_OK) 2104 return status; 2105 2106 status = add_partition(sDeviceFileSystem, device, name, *info); 2107 2108 put_vnode(sDeviceFileSystem->volume, device->id); 2109 return status; 2110 } 2111 2112 2113 extern "C" status_t 2114 devfs_rename_partition(const char* devicePath, const char* oldName, 2115 const char* newName) 2116 { 2117 if (oldName == NULL || newName == NULL) 2118 return B_BAD_VALUE; 2119 2120 devfs_vnode* device; 2121 status_t status = get_node_for_path(sDeviceFileSystem, devicePath, &device); 2122 if (status != B_OK) 2123 return status; 2124 2125 RecursiveLocker locker(sDeviceFileSystem->lock); 2126 devfs_vnode* node = devfs_find_in_dir(device->parent, oldName); 2127 if (node == NULL) 2128 return B_ENTRY_NOT_FOUND; 2129 2130 // check if the new path already exists 2131 if (devfs_find_in_dir(device->parent, newName)) 2132 return B_BAD_VALUE; 2133 2134 char* name = strdup(newName); 2135 if (name == NULL) 2136 return B_NO_MEMORY; 2137 2138 devfs_remove_from_dir(device->parent, node, false); 2139 2140 free(node->name); 2141 node->name = name; 2142 2143 devfs_insert_in_dir(device->parent, node, false); 2144 2145 notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName, 2146 device->parent->id, newName, node->id); 2147 notify_stat_changed(sDeviceFileSystem->id, device->parent->id, 2148 B_STAT_MODIFICATION_TIME); 2149 2150 return B_OK; 2151 } 2152 2153 2154 extern "C" status_t 2155 devfs_publish_directory(const char* path) 2156 { 2157 RecursiveLocker locker(&sDeviceFileSystem->lock); 2158 2159 return publish_directory(sDeviceFileSystem, path); 2160 } 2161 2162 2163 extern "C" status_t 2164 devfs_unpublish_device(const char* path, bool disconnect) 2165 { 2166 devfs_vnode* node; 2167 status_t status = get_node_for_path(sDeviceFileSystem, path, &node); 2168 if (status != B_OK) 2169 return status; 2170 2171 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2172 2173 if (status == B_OK && disconnect) 2174 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id); 2175 2176 put_vnode(sDeviceFileSystem->volume, node->id); 2177 return status; 2178 } 2179 2180 2181 // #pragma mark - device_manager private API 2182 2183 2184 status_t 2185 devfs_publish_device(const char* path, BaseDevice* device) 2186 { 2187 return publish_device(sDeviceFileSystem, path, device); 2188 } 2189 2190 2191 status_t 2192 devfs_unpublish_device(BaseDevice* device, bool disconnect) 2193 { 2194 devfs_vnode* node; 2195 status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(), 2196 (void**)&node); 2197 if (status != B_OK) 2198 return status; 2199 2200 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); 2201 2202 if (status == B_OK && disconnect) 2203 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id); 2204 2205 put_vnode(sDeviceFileSystem->volume, node->id); 2206 return status; 2207 } 2208 2209 2210 // #pragma mark - support API for legacy drivers 2211 2212 2213 extern "C" status_t 2214 devfs_rescan_driver(const char* driverName) 2215 { 2216 TRACE(("devfs_rescan_driver: %s\n", driverName)); 2217 2218 return legacy_driver_rescan(driverName); 2219 } 2220 2221 2222 extern "C" status_t 2223 devfs_publish_device(const char* path, device_hooks* hooks) 2224 { 2225 return legacy_driver_publish(path, hooks); 2226 } 2227 2228 2229