1 /* 2 * Copyright 2002-2012, 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/devfs.h> 30 #include <fs/KPath.h> 31 #include <fs/node_monitor.h> 32 #include <kdevice_manager.h> 33 #include <lock.h> 34 #include <Notifications.h> 35 #include <util/AutoLock.h> 36 #include <util/khash.h> 37 #include <vfs.h> 38 #include <vm/vm.h> 39 40 #include "BaseDevice.h" 41 #include "FileDevice.h" 42 #include "IORequest.h" 43 #include "legacy_drivers.h" 44 45 46 //#define TRACE_DEVFS 47 #ifdef TRACE_DEVFS 48 # define TRACE(x) dprintf x 49 #else 50 # define TRACE(x) 51 #endif 52 53 54 struct devfs_partition { 55 struct devfs_vnode* raw_device; 56 partition_info info; 57 }; 58 59 struct driver_entry; 60 61 enum { 62 kNotScanned = 0, 63 kBootScan, 64 kNormalScan, 65 }; 66 67 struct devfs_stream { 68 mode_t type; 69 union { 70 struct stream_dir { 71 struct devfs_vnode* dir_head; 72 struct list cookies; 73 mutex scan_lock; 74 int32 scanned; 75 } dir; 76 struct stream_dev { 77 BaseDevice* device; 78 struct devfs_partition* partition; 79 } dev; 80 struct stream_symlink { 81 const char* path; 82 size_t length; 83 } symlink; 84 } u; 85 }; 86 87 struct devfs_vnode { 88 struct devfs_vnode* all_next; 89 ino_t id; 90 char* name; 91 timespec modification_time; 92 timespec creation_time; 93 uid_t uid; 94 gid_t gid; 95 struct devfs_vnode* parent; 96 struct devfs_vnode* dir_next; 97 struct devfs_stream stream; 98 }; 99 100 #define DEVFS_HASH_SIZE 16 101 102 struct devfs { 103 dev_t id; 104 fs_volume* volume; 105 recursive_lock lock; 106 int32 next_vnode_id; 107 hash_table* vnode_hash; 108 struct devfs_vnode* root_vnode; 109 }; 110 111 struct devfs_dir_cookie { 112 struct list_link link; 113 struct devfs_vnode* current; 114 int32 state; // iteration state 115 }; 116 117 struct devfs_cookie { 118 void* device_cookie; 119 }; 120 121 struct synchronous_io_cookie { 122 BaseDevice* device; 123 void* cookie; 124 }; 125 126 // directory iteration states 127 enum { 128 ITERATION_STATE_DOT = 0, 129 ITERATION_STATE_DOT_DOT = 1, 130 ITERATION_STATE_OTHERS = 2, 131 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT, 132 }; 133 134 // extern and in a private namespace only to make forward declaration possible 135 namespace { 136 extern fs_volume_ops kVolumeOps; 137 extern fs_vnode_ops kVnodeOps; 138 } 139 140 141 static status_t get_node_for_path(struct devfs* fs, const char* path, 142 struct devfs_vnode** _node); 143 static void get_device_name(struct devfs_vnode* vnode, char* buffer, 144 size_t size); 145 static status_t unpublish_node(struct devfs* fs, devfs_vnode* node, 146 mode_t type); 147 static status_t publish_device(struct devfs* fs, const char* path, 148 BaseDevice* device); 149 150 151 // The one and only allowed devfs instance 152 static struct devfs* sDeviceFileSystem = NULL; 153 154 155 // #pragma mark - devfs private 156 157 158 static timespec 159 current_timespec() 160 { 161 bigtime_t time = real_time_clock_usecs(); 162 163 timespec tv; 164 tv.tv_sec = time / 1000000; 165 tv.tv_nsec = (time % 1000000) * 1000; 166 return tv; 167 } 168 169 170 static int32 171 scan_mode(void) 172 { 173 // We may scan every device twice: 174 // - once before there is a boot device, 175 // - and once when there is one 176 177 return gBootDevice >= 0 ? kNormalScan : kBootScan; 178 } 179 180 181 static status_t 182 scan_for_drivers_if_needed(devfs_vnode* dir) 183 { 184 ASSERT(S_ISDIR(dir->stream.type)); 185 186 MutexLocker _(dir->stream.u.dir.scan_lock); 187 188 if (dir->stream.u.dir.scanned >= scan_mode()) 189 return B_OK; 190 191 KPath path; 192 if (path.InitCheck() != B_OK) 193 return B_NO_MEMORY; 194 195 get_device_name(dir, path.LockBuffer(), path.BufferSize()); 196 path.UnlockBuffer(); 197 198 TRACE(("scan_for_drivers_if_needed: mode %ld: %s\n", scan_mode(), 199 path.Path())); 200 201 // scan for drivers at this path 202 static int32 updateCycle = 1; 203 device_manager_probe(path.Path(), updateCycle++); 204 legacy_driver_probe(path.Path()); 205 206 dir->stream.u.dir.scanned = scan_mode(); 207 return B_OK; 208 } 209 210 211 static uint32 212 devfs_vnode_hash(void* _vnode, const void* _key, uint32 range) 213 { 214 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode; 215 const ino_t* key = (const ino_t*)_key; 216 217 if (vnode != NULL) 218 return vnode->id % range; 219 220 return (uint64)*key % range; 221 } 222 223 224 static int 225 devfs_vnode_compare(void* _vnode, const void* _key) 226 { 227 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode; 228 const ino_t* key = (const ino_t*)_key; 229 230 if (vnode->id == *key) 231 return 0; 232 233 return -1; 234 } 235 236 237 static void 238 init_directory_vnode(struct devfs_vnode* vnode, int permissions) 239 { 240 vnode->stream.type = S_IFDIR | permissions; 241 mutex_init(&vnode->stream.u.dir.scan_lock, "devfs scan"); 242 vnode->stream.u.dir.dir_head = NULL; 243 list_init(&vnode->stream.u.dir.cookies); 244 } 245 246 247 static struct devfs_vnode* 248 devfs_create_vnode(struct devfs* fs, devfs_vnode* parent, const char* name) 249 { 250 struct devfs_vnode* vnode; 251 252 vnode = (struct devfs_vnode*)malloc(sizeof(struct devfs_vnode)); 253 if (vnode == NULL) 254 return NULL; 255 256 memset(vnode, 0, sizeof(struct devfs_vnode)); 257 vnode->id = fs->next_vnode_id++; 258 259 vnode->name = strdup(name); 260 if (vnode->name == NULL) { 261 free(vnode); 262 return NULL; 263 } 264 265 vnode->creation_time = vnode->modification_time = current_timespec(); 266 vnode->uid = geteuid(); 267 vnode->gid = parent ? parent->gid : getegid(); 268 // inherit group from parent if possible 269 270 return vnode; 271 } 272 273 274 static status_t 275 devfs_delete_vnode(struct devfs* fs, struct devfs_vnode* vnode, 276 bool forceDelete) 277 { 278 // Can't delete it if it's in a directory or is a directory 279 // and has children 280 if (!forceDelete && ((S_ISDIR(vnode->stream.type) 281 && vnode->stream.u.dir.dir_head != NULL) 282 || vnode->dir_next != NULL)) 283 return B_NOT_ALLOWED; 284 285 // remove it from the global hash table 286 hash_remove(fs->vnode_hash, vnode); 287 288 if (S_ISCHR(vnode->stream.type)) { 289 if (vnode->stream.u.dev.partition == NULL) { 290 // pass the call through to the underlying device 291 vnode->stream.u.dev.device->Removed(); 292 } else { 293 // for partitions, we have to release the raw device but must 294 // not free the device info as it was inherited from the raw 295 // device and is still in use there 296 put_vnode(fs->volume, vnode->stream.u.dev.partition->raw_device->id); 297 } 298 } else if (S_ISDIR(vnode->stream.type)) { 299 mutex_destroy(&vnode->stream.u.dir.scan_lock); 300 } 301 302 free(vnode->name); 303 free(vnode); 304 305 return B_OK; 306 } 307 308 309 /*! Makes sure none of the dircookies point to the vnode passed in */ 310 static void 311 update_dir_cookies(struct devfs_vnode* dir, struct devfs_vnode* vnode) 312 { 313 struct devfs_dir_cookie* cookie = NULL; 314 315 while ((cookie = (devfs_dir_cookie*)list_get_next_item( 316 &dir->stream.u.dir.cookies, cookie)) != NULL) { 317 if (cookie->current == vnode) 318 cookie->current = vnode->dir_next; 319 } 320 } 321 322 323 static struct devfs_vnode* 324 devfs_find_in_dir(struct devfs_vnode* dir, const char* path) 325 { 326 struct devfs_vnode* vnode; 327 328 if (!S_ISDIR(dir->stream.type)) 329 return NULL; 330 331 if (!strcmp(path, ".")) 332 return dir; 333 if (!strcmp(path, "..")) 334 return dir->parent; 335 336 for (vnode = dir->stream.u.dir.dir_head; vnode; vnode = vnode->dir_next) { 337 //TRACE(("devfs_find_in_dir: looking at entry '%s'\n", vnode->name)); 338 if (strcmp(vnode->name, path) == 0) { 339 //TRACE(("devfs_find_in_dir: found it at %p\n", vnode)); 340 return vnode; 341 } 342 } 343 return NULL; 344 } 345 346 347 static status_t 348 devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode, 349 bool notify = true) 350 { 351 if (!S_ISDIR(dir->stream.type)) 352 return B_BAD_VALUE; 353 354 // make sure the directory stays sorted alphabetically 355 356 devfs_vnode* node = dir->stream.u.dir.dir_head; 357 devfs_vnode* last = NULL; 358 while (node && strcmp(node->name, vnode->name) < 0) { 359 last = node; 360 node = node->dir_next; 361 } 362 if (last == NULL) { 363 // the new vnode is the first entry in the list 364 vnode->dir_next = dir->stream.u.dir.dir_head; 365 dir->stream.u.dir.dir_head = vnode; 366 } else { 367 // insert after that node 368 vnode->dir_next = last->dir_next; 369 last->dir_next = vnode; 370 } 371 372 vnode->parent = dir; 373 dir->modification_time = current_timespec(); 374 375 if (notify) { 376 notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name, 377 vnode->id); 378 notify_stat_changed(sDeviceFileSystem->id, dir->id, 379 B_STAT_MODIFICATION_TIME); 380 } 381 return B_OK; 382 } 383 384 385 static status_t 386 devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode, 387 bool notify = true) 388 { 389 struct devfs_vnode* vnode = dir->stream.u.dir.dir_head; 390 struct devfs_vnode* lastNode = NULL; 391 392 for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) { 393 if (vnode == removeNode) { 394 // make sure no dircookies point to this vnode 395 update_dir_cookies(dir, vnode); 396 397 if (lastNode) 398 lastNode->dir_next = vnode->dir_next; 399 else 400 dir->stream.u.dir.dir_head = vnode->dir_next; 401 vnode->dir_next = NULL; 402 dir->modification_time = current_timespec(); 403 404 if (notify) { 405 notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name, 406 vnode->id); 407 notify_stat_changed(sDeviceFileSystem->id, dir->id, 408 B_STAT_MODIFICATION_TIME); 409 } 410 return B_OK; 411 } 412 } 413 return B_ENTRY_NOT_FOUND; 414 } 415 416 417 static status_t 418 add_partition(struct devfs* fs, struct devfs_vnode* device, const char* name, 419 const partition_info& info) 420 { 421 struct devfs_vnode* partitionNode; 422 status_t status; 423 424 if (!S_ISCHR(device->stream.type)) 425 return B_BAD_VALUE; 426 427 // we don't support nested partitions 428 if (device->stream.u.dev.partition != NULL) 429 return B_BAD_VALUE; 430 431 // reduce checks to a minimum - things like negative offsets could be useful 432 if (info.size < 0) 433 return B_BAD_VALUE; 434 435 // create partition 436 struct devfs_partition* partition = (struct devfs_partition*)malloc( 437 sizeof(struct devfs_partition)); 438 if (partition == NULL) 439 return B_NO_MEMORY; 440 441 memcpy(&partition->info, &info, sizeof(partition_info)); 442 443 RecursiveLocker locker(fs->lock); 444 445 // you cannot change a partition once set 446 if (devfs_find_in_dir(device->parent, name)) { 447 status = B_BAD_VALUE; 448 goto err1; 449 } 450 451 // increase reference count of raw device - 452 // the partition device really needs it 453 status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device); 454 if (status < B_OK) 455 goto err1; 456 457 // now create the partition vnode 458 partitionNode = devfs_create_vnode(fs, device->parent, name); 459 if (partitionNode == NULL) { 460 status = B_NO_MEMORY; 461 goto err2; 462 } 463 464 partitionNode->stream.type = device->stream.type; 465 partitionNode->stream.u.dev.device = device->stream.u.dev.device; 466 partitionNode->stream.u.dev.partition = partition; 467 468 hash_insert(fs->vnode_hash, partitionNode); 469 devfs_insert_in_dir(device->parent, partitionNode); 470 471 TRACE(("add_partition(name = %s, offset = %Ld, size = %Ld)\n", 472 name, info.offset, info.size)); 473 return B_OK; 474 475 err2: 476 put_vnode(fs->volume, device->id); 477 err1: 478 free(partition); 479 return status; 480 } 481 482 483 static inline void 484 translate_partition_access(devfs_partition* partition, off_t& offset, 485 size_t& size) 486 { 487 ASSERT(offset >= 0); 488 ASSERT(offset < partition->info.size); 489 490 size = (size_t)min_c((off_t)size, partition->info.size - offset); 491 offset += partition->info.offset; 492 } 493 494 495 static inline void 496 translate_partition_access(devfs_partition* partition, io_request* request) 497 { 498 off_t offset = request->Offset(); 499 500 ASSERT(offset >= 0); 501 ASSERT(offset + (off_t)request->Length() <= partition->info.size); 502 503 request->SetOffset(offset + partition->info.offset); 504 } 505 506 507 static status_t 508 get_node_for_path(struct devfs* fs, const char* path, 509 struct devfs_vnode** _node) 510 { 511 return vfs_get_fs_node_from_path(fs->volume, path, false, true, 512 (void**)_node); 513 } 514 515 516 static status_t 517 unpublish_node(struct devfs* fs, devfs_vnode* node, mode_t type) 518 { 519 if ((node->stream.type & S_IFMT) != type) 520 return B_BAD_TYPE; 521 522 recursive_lock_lock(&fs->lock); 523 524 status_t status = devfs_remove_from_dir(node->parent, node); 525 if (status < B_OK) 526 goto out; 527 528 status = remove_vnode(fs->volume, node->id); 529 530 out: 531 recursive_lock_unlock(&fs->lock); 532 return status; 533 } 534 535 536 static void 537 publish_node(devfs* fs, devfs_vnode* dirNode, struct devfs_vnode* node) 538 { 539 hash_insert(fs->vnode_hash, node); 540 devfs_insert_in_dir(dirNode, node); 541 } 542 543 544 static status_t 545 publish_directory(struct devfs* fs, const char* path) 546 { 547 ASSERT_LOCKED_RECURSIVE(&fs->lock); 548 549 // copy the path over to a temp buffer so we can munge it 550 KPath tempPath(path); 551 if (tempPath.InitCheck() != B_OK) 552 return B_NO_MEMORY; 553 554 TRACE(("devfs: publish directory \"%s\"\n", path)); 555 char* temp = tempPath.LockBuffer(); 556 557 // create the path leading to the device 558 // parse the path passed in, stripping out '/' 559 560 struct devfs_vnode* dir = fs->root_vnode; 561 struct devfs_vnode* vnode = NULL; 562 status_t status = B_OK; 563 int32 i = 0, last = 0; 564 565 while (temp[last]) { 566 if (temp[i] == '/') { 567 temp[i] = '\0'; 568 i++; 569 } else if (temp[i] != '\0') { 570 i++; 571 continue; 572 } 573 574 //TRACE(("\tpath component '%s'\n", &temp[last])); 575 576 // we have a path component 577 vnode = devfs_find_in_dir(dir, &temp[last]); 578 if (vnode) { 579 if (S_ISDIR(vnode->stream.type)) { 580 last = i; 581 dir = vnode; 582 continue; 583 } 584 585 // we hit something on our path that's not a directory 586 status = B_FILE_EXISTS; 587 goto out; 588 } else { 589 vnode = devfs_create_vnode(fs, dir, &temp[last]); 590 if (!vnode) { 591 status = B_NO_MEMORY; 592 goto out; 593 } 594 } 595 596 // set up the new directory 597 init_directory_vnode(vnode, 0755); 598 publish_node(sDeviceFileSystem, dir, vnode); 599 600 last = i; 601 dir = vnode; 602 } 603 604 out: 605 return status; 606 } 607 608 609 static status_t 610 new_node(struct devfs* fs, const char* path, struct devfs_vnode** _node, 611 struct devfs_vnode** _dir) 612 { 613 ASSERT_LOCKED_RECURSIVE(&fs->lock); 614 615 // copy the path over to a temp buffer so we can munge it 616 KPath tempPath(path); 617 if (tempPath.InitCheck() != B_OK) 618 return B_NO_MEMORY; 619 620 char* temp = tempPath.LockBuffer(); 621 622 // create the path leading to the device 623 // parse the path passed in, stripping out '/' 624 625 struct devfs_vnode* dir = fs->root_vnode; 626 struct devfs_vnode* vnode = NULL; 627 status_t status = B_OK; 628 int32 i = 0, last = 0; 629 bool atLeaf = false; 630 631 for (;;) { 632 if (temp[i] == '\0') { 633 atLeaf = true; // we'll be done after this one 634 } else if (temp[i] == '/') { 635 temp[i] = '\0'; 636 i++; 637 } else { 638 i++; 639 continue; 640 } 641 642 //TRACE(("\tpath component '%s'\n", &temp[last])); 643 644 // we have a path component 645 vnode = devfs_find_in_dir(dir, &temp[last]); 646 if (vnode) { 647 if (!atLeaf) { 648 // we are not at the leaf of the path, so as long as 649 // this is a dir we're okay 650 if (S_ISDIR(vnode->stream.type)) { 651 last = i; 652 dir = vnode; 653 continue; 654 } 655 } 656 // we are at the leaf and hit another node 657 // or we aren't but hit a non-dir node. 658 // we're screwed 659 status = B_FILE_EXISTS; 660 goto out; 661 } else { 662 vnode = devfs_create_vnode(fs, dir, &temp[last]); 663 if (!vnode) { 664 status = B_NO_MEMORY; 665 goto out; 666 } 667 } 668 669 // set up the new vnode 670 if (!atLeaf) { 671 // this is a dir 672 init_directory_vnode(vnode, 0755); 673 publish_node(fs, dir, vnode); 674 } else { 675 // this is the last component 676 // Note: We do not yet insert the node into the directory, as it 677 // is not yet fully initialized. Instead we return the directory 678 // vnode so that the calling function can insert it after all 679 // initialization is done. This ensures that no create notification 680 // is sent out for a vnode that is not yet fully valid. 681 *_node = vnode; 682 *_dir = dir; 683 break; 684 } 685 686 last = i; 687 dir = vnode; 688 } 689 690 out: 691 return status; 692 } 693 694 695 static status_t 696 publish_device(struct devfs* fs, const char* path, BaseDevice* device) 697 { 698 TRACE(("publish_device(path = \"%s\", device = %p)\n", path, device)); 699 700 if (sDeviceFileSystem == NULL) { 701 panic("publish_device() called before devfs mounted\n"); 702 return B_ERROR; 703 } 704 705 if (device == NULL || path == NULL || path[0] == '\0' || path[0] == '/') 706 return B_BAD_VALUE; 707 708 // TODO: this has to be done in the BaseDevice sub classes! 709 #if 0 710 // are the provided device hooks okay? 711 if (info->device_open == NULL || info->device_close == NULL 712 || info->device_free == NULL 713 || ((info->device_read == NULL || info->device_write == NULL) 714 && info->device_io == NULL)) 715 return B_BAD_VALUE; 716 #endif 717 718 struct devfs_vnode* node; 719 struct devfs_vnode* dirNode; 720 status_t status; 721 722 RecursiveLocker locker(&fs->lock); 723 724 status = new_node(fs, path, &node, &dirNode); 725 if (status != B_OK) 726 return status; 727 728 // all went fine, let's initialize the node 729 node->stream.type = S_IFCHR | 0644; 730 node->stream.u.dev.device = device; 731 device->SetID(node->id); 732 733 // the node is now fully valid and we may insert it into the dir 734 publish_node(fs, dirNode, node); 735 return B_OK; 736 } 737 738 739 /*! Construct complete device name (as used for device_open()). 740 This is safe to use only when the device is in use (and therefore 741 cannot be unpublished during the iteration). 742 */ 743 static void 744 get_device_name(struct devfs_vnode* vnode, char* buffer, size_t size) 745 { 746 RecursiveLocker _(sDeviceFileSystem->lock); 747 748 struct devfs_vnode* leaf = vnode; 749 size_t offset = 0; 750 751 // count levels 752 753 for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) { 754 offset += strlen(vnode->name) + 1; 755 } 756 757 // construct full path name 758 759 for (vnode = leaf; vnode->parent && vnode->parent != vnode; 760 vnode = vnode->parent) { 761 size_t length = strlen(vnode->name); 762 size_t start = offset - length - 1; 763 764 if (size >= offset) { 765 strcpy(buffer + start, vnode->name); 766 if (vnode != leaf) 767 buffer[offset - 1] = '/'; 768 } 769 770 offset = start; 771 } 772 } 773 774 775 static status_t 776 device_read(void* _cookie, off_t offset, void* buffer, size_t* length) 777 { 778 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie; 779 return cookie->device->Read(cookie->cookie, offset, buffer, length); 780 } 781 782 783 static status_t 784 device_write(void* _cookie, off_t offset, void* buffer, size_t* length) 785 { 786 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie; 787 return cookie->device->Write(cookie->cookie, offset, buffer, length); 788 } 789 790 791 static int 792 dump_node(int argc, char** argv) 793 { 794 if (argc != 2) { 795 print_debugger_command_usage(argv[0]); 796 return 0; 797 } 798 799 struct devfs_vnode* vnode = (struct devfs_vnode*)parse_expression(argv[1]); 800 if (vnode == NULL) { 801 kprintf("invalid node address\n"); 802 return 0; 803 } 804 805 kprintf("DEVFS NODE: %p\n", vnode); 806 kprintf(" id: %" B_PRIdINO "\n", vnode->id); 807 kprintf(" name: \"%s\"\n", vnode->name); 808 kprintf(" type: %x\n", vnode->stream.type); 809 kprintf(" parent: %p\n", vnode->parent); 810 kprintf(" dir next: %p\n", vnode->dir_next); 811 812 if (S_ISDIR(vnode->stream.type)) { 813 kprintf(" dir scanned: %" B_PRId32 "\n", vnode->stream.u.dir.scanned); 814 kprintf(" contents:\n"); 815 816 devfs_vnode* children = vnode->stream.u.dir.dir_head; 817 while (children != NULL) { 818 kprintf(" %p, id %" B_PRIdINO "\n", children, children->id); 819 children = children->dir_next; 820 } 821 } else if (S_ISLNK(vnode->stream.type)) { 822 kprintf(" symlink to: %s\n", vnode->stream.u.symlink.path); 823 } else { 824 kprintf(" device: %p\n", vnode->stream.u.dev.device); 825 kprintf(" partition: %p\n", vnode->stream.u.dev.partition); 826 if (vnode->stream.u.dev.partition != NULL) { 827 partition_info& info = vnode->stream.u.dev.partition->info; 828 kprintf(" raw device node: %p\n", 829 vnode->stream.u.dev.partition->raw_device); 830 kprintf(" offset: %" B_PRIdOFF "\n", info.offset); 831 kprintf(" size: %" B_PRIdOFF "\n", info.size); 832 kprintf(" block size: %" B_PRId32 "\n", info.logical_block_size); 833 kprintf(" session: %" B_PRId32 "\n", info.session); 834 kprintf(" partition: %" B_PRId32 "\n", info.partition); 835 kprintf(" device: %s\n", info.device); 836 set_debug_variable("_raw", 837 (addr_t)vnode->stream.u.dev.partition->raw_device); 838 } 839 } 840 841 return 0; 842 } 843 844 845 static int 846 dump_cookie(int argc, char** argv) 847 { 848 if (argc != 2) { 849 print_debugger_command_usage(argv[0]); 850 return 0; 851 } 852 853 uint64 address; 854 if (!evaluate_debug_expression(argv[1], &address, false)) 855 return 0; 856 857 struct devfs_cookie* cookie = (devfs_cookie*)(addr_t)address; 858 859 kprintf("DEVFS COOKIE: %p\n", cookie); 860 kprintf(" device_cookie: %p\n", cookie->device_cookie); 861 862 return 0; 863 } 864 865 866 // #pragma mark - file system interface 867 868 869 static status_t 870 devfs_mount(fs_volume* volume, const char* devfs, uint32 flags, 871 const char* args, ino_t* _rootNodeID) 872 { 873 struct devfs_vnode* vnode; 874 struct devfs* fs; 875 status_t err; 876 877 TRACE(("devfs_mount: entry\n")); 878 879 if (sDeviceFileSystem) { 880 TRACE(("double mount of devfs attempted\n")); 881 err = B_ERROR; 882 goto err; 883 } 884 885 fs = (struct devfs*)malloc(sizeof(struct devfs)); 886 if (fs == NULL) { 887 err = B_NO_MEMORY; 888 goto err; 889 } 890 891 volume->private_volume = fs; 892 volume->ops = &kVolumeOps; 893 fs->volume = volume; 894 fs->id = volume->id; 895 fs->next_vnode_id = 0; 896 897 recursive_lock_init(&fs->lock, "devfs lock"); 898 899 fs->vnode_hash = hash_init(DEVFS_HASH_SIZE, offsetof(devfs_vnode, all_next), 900 //(addr_t)&vnode->all_next - (addr_t)vnode, 901 &devfs_vnode_compare, &devfs_vnode_hash); 902 if (fs->vnode_hash == NULL) { 903 err = B_NO_MEMORY; 904 goto err2; 905 } 906 907 // create a vnode 908 vnode = devfs_create_vnode(fs, NULL, ""); 909 if (vnode == NULL) { 910 err = B_NO_MEMORY; 911 goto err3; 912 } 913 914 // set it up 915 vnode->parent = vnode; 916 917 // create a dir stream for it to hold 918 init_directory_vnode(vnode, 0755); 919 fs->root_vnode = vnode; 920 921 hash_insert(fs->vnode_hash, vnode); 922 publish_vnode(volume, vnode->id, vnode, &kVnodeOps, vnode->stream.type, 0); 923 924 *_rootNodeID = vnode->id; 925 sDeviceFileSystem = fs; 926 return B_OK; 927 928 err3: 929 hash_uninit(fs->vnode_hash); 930 err2: 931 recursive_lock_destroy(&fs->lock); 932 free(fs); 933 err: 934 return err; 935 } 936 937 938 static status_t 939 devfs_unmount(fs_volume* _volume) 940 { 941 struct devfs* fs = (struct devfs*)_volume->private_volume; 942 struct devfs_vnode* vnode; 943 struct hash_iterator i; 944 945 TRACE(("devfs_unmount: entry fs = %p\n", fs)); 946 947 recursive_lock_lock(&fs->lock); 948 949 // release the reference to the root 950 put_vnode(fs->volume, fs->root_vnode->id); 951 952 // delete all of the vnodes 953 hash_open(fs->vnode_hash, &i); 954 while ((vnode = (devfs_vnode*)hash_next(fs->vnode_hash, &i)) != NULL) { 955 devfs_delete_vnode(fs, vnode, true); 956 } 957 hash_close(fs->vnode_hash, &i, false); 958 hash_uninit(fs->vnode_hash); 959 960 recursive_lock_destroy(&fs->lock); 961 free(fs); 962 963 return B_OK; 964 } 965 966 967 static status_t 968 devfs_sync(fs_volume* _volume) 969 { 970 TRACE(("devfs_sync: entry\n")); 971 972 return B_OK; 973 } 974 975 976 static status_t 977 devfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id) 978 { 979 struct devfs* fs = (struct devfs*)_volume->private_volume; 980 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 981 struct devfs_vnode* vnode; 982 status_t status; 983 984 TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name)); 985 986 if (!S_ISDIR(dir->stream.type)) 987 return B_NOT_A_DIRECTORY; 988 989 // Make sure the directory contents are up to date 990 scan_for_drivers_if_needed(dir); 991 992 RecursiveLocker locker(&fs->lock); 993 994 // look it up 995 vnode = devfs_find_in_dir(dir, name); 996 if (vnode == NULL) { 997 // We don't have to rescan here, because thanks to node monitoring 998 // we already know it does not exist 999 return B_ENTRY_NOT_FOUND; 1000 } 1001 1002 status = get_vnode(fs->volume, vnode->id, NULL); 1003 if (status < B_OK) 1004 return status; 1005 1006 *_id = vnode->id; 1007 1008 return B_OK; 1009 } 1010 1011 1012 static status_t 1013 devfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer, 1014 size_t bufferSize) 1015 { 1016 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1017 1018 TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode)); 1019 1020 strlcpy(buffer, vnode->name, bufferSize); 1021 return B_OK; 1022 } 1023 1024 1025 static status_t 1026 devfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type, 1027 uint32* _flags, bool reenter) 1028 { 1029 struct devfs* fs = (struct devfs*)_volume->private_volume; 1030 1031 TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter)); 1032 1033 RecursiveLocker _(fs->lock); 1034 1035 struct devfs_vnode* vnode = (devfs_vnode*)hash_lookup(fs->vnode_hash, &id); 1036 if (vnode == NULL) 1037 return B_ENTRY_NOT_FOUND; 1038 1039 TRACE(("devfs_get_vnode: looked it up at %p\n", vnode)); 1040 1041 _vnode->private_node = vnode; 1042 _vnode->ops = &kVnodeOps; 1043 *_type = vnode->stream.type; 1044 *_flags = 0; 1045 return B_OK; 1046 } 1047 1048 1049 static status_t 1050 devfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter) 1051 { 1052 #ifdef TRACE_DEVFS 1053 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1054 1055 TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", 1056 vnode, vnode->id, reenter)); 1057 #endif 1058 1059 return B_OK; 1060 } 1061 1062 1063 static status_t 1064 devfs_remove_vnode(fs_volume* _volume, fs_vnode* _v, bool reenter) 1065 { 1066 struct devfs* fs = (struct devfs*)_volume->private_volume; 1067 struct devfs_vnode* vnode = (struct devfs_vnode*)_v->private_node; 1068 1069 TRACE(("devfs_removevnode: remove %p (%Ld), reenter %d\n", vnode, vnode->id, reenter)); 1070 1071 RecursiveLocker locker(&fs->lock); 1072 1073 if (vnode->dir_next) { 1074 // can't remove node if it's linked to the dir 1075 panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode); 1076 } 1077 1078 devfs_delete_vnode(fs, vnode, false); 1079 1080 return B_OK; 1081 } 1082 1083 1084 static status_t 1085 devfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int openMode, 1086 int perms, void** _cookie, ino_t* _newVnodeID) 1087 { 1088 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 1089 struct devfs* fs = (struct devfs*)_volume->private_volume; 1090 struct devfs_cookie* cookie; 1091 struct devfs_vnode* vnode; 1092 status_t status = B_OK; 1093 1094 TRACE(("devfs_create: dir %p, name \"%s\", openMode 0x%x, fs_cookie %p \n", 1095 dir, name, openMode, _cookie)); 1096 1097 RecursiveLocker locker(fs->lock); 1098 1099 // look it up 1100 vnode = devfs_find_in_dir(dir, name); 1101 if (vnode == NULL) 1102 return EROFS; 1103 1104 if ((openMode & O_EXCL) != 0) 1105 return B_FILE_EXISTS; 1106 1107 status = get_vnode(fs->volume, vnode->id, NULL); 1108 if (status != B_OK) 1109 return status; 1110 1111 locker.Unlock(); 1112 1113 *_newVnodeID = vnode->id; 1114 1115 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1116 if (cookie == NULL) { 1117 status = B_NO_MEMORY; 1118 goto err1; 1119 } 1120 1121 if (S_ISCHR(vnode->stream.type)) { 1122 BaseDevice* device = vnode->stream.u.dev.device; 1123 status = device->InitDevice(); 1124 if (status != B_OK) 1125 goto err2; 1126 1127 char path[B_FILE_NAME_LENGTH]; 1128 get_device_name(vnode, path, sizeof(path)); 1129 1130 status = device->Open(path, openMode, &cookie->device_cookie); 1131 if (status != B_OK) { 1132 device->UninitDevice(); 1133 goto err2; 1134 } 1135 } 1136 1137 *_cookie = cookie; 1138 return B_OK; 1139 1140 err2: 1141 free(cookie); 1142 err1: 1143 put_vnode(fs->volume, vnode->id); 1144 return status; 1145 } 1146 1147 1148 static status_t 1149 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode, 1150 void** _cookie) 1151 { 1152 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1153 struct devfs_cookie* cookie; 1154 status_t status = B_OK; 1155 1156 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie)); 1157 if (cookie == NULL) 1158 return B_NO_MEMORY; 1159 1160 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode, 1161 cookie)); 1162 1163 cookie->device_cookie = NULL; 1164 1165 if (S_ISCHR(vnode->stream.type)) { 1166 BaseDevice* device = vnode->stream.u.dev.device; 1167 status = device->InitDevice(); 1168 if (status != B_OK) { 1169 free(cookie); 1170 return status; 1171 } 1172 1173 char path[B_FILE_NAME_LENGTH]; 1174 get_device_name(vnode, path, sizeof(path)); 1175 1176 status = device->Open(path, openMode, &cookie->device_cookie); 1177 if (status != B_OK) 1178 device->UninitDevice(); 1179 } 1180 1181 if (status != B_OK) 1182 free(cookie); 1183 else 1184 *_cookie = cookie; 1185 1186 return status; 1187 } 1188 1189 1190 static status_t 1191 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1192 { 1193 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1194 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1195 1196 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie)); 1197 1198 if (S_ISCHR(vnode->stream.type)) { 1199 // pass the call through to the underlying device 1200 return vnode->stream.u.dev.device->Close(cookie->device_cookie); 1201 } 1202 1203 return B_OK; 1204 } 1205 1206 1207 static status_t 1208 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1209 { 1210 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1211 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1212 1213 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1214 1215 if (S_ISCHR(vnode->stream.type)) { 1216 // pass the call through to the underlying device 1217 vnode->stream.u.dev.device->Free(cookie->device_cookie); 1218 vnode->stream.u.dev.device->UninitDevice(); 1219 } 1220 1221 free(cookie); 1222 return B_OK; 1223 } 1224 1225 1226 static status_t 1227 devfs_fsync(fs_volume* _volume, fs_vnode* _v) 1228 { 1229 return B_OK; 1230 } 1231 1232 1233 static status_t 1234 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer, 1235 size_t* _bufferSize) 1236 { 1237 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node; 1238 1239 if (!S_ISLNK(link->stream.type)) 1240 return B_BAD_VALUE; 1241 1242 if (link->stream.u.symlink.length < *_bufferSize) 1243 *_bufferSize = link->stream.u.symlink.length; 1244 1245 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize); 1246 return B_OK; 1247 } 1248 1249 1250 static status_t 1251 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1252 void* buffer, size_t* _length) 1253 { 1254 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1255 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1256 1257 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n", 1258 // vnode, cookie, pos, _length)); 1259 1260 if (!S_ISCHR(vnode->stream.type)) 1261 return B_BAD_VALUE; 1262 1263 if (pos < 0) 1264 return B_BAD_VALUE; 1265 1266 if (vnode->stream.u.dev.partition != NULL) { 1267 if (pos >= vnode->stream.u.dev.partition->info.size) 1268 return B_BAD_VALUE; 1269 1270 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1271 } 1272 1273 if (*_length == 0) 1274 return B_OK; 1275 1276 // pass the call through to the device 1277 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer, 1278 _length); 1279 } 1280 1281 1282 static status_t 1283 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, 1284 const void* buffer, size_t* _length) 1285 { 1286 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1287 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1288 1289 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n", 1290 // vnode, cookie, pos, _length)); 1291 1292 if (!S_ISCHR(vnode->stream.type)) 1293 return B_BAD_VALUE; 1294 1295 if (pos < 0) 1296 return B_BAD_VALUE; 1297 1298 if (vnode->stream.u.dev.partition != NULL) { 1299 if (pos >= vnode->stream.u.dev.partition->info.size) 1300 return B_BAD_VALUE; 1301 1302 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length); 1303 } 1304 1305 if (*_length == 0) 1306 return B_OK; 1307 1308 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer, 1309 _length); 1310 } 1311 1312 1313 static status_t 1314 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name, 1315 int perms) 1316 { 1317 struct devfs* fs = (struct devfs*)_volume->private_volume; 1318 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node; 1319 1320 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name); 1321 if (vnode != NULL) { 1322 return EEXIST; 1323 } 1324 1325 vnode = devfs_create_vnode(fs, dir, name); 1326 if (vnode == NULL) { 1327 return B_NO_MEMORY; 1328 } 1329 1330 // set up the new directory 1331 init_directory_vnode(vnode, perms); 1332 publish_node(sDeviceFileSystem, dir, vnode); 1333 1334 return B_OK; 1335 } 1336 1337 1338 static status_t 1339 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie) 1340 { 1341 struct devfs* fs = (struct devfs*)_volume->private_volume; 1342 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1343 struct devfs_dir_cookie* cookie; 1344 1345 TRACE(("devfs_open_dir: vnode %p\n", vnode)); 1346 1347 if (!S_ISDIR(vnode->stream.type)) 1348 return B_BAD_VALUE; 1349 1350 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie)); 1351 if (cookie == NULL) 1352 return B_NO_MEMORY; 1353 1354 // make sure the directory has up-to-date contents 1355 scan_for_drivers_if_needed(vnode); 1356 1357 RecursiveLocker locker(&fs->lock); 1358 1359 cookie->current = vnode->stream.u.dir.dir_head; 1360 cookie->state = ITERATION_STATE_BEGIN; 1361 1362 list_add_item(&vnode->stream.u.dir.cookies, cookie); 1363 *_cookie = cookie; 1364 1365 return B_OK; 1366 } 1367 1368 1369 static status_t 1370 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1371 { 1372 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1373 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1374 struct devfs* fs = (struct devfs*)_volume->private_volume; 1375 1376 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie)); 1377 1378 RecursiveLocker locker(&fs->lock); 1379 1380 list_remove_item(&vnode->stream.u.dir.cookies, cookie); 1381 free(cookie); 1382 return B_OK; 1383 } 1384 1385 1386 static status_t 1387 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 1388 struct dirent* dirent, size_t bufferSize, uint32* _num) 1389 { 1390 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node; 1391 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1392 struct devfs* fs = (struct devfs*)_volume->private_volume; 1393 status_t status = B_OK; 1394 struct devfs_vnode* childNode = NULL; 1395 const char* name = NULL; 1396 struct devfs_vnode* nextChildNode = NULL; 1397 int32 nextState = cookie->state; 1398 1399 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n", 1400 _vnode, cookie, dirent, bufferSize)); 1401 1402 if (!S_ISDIR(vnode->stream.type)) 1403 return B_BAD_VALUE; 1404 1405 RecursiveLocker locker(&fs->lock); 1406 1407 switch (cookie->state) { 1408 case ITERATION_STATE_DOT: 1409 childNode = vnode; 1410 name = "."; 1411 nextChildNode = vnode->stream.u.dir.dir_head; 1412 nextState = cookie->state + 1; 1413 break; 1414 case ITERATION_STATE_DOT_DOT: 1415 childNode = vnode->parent; 1416 name = ".."; 1417 nextChildNode = vnode->stream.u.dir.dir_head; 1418 nextState = cookie->state + 1; 1419 break; 1420 default: 1421 childNode = cookie->current; 1422 if (childNode) { 1423 name = childNode->name; 1424 nextChildNode = childNode->dir_next; 1425 } 1426 break; 1427 } 1428 1429 if (!childNode) { 1430 *_num = 0; 1431 return B_OK; 1432 } 1433 1434 dirent->d_dev = fs->id; 1435 dirent->d_ino = childNode->id; 1436 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 1437 1438 if (dirent->d_reclen > bufferSize) 1439 return ENOBUFS; 1440 1441 status = user_strlcpy(dirent->d_name, name, 1442 bufferSize - sizeof(struct dirent)); 1443 if (status < B_OK) 1444 return status; 1445 1446 cookie->current = nextChildNode; 1447 cookie->state = nextState; 1448 *_num = 1; 1449 1450 return B_OK; 1451 } 1452 1453 1454 static status_t 1455 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 1456 { 1457 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1458 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie; 1459 struct devfs* fs = (struct devfs*)_volume->private_volume; 1460 1461 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie)); 1462 1463 if (!S_ISDIR(vnode->stream.type)) 1464 return B_BAD_VALUE; 1465 1466 RecursiveLocker locker(&fs->lock); 1467 1468 cookie->current = vnode->stream.u.dir.dir_head; 1469 cookie->state = ITERATION_STATE_BEGIN; 1470 1471 return B_OK; 1472 } 1473 1474 1475 /*! Forwards the opcode to the device driver, but also handles some devfs 1476 specific functionality, like partitions. 1477 */ 1478 static status_t 1479 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op, 1480 void* buffer, size_t length) 1481 { 1482 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node; 1483 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie; 1484 1485 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", 1486 vnode, cookie, op, buffer, length)); 1487 1488 // we are actually checking for a *device* here, we don't make the 1489 // distinction between char and block devices 1490 if (S_ISCHR(vnode->stream.type)) { 1491 switch (op) { 1492 case B_GET_GEOMETRY: 1493 { 1494 struct devfs_partition* partition 1495 = vnode->stream.u.dev.partition; 1496 if (partition == NULL) 1497 break; 1498 1499 device_geometry geometry; 1500 status_t status = vnode->stream.u.dev.device->Control( 1501 cookie->device_cookie, op, &geometry, length); 1502 if (status != B_OK) 1503 return status; 1504 1505 // patch values to match partition size 1506 if (geometry.bytes_per_sector == 0) 1507 geometry.bytes_per_sector = 512; 1508 1509 devfs_compute_geometry_size(&geometry, 1510 partition->info.size / geometry.bytes_per_sector, 1511 geometry.bytes_per_sector); 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 void 2211 devfs_compute_geometry_size(device_geometry* geometry, uint64 blockCount, 2212 uint32 blockSize) 2213 { 2214 if (blockCount > UINT32_MAX) 2215 geometry->head_count = (blockCount + UINT32_MAX - 1) / UINT32_MAX; 2216 else 2217 geometry->head_count = 1; 2218 2219 geometry->cylinder_count = 1; 2220 geometry->sectors_per_track = blockCount / geometry->head_count; 2221 geometry->bytes_per_sector = blockSize; 2222 } 2223 2224 2225 // #pragma mark - support API for legacy drivers 2226 2227 2228 extern "C" status_t 2229 devfs_rescan_driver(const char* driverName) 2230 { 2231 TRACE(("devfs_rescan_driver: %s\n", driverName)); 2232 2233 return legacy_driver_rescan(driverName); 2234 } 2235 2236 2237 extern "C" status_t 2238 devfs_publish_device(const char* path, device_hooks* hooks) 2239 { 2240 return legacy_driver_publish(path, hooks); 2241 } 2242 2243 2244