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