btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / device_manager / devfs.cpp
blobc0139f3fe3ac610f452a9cb3da9b5270e9ccd5b1
1 /*
2 * Copyright 2002-2016, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
10 #include <fs/devfs.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
18 #include <Drivers.h>
19 #include <KernelExport.h>
20 #include <NodeMonitor.h>
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 <vfs.h>
37 #include <vm/vm.h>
39 #include "BaseDevice.h"
40 #include "FileDevice.h"
41 #include "IORequest.h"
42 #include "legacy_drivers.h"
45 //#define TRACE_DEVFS
46 #ifdef TRACE_DEVFS
47 # define TRACE(x) dprintf x
48 #else
49 # define TRACE(x)
50 #endif
53 namespace {
55 struct devfs_partition {
56 struct devfs_vnode* raw_device;
57 partition_info info;
60 struct driver_entry;
62 enum {
63 kNotScanned = 0,
64 kBootScan,
65 kNormalScan,
68 struct devfs_stream {
69 mode_t type;
70 union {
71 struct stream_dir {
72 struct devfs_vnode* dir_head;
73 struct list cookies;
74 mutex scan_lock;
75 int32 scanned;
76 } dir;
77 struct stream_dev {
78 BaseDevice* device;
79 struct devfs_partition* partition;
80 } dev;
81 struct stream_symlink {
82 const char* path;
83 size_t length;
84 } symlink;
85 } u;
88 struct devfs_vnode {
89 struct devfs_vnode* all_next;
90 ino_t id;
91 char* name;
92 timespec modification_time;
93 timespec creation_time;
94 uid_t uid;
95 gid_t gid;
96 struct devfs_vnode* parent;
97 struct devfs_vnode* dir_next;
98 struct devfs_stream stream;
101 #define DEVFS_HASH_SIZE 16
104 struct NodeHash {
105 typedef ino_t KeyType;
106 typedef devfs_vnode ValueType;
108 size_t HashKey(KeyType key) const
110 return key ^ (key >> 32);
113 size_t Hash(ValueType* value) const
115 return HashKey(value->id);
118 bool Compare(KeyType key, ValueType* value) const
120 return value->id == key;
123 ValueType*& GetLink(ValueType* value) const
125 return value->all_next;
129 typedef BOpenHashTable<NodeHash> NodeTable;
131 struct devfs {
132 dev_t id;
133 fs_volume* volume;
134 recursive_lock lock;
135 int32 next_vnode_id;
136 NodeTable* vnode_hash;
137 struct devfs_vnode* root_vnode;
140 struct devfs_dir_cookie {
141 struct list_link link;
142 struct devfs_vnode* current;
143 int32 state; // iteration state
146 struct devfs_cookie {
147 void* device_cookie;
150 struct synchronous_io_cookie {
151 BaseDevice* device;
152 void* cookie;
155 // directory iteration states
156 enum {
157 ITERATION_STATE_DOT = 0,
158 ITERATION_STATE_DOT_DOT = 1,
159 ITERATION_STATE_OTHERS = 2,
160 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT,
163 // extern only to make forward declaration possible
164 extern fs_volume_ops kVolumeOps;
165 extern fs_vnode_ops kVnodeOps;
167 } // namespace
170 static status_t get_node_for_path(struct devfs* fs, const char* path,
171 struct devfs_vnode** _node);
172 static void get_device_name(struct devfs_vnode* vnode, char* buffer,
173 size_t size);
174 static status_t unpublish_node(struct devfs* fs, devfs_vnode* node,
175 mode_t type);
176 static status_t publish_device(struct devfs* fs, const char* path,
177 BaseDevice* device);
180 // The one and only allowed devfs instance
181 static struct devfs* sDeviceFileSystem = NULL;
184 // #pragma mark - devfs private
187 static timespec
188 current_timespec()
190 bigtime_t time = real_time_clock_usecs();
192 timespec tv;
193 tv.tv_sec = time / 1000000;
194 tv.tv_nsec = (time % 1000000) * 1000;
195 return tv;
199 static ino_t
200 get_parent_id(struct devfs_vnode* vnode)
202 if (vnode->parent != NULL)
203 return vnode->parent->id;
204 return -1;
208 static int32
209 scan_mode(void)
211 // We may scan every device twice:
212 // - once before there is a boot device,
213 // - and once when there is one
215 return gBootDevice >= 0 ? kNormalScan : kBootScan;
219 static status_t
220 scan_for_drivers_if_needed(devfs_vnode* dir)
222 ASSERT(S_ISDIR(dir->stream.type));
224 MutexLocker _(dir->stream.u.dir.scan_lock);
226 if (dir->stream.u.dir.scanned >= scan_mode())
227 return B_OK;
229 KPath path;
230 if (path.InitCheck() != B_OK)
231 return B_NO_MEMORY;
233 get_device_name(dir, path.LockBuffer(), path.BufferSize());
234 path.UnlockBuffer();
236 TRACE(("scan_for_drivers_if_needed: mode %ld: %s\n", scan_mode(),
237 path.Path()));
239 // scan for drivers at this path
240 static int32 updateCycle = 1;
241 device_manager_probe(path.Path(), updateCycle++);
242 legacy_driver_probe(path.Path());
244 dir->stream.u.dir.scanned = scan_mode();
245 return B_OK;
249 static void
250 init_directory_vnode(struct devfs_vnode* vnode, int permissions)
252 vnode->stream.type = S_IFDIR | permissions;
253 mutex_init(&vnode->stream.u.dir.scan_lock, "devfs scan");
254 vnode->stream.u.dir.dir_head = NULL;
255 list_init(&vnode->stream.u.dir.cookies);
259 static struct devfs_vnode*
260 devfs_create_vnode(struct devfs* fs, devfs_vnode* parent, const char* name)
262 struct devfs_vnode* vnode;
264 vnode = (struct devfs_vnode*)malloc(sizeof(struct devfs_vnode));
265 if (vnode == NULL)
266 return NULL;
268 memset(vnode, 0, sizeof(struct devfs_vnode));
269 vnode->id = fs->next_vnode_id++;
271 vnode->name = strdup(name);
272 if (vnode->name == NULL) {
273 free(vnode);
274 return NULL;
277 vnode->creation_time = vnode->modification_time = current_timespec();
278 vnode->uid = geteuid();
279 vnode->gid = parent ? parent->gid : getegid();
280 // inherit group from parent if possible
282 return vnode;
286 static status_t
287 devfs_delete_vnode(struct devfs* fs, struct devfs_vnode* vnode,
288 bool forceDelete)
290 // Can't delete it if it's in a directory or is a directory
291 // and has children
292 if (!forceDelete && ((S_ISDIR(vnode->stream.type)
293 && vnode->stream.u.dir.dir_head != NULL)
294 || vnode->dir_next != NULL))
295 return B_NOT_ALLOWED;
297 // remove it from the global hash table
298 fs->vnode_hash->Remove(vnode);
300 if (S_ISCHR(vnode->stream.type)) {
301 if (vnode->stream.u.dev.partition == NULL) {
302 // pass the call through to the underlying device
303 vnode->stream.u.dev.device->Removed();
304 } else {
305 // for partitions, we have to release the raw device but must
306 // not free the device info as it was inherited from the raw
307 // device and is still in use there
308 put_vnode(fs->volume, vnode->stream.u.dev.partition->raw_device->id);
310 } else if (S_ISDIR(vnode->stream.type)) {
311 mutex_destroy(&vnode->stream.u.dir.scan_lock);
314 free(vnode->name);
315 free(vnode);
317 return B_OK;
321 /*! Makes sure none of the dircookies point to the vnode passed in */
322 static void
323 update_dir_cookies(struct devfs_vnode* dir, struct devfs_vnode* vnode)
325 struct devfs_dir_cookie* cookie = NULL;
327 while ((cookie = (devfs_dir_cookie*)list_get_next_item(
328 &dir->stream.u.dir.cookies, cookie)) != NULL) {
329 if (cookie->current == vnode)
330 cookie->current = vnode->dir_next;
335 static struct devfs_vnode*
336 devfs_find_in_dir(struct devfs_vnode* dir, const char* path)
338 struct devfs_vnode* vnode;
340 if (!S_ISDIR(dir->stream.type))
341 return NULL;
343 if (!strcmp(path, "."))
344 return dir;
345 if (!strcmp(path, ".."))
346 return dir->parent;
348 for (vnode = dir->stream.u.dir.dir_head; vnode; vnode = vnode->dir_next) {
349 //TRACE(("devfs_find_in_dir: looking at entry '%s'\n", vnode->name));
350 if (strcmp(vnode->name, path) == 0) {
351 //TRACE(("devfs_find_in_dir: found it at %p\n", vnode));
352 return vnode;
355 return NULL;
359 static status_t
360 devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode,
361 bool notify = true)
363 if (!S_ISDIR(dir->stream.type))
364 return B_BAD_VALUE;
366 // make sure the directory stays sorted alphabetically
368 devfs_vnode* node = dir->stream.u.dir.dir_head;
369 devfs_vnode* last = NULL;
370 while (node && strcmp(node->name, vnode->name) < 0) {
371 last = node;
372 node = node->dir_next;
374 if (last == NULL) {
375 // the new vnode is the first entry in the list
376 vnode->dir_next = dir->stream.u.dir.dir_head;
377 dir->stream.u.dir.dir_head = vnode;
378 } else {
379 // insert after that node
380 vnode->dir_next = last->dir_next;
381 last->dir_next = vnode;
384 vnode->parent = dir;
385 dir->modification_time = current_timespec();
387 if (notify) {
388 notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name,
389 vnode->id);
390 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(dir), dir->id,
391 B_STAT_MODIFICATION_TIME);
393 return B_OK;
397 static status_t
398 devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode,
399 bool notify = true)
401 struct devfs_vnode* vnode = dir->stream.u.dir.dir_head;
402 struct devfs_vnode* lastNode = NULL;
404 for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) {
405 if (vnode == removeNode) {
406 // make sure no dircookies point to this vnode
407 update_dir_cookies(dir, vnode);
409 if (lastNode)
410 lastNode->dir_next = vnode->dir_next;
411 else
412 dir->stream.u.dir.dir_head = vnode->dir_next;
413 vnode->dir_next = NULL;
414 dir->modification_time = current_timespec();
416 if (notify) {
417 notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name,
418 vnode->id);
419 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(dir),
420 dir->id, B_STAT_MODIFICATION_TIME);
422 return B_OK;
425 return B_ENTRY_NOT_FOUND;
429 static status_t
430 add_partition(struct devfs* fs, struct devfs_vnode* device, const char* name,
431 const partition_info& info)
433 struct devfs_vnode* partitionNode;
434 status_t status;
436 if (!S_ISCHR(device->stream.type))
437 return B_BAD_VALUE;
439 // we don't support nested partitions
440 if (device->stream.u.dev.partition != NULL)
441 return B_BAD_VALUE;
443 // reduce checks to a minimum - things like negative offsets could be useful
444 if (info.size < 0)
445 return B_BAD_VALUE;
447 // create partition
448 struct devfs_partition* partition = (struct devfs_partition*)malloc(
449 sizeof(struct devfs_partition));
450 if (partition == NULL)
451 return B_NO_MEMORY;
453 memcpy(&partition->info, &info, sizeof(partition_info));
455 RecursiveLocker locker(fs->lock);
457 // you cannot change a partition once set
458 if (devfs_find_in_dir(device->parent, name)) {
459 status = B_BAD_VALUE;
460 goto err1;
463 // increase reference count of raw device -
464 // the partition device really needs it
465 status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device);
466 if (status < B_OK)
467 goto err1;
469 // now create the partition vnode
470 partitionNode = devfs_create_vnode(fs, device->parent, name);
471 if (partitionNode == NULL) {
472 status = B_NO_MEMORY;
473 goto err2;
476 partitionNode->stream.type = device->stream.type;
477 partitionNode->stream.u.dev.device = device->stream.u.dev.device;
478 partitionNode->stream.u.dev.partition = partition;
480 fs->vnode_hash->Insert(partitionNode);
481 devfs_insert_in_dir(device->parent, partitionNode);
483 TRACE(("add_partition(name = %s, offset = %Ld, size = %Ld)\n",
484 name, info.offset, info.size));
485 return B_OK;
487 err2:
488 put_vnode(fs->volume, device->id);
489 err1:
490 free(partition);
491 return status;
495 static inline void
496 translate_partition_access(devfs_partition* partition, off_t& offset,
497 size_t& size)
499 ASSERT(offset >= 0);
500 ASSERT(offset < partition->info.size);
502 size = (size_t)min_c((off_t)size, partition->info.size - offset);
503 offset += partition->info.offset;
507 static inline void
508 translate_partition_access(devfs_partition* partition, io_request* request)
510 off_t offset = request->Offset();
512 ASSERT(offset >= 0);
513 ASSERT(offset + (off_t)request->Length() <= partition->info.size);
515 request->SetOffset(offset + partition->info.offset);
519 static status_t
520 get_node_for_path(struct devfs* fs, const char* path,
521 struct devfs_vnode** _node)
523 return vfs_get_fs_node_from_path(fs->volume, path, false, true,
524 (void**)_node);
528 static status_t
529 unpublish_node(struct devfs* fs, devfs_vnode* node, mode_t type)
531 if ((node->stream.type & S_IFMT) != type)
532 return B_BAD_TYPE;
534 recursive_lock_lock(&fs->lock);
536 status_t status = devfs_remove_from_dir(node->parent, node);
537 if (status < B_OK)
538 goto out;
540 status = remove_vnode(fs->volume, node->id);
542 out:
543 recursive_lock_unlock(&fs->lock);
544 return status;
548 static void
549 publish_node(devfs* fs, devfs_vnode* dirNode, struct devfs_vnode* node)
551 fs->vnode_hash->Insert(node);
552 devfs_insert_in_dir(dirNode, node);
556 static status_t
557 publish_directory(struct devfs* fs, const char* path)
559 ASSERT_LOCKED_RECURSIVE(&fs->lock);
561 // copy the path over to a temp buffer so we can munge it
562 KPath tempPath(path);
563 if (tempPath.InitCheck() != B_OK)
564 return B_NO_MEMORY;
566 TRACE(("devfs: publish directory \"%s\"\n", path));
567 char* temp = tempPath.LockBuffer();
569 // create the path leading to the device
570 // parse the path passed in, stripping out '/'
572 struct devfs_vnode* dir = fs->root_vnode;
573 struct devfs_vnode* vnode = NULL;
574 status_t status = B_OK;
575 int32 i = 0, last = 0;
577 while (temp[last]) {
578 if (temp[i] == '/') {
579 temp[i] = '\0';
580 i++;
581 } else if (temp[i] != '\0') {
582 i++;
583 continue;
586 //TRACE(("\tpath component '%s'\n", &temp[last]));
588 // we have a path component
589 vnode = devfs_find_in_dir(dir, &temp[last]);
590 if (vnode) {
591 if (S_ISDIR(vnode->stream.type)) {
592 last = i;
593 dir = vnode;
594 continue;
597 // we hit something on our path that's not a directory
598 status = B_FILE_EXISTS;
599 goto out;
600 } else {
601 vnode = devfs_create_vnode(fs, dir, &temp[last]);
602 if (!vnode) {
603 status = B_NO_MEMORY;
604 goto out;
608 // set up the new directory
609 init_directory_vnode(vnode, 0755);
610 publish_node(sDeviceFileSystem, dir, vnode);
612 last = i;
613 dir = vnode;
616 out:
617 return status;
621 static status_t
622 new_node(struct devfs* fs, const char* path, struct devfs_vnode** _node,
623 struct devfs_vnode** _dir)
625 ASSERT_LOCKED_RECURSIVE(&fs->lock);
627 // copy the path over to a temp buffer so we can munge it
628 KPath tempPath(path);
629 if (tempPath.InitCheck() != B_OK)
630 return B_NO_MEMORY;
632 char* temp = tempPath.LockBuffer();
634 // create the path leading to the device
635 // parse the path passed in, stripping out '/'
637 struct devfs_vnode* dir = fs->root_vnode;
638 struct devfs_vnode* vnode = NULL;
639 status_t status = B_OK;
640 int32 i = 0, last = 0;
641 bool atLeaf = false;
643 for (;;) {
644 if (temp[i] == '\0') {
645 atLeaf = true; // we'll be done after this one
646 } else if (temp[i] == '/') {
647 temp[i] = '\0';
648 i++;
649 } else {
650 i++;
651 continue;
654 //TRACE(("\tpath component '%s'\n", &temp[last]));
656 // we have a path component
657 vnode = devfs_find_in_dir(dir, &temp[last]);
658 if (vnode) {
659 if (!atLeaf) {
660 // we are not at the leaf of the path, so as long as
661 // this is a dir we're okay
662 if (S_ISDIR(vnode->stream.type)) {
663 last = i;
664 dir = vnode;
665 continue;
668 // we are at the leaf and hit another node
669 // or we aren't but hit a non-dir node.
670 // we're screwed
671 status = B_FILE_EXISTS;
672 goto out;
673 } else {
674 vnode = devfs_create_vnode(fs, dir, &temp[last]);
675 if (!vnode) {
676 status = B_NO_MEMORY;
677 goto out;
681 // set up the new vnode
682 if (!atLeaf) {
683 // this is a dir
684 init_directory_vnode(vnode, 0755);
685 publish_node(fs, dir, vnode);
686 } else {
687 // this is the last component
688 // Note: We do not yet insert the node into the directory, as it
689 // is not yet fully initialized. Instead we return the directory
690 // vnode so that the calling function can insert it after all
691 // initialization is done. This ensures that no create notification
692 // is sent out for a vnode that is not yet fully valid.
693 *_node = vnode;
694 *_dir = dir;
695 break;
698 last = i;
699 dir = vnode;
702 out:
703 return status;
707 static status_t
708 publish_device(struct devfs* fs, const char* path, BaseDevice* device)
710 TRACE(("publish_device(path = \"%s\", device = %p)\n", path, device));
712 if (sDeviceFileSystem == NULL) {
713 panic("publish_device() called before devfs mounted\n");
714 return B_ERROR;
717 if (device == NULL || path == NULL || path[0] == '\0' || path[0] == '/')
718 return B_BAD_VALUE;
720 // TODO: this has to be done in the BaseDevice sub classes!
721 #if 0
722 // are the provided device hooks okay?
723 if (info->device_open == NULL || info->device_close == NULL
724 || info->device_free == NULL
725 || ((info->device_read == NULL || info->device_write == NULL)
726 && info->device_io == NULL))
727 return B_BAD_VALUE;
728 #endif
730 struct devfs_vnode* node;
731 struct devfs_vnode* dirNode;
732 status_t status;
734 RecursiveLocker locker(&fs->lock);
736 status = new_node(fs, path, &node, &dirNode);
737 if (status != B_OK)
738 return status;
740 // all went fine, let's initialize the node
741 node->stream.type = S_IFCHR | 0644;
742 node->stream.u.dev.device = device;
743 device->SetID(node->id);
745 // the node is now fully valid and we may insert it into the dir
746 publish_node(fs, dirNode, node);
747 return B_OK;
751 /*! Construct complete device name (as used for device_open()).
752 This is safe to use only when the device is in use (and therefore
753 cannot be unpublished during the iteration).
755 static void
756 get_device_name(struct devfs_vnode* vnode, char* buffer, size_t size)
758 RecursiveLocker _(sDeviceFileSystem->lock);
760 struct devfs_vnode* leaf = vnode;
761 size_t offset = 0;
763 // count levels
765 for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) {
766 offset += strlen(vnode->name) + 1;
769 // construct full path name
771 for (vnode = leaf; vnode->parent && vnode->parent != vnode;
772 vnode = vnode->parent) {
773 size_t length = strlen(vnode->name);
774 size_t start = offset - length - 1;
776 if (size >= offset) {
777 strcpy(buffer + start, vnode->name);
778 if (vnode != leaf)
779 buffer[offset - 1] = '/';
782 offset = start;
787 static status_t
788 device_read(void* _cookie, off_t offset, void* buffer, size_t* length)
790 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie;
791 return cookie->device->Read(cookie->cookie, offset, buffer, length);
795 static status_t
796 device_write(void* _cookie, off_t offset, void* buffer, size_t* length)
798 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie;
799 return cookie->device->Write(cookie->cookie, offset, buffer, length);
803 static int
804 dump_node(int argc, char** argv)
806 if (argc != 2) {
807 print_debugger_command_usage(argv[0]);
808 return 0;
811 struct devfs_vnode* vnode = (struct devfs_vnode*)parse_expression(argv[1]);
812 if (vnode == NULL) {
813 kprintf("invalid node address\n");
814 return 0;
817 kprintf("DEVFS NODE: %p\n", vnode);
818 kprintf(" id: %" B_PRIdINO "\n", vnode->id);
819 kprintf(" name: \"%s\"\n", vnode->name);
820 kprintf(" type: %x\n", vnode->stream.type);
821 kprintf(" parent: %p\n", vnode->parent);
822 kprintf(" dir next: %p\n", vnode->dir_next);
824 if (S_ISDIR(vnode->stream.type)) {
825 kprintf(" dir scanned: %" B_PRId32 "\n", vnode->stream.u.dir.scanned);
826 kprintf(" contents:\n");
828 devfs_vnode* children = vnode->stream.u.dir.dir_head;
829 while (children != NULL) {
830 kprintf(" %p, id %" B_PRIdINO "\n", children, children->id);
831 children = children->dir_next;
833 } else if (S_ISLNK(vnode->stream.type)) {
834 kprintf(" symlink to: %s\n", vnode->stream.u.symlink.path);
835 } else {
836 kprintf(" device: %p\n", vnode->stream.u.dev.device);
837 kprintf(" partition: %p\n", vnode->stream.u.dev.partition);
838 if (vnode->stream.u.dev.partition != NULL) {
839 partition_info& info = vnode->stream.u.dev.partition->info;
840 kprintf(" raw device node: %p\n",
841 vnode->stream.u.dev.partition->raw_device);
842 kprintf(" offset: %" B_PRIdOFF "\n", info.offset);
843 kprintf(" size: %" B_PRIdOFF "\n", info.size);
844 kprintf(" block size: %" B_PRId32 "\n", info.logical_block_size);
845 kprintf(" session: %" B_PRId32 "\n", info.session);
846 kprintf(" partition: %" B_PRId32 "\n", info.partition);
847 kprintf(" device: %s\n", info.device);
848 set_debug_variable("_raw",
849 (addr_t)vnode->stream.u.dev.partition->raw_device);
853 return 0;
857 static int
858 dump_cookie(int argc, char** argv)
860 if (argc != 2) {
861 print_debugger_command_usage(argv[0]);
862 return 0;
865 uint64 address;
866 if (!evaluate_debug_expression(argv[1], &address, false))
867 return 0;
869 struct devfs_cookie* cookie = (devfs_cookie*)(addr_t)address;
871 kprintf("DEVFS COOKIE: %p\n", cookie);
872 kprintf(" device_cookie: %p\n", cookie->device_cookie);
874 return 0;
878 // #pragma mark - file system interface
881 static status_t
882 devfs_mount(fs_volume* volume, const char* devfs, uint32 flags,
883 const char* args, ino_t* _rootNodeID)
885 struct devfs_vnode* vnode;
886 struct devfs* fs;
887 status_t err;
889 TRACE(("devfs_mount: entry\n"));
891 if (sDeviceFileSystem) {
892 TRACE(("double mount of devfs attempted\n"));
893 err = B_ERROR;
894 goto err;
897 fs = (struct devfs*)malloc(sizeof(struct devfs));
898 if (fs == NULL) {
899 err = B_NO_MEMORY;
900 goto err;
903 volume->private_volume = fs;
904 volume->ops = &kVolumeOps;
905 fs->volume = volume;
906 fs->id = volume->id;
907 fs->next_vnode_id = 0;
909 recursive_lock_init(&fs->lock, "devfs lock");
911 fs->vnode_hash = new(std::nothrow) NodeTable();
912 if (fs->vnode_hash == NULL || fs->vnode_hash->Init(DEVFS_HASH_SIZE) != B_OK) {
913 err = B_NO_MEMORY;
914 goto err2;
917 // create a vnode
918 vnode = devfs_create_vnode(fs, NULL, "");
919 if (vnode == NULL) {
920 err = B_NO_MEMORY;
921 goto err3;
924 // set it up
925 vnode->parent = vnode;
927 // create a dir stream for it to hold
928 init_directory_vnode(vnode, 0755);
929 fs->root_vnode = vnode;
931 fs->vnode_hash->Insert(vnode);
932 publish_vnode(volume, vnode->id, vnode, &kVnodeOps, vnode->stream.type, 0);
934 *_rootNodeID = vnode->id;
935 sDeviceFileSystem = fs;
936 return B_OK;
938 err3:
939 delete fs->vnode_hash;
940 err2:
941 recursive_lock_destroy(&fs->lock);
942 free(fs);
943 err:
944 return err;
948 static status_t
949 devfs_unmount(fs_volume* _volume)
951 struct devfs* fs = (struct devfs*)_volume->private_volume;
952 struct devfs_vnode* vnode;
954 TRACE(("devfs_unmount: entry fs = %p\n", fs));
956 recursive_lock_lock(&fs->lock);
958 // release the reference to the root
959 put_vnode(fs->volume, fs->root_vnode->id);
961 // delete all of the vnodes
962 NodeTable::Iterator i(fs->vnode_hash);
963 while (i.HasNext()) {
964 vnode = i.Next();
965 devfs_delete_vnode(fs, vnode, true);
967 delete fs->vnode_hash;
969 recursive_lock_destroy(&fs->lock);
970 free(fs);
972 return B_OK;
976 static status_t
977 devfs_sync(fs_volume* _volume)
979 TRACE(("devfs_sync: entry\n"));
981 return B_OK;
985 static status_t
986 devfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
988 struct devfs* fs = (struct devfs*)_volume->private_volume;
989 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
990 struct devfs_vnode* vnode;
991 status_t status;
993 TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name));
995 if (!S_ISDIR(dir->stream.type))
996 return B_NOT_A_DIRECTORY;
998 // Make sure the directory contents are up to date
999 scan_for_drivers_if_needed(dir);
1001 RecursiveLocker locker(&fs->lock);
1003 // look it up
1004 vnode = devfs_find_in_dir(dir, name);
1005 if (vnode == NULL) {
1006 // We don't have to rescan here, because thanks to node monitoring
1007 // we already know it does not exist
1008 return B_ENTRY_NOT_FOUND;
1011 status = get_vnode(fs->volume, vnode->id, NULL);
1012 if (status < B_OK)
1013 return status;
1015 *_id = vnode->id;
1017 return B_OK;
1021 static status_t
1022 devfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer,
1023 size_t bufferSize)
1025 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1027 TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode));
1029 strlcpy(buffer, vnode->name, bufferSize);
1030 return B_OK;
1034 static status_t
1035 devfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type,
1036 uint32* _flags, bool reenter)
1038 struct devfs* fs = (struct devfs*)_volume->private_volume;
1040 TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
1042 RecursiveLocker _(fs->lock);
1044 struct devfs_vnode* vnode = fs->vnode_hash->Lookup(id);
1045 if (vnode == NULL)
1046 return B_ENTRY_NOT_FOUND;
1048 TRACE(("devfs_get_vnode: looked it up at %p\n", vnode));
1050 _vnode->private_node = vnode;
1051 _vnode->ops = &kVnodeOps;
1052 *_type = vnode->stream.type;
1053 *_flags = 0;
1054 return B_OK;
1058 static status_t
1059 devfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
1061 #ifdef TRACE_DEVFS
1062 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1064 TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n",
1065 vnode, vnode->id, reenter));
1066 #endif
1068 return B_OK;
1072 static status_t
1073 devfs_remove_vnode(fs_volume* _volume, fs_vnode* _v, bool reenter)
1075 struct devfs* fs = (struct devfs*)_volume->private_volume;
1076 struct devfs_vnode* vnode = (struct devfs_vnode*)_v->private_node;
1078 TRACE(("devfs_removevnode: remove %p (%Ld), reenter %d\n", vnode, vnode->id, reenter));
1080 RecursiveLocker locker(&fs->lock);
1082 if (vnode->dir_next) {
1083 // can't remove node if it's linked to the dir
1084 panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode);
1087 devfs_delete_vnode(fs, vnode, false);
1089 return B_OK;
1093 static status_t
1094 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode,
1095 void** _cookie)
1097 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1098 struct devfs_cookie* cookie;
1099 status_t status = B_OK;
1101 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie));
1102 if (cookie == NULL)
1103 return B_NO_MEMORY;
1105 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode,
1106 cookie));
1108 cookie->device_cookie = NULL;
1110 if (S_ISCHR(vnode->stream.type)) {
1111 BaseDevice* device = vnode->stream.u.dev.device;
1112 status = device->InitDevice();
1113 if (status != B_OK) {
1114 free(cookie);
1115 return status;
1118 char path[B_FILE_NAME_LENGTH];
1119 get_device_name(vnode, path, sizeof(path));
1121 status = device->Open(path, openMode, &cookie->device_cookie);
1122 if (status != B_OK)
1123 device->UninitDevice();
1126 if (status != B_OK)
1127 free(cookie);
1128 else
1129 *_cookie = cookie;
1131 return status;
1135 static status_t
1136 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1138 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1139 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1141 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie));
1143 if (S_ISCHR(vnode->stream.type)) {
1144 // pass the call through to the underlying device
1145 return vnode->stream.u.dev.device->Close(cookie->device_cookie);
1148 return B_OK;
1152 static status_t
1153 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1155 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1156 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1158 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie));
1160 if (S_ISCHR(vnode->stream.type)) {
1161 // pass the call through to the underlying device
1162 vnode->stream.u.dev.device->Free(cookie->device_cookie);
1163 vnode->stream.u.dev.device->UninitDevice();
1166 free(cookie);
1167 return B_OK;
1171 static status_t
1172 devfs_fsync(fs_volume* _volume, fs_vnode* _v)
1174 return B_OK;
1178 static status_t
1179 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer,
1180 size_t* _bufferSize)
1182 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node;
1184 if (!S_ISLNK(link->stream.type))
1185 return B_BAD_VALUE;
1187 if (link->stream.u.symlink.length < *_bufferSize)
1188 *_bufferSize = link->stream.u.symlink.length;
1190 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize);
1191 return B_OK;
1195 static status_t
1196 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1197 void* buffer, size_t* _length)
1199 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1200 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1202 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n",
1203 // vnode, cookie, pos, _length));
1205 if (!S_ISCHR(vnode->stream.type))
1206 return B_BAD_VALUE;
1208 if (pos < 0)
1209 return B_BAD_VALUE;
1211 if (vnode->stream.u.dev.partition != NULL) {
1212 if (pos >= vnode->stream.u.dev.partition->info.size)
1213 return B_BAD_VALUE;
1215 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length);
1218 if (*_length == 0)
1219 return B_OK;
1221 // pass the call through to the device
1222 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer,
1223 _length);
1227 static status_t
1228 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1229 const void* buffer, size_t* _length)
1231 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1232 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1234 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n",
1235 // vnode, cookie, pos, _length));
1237 if (!S_ISCHR(vnode->stream.type))
1238 return B_BAD_VALUE;
1240 if (pos < 0)
1241 return B_BAD_VALUE;
1243 if (vnode->stream.u.dev.partition != NULL) {
1244 if (pos >= vnode->stream.u.dev.partition->info.size)
1245 return B_BAD_VALUE;
1247 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length);
1250 if (*_length == 0)
1251 return B_OK;
1253 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer,
1254 _length);
1258 static status_t
1259 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name,
1260 int perms)
1262 struct devfs* fs = (struct devfs*)_volume->private_volume;
1263 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
1265 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name);
1266 if (vnode != NULL) {
1267 return EEXIST;
1270 vnode = devfs_create_vnode(fs, dir, name);
1271 if (vnode == NULL) {
1272 return B_NO_MEMORY;
1275 // set up the new directory
1276 init_directory_vnode(vnode, perms);
1277 publish_node(sDeviceFileSystem, dir, vnode);
1279 return B_OK;
1283 static status_t
1284 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie)
1286 struct devfs* fs = (struct devfs*)_volume->private_volume;
1287 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1288 struct devfs_dir_cookie* cookie;
1290 TRACE(("devfs_open_dir: vnode %p\n", vnode));
1292 if (!S_ISDIR(vnode->stream.type))
1293 return B_BAD_VALUE;
1295 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie));
1296 if (cookie == NULL)
1297 return B_NO_MEMORY;
1299 // make sure the directory has up-to-date contents
1300 scan_for_drivers_if_needed(vnode);
1302 RecursiveLocker locker(&fs->lock);
1304 cookie->current = vnode->stream.u.dir.dir_head;
1305 cookie->state = ITERATION_STATE_BEGIN;
1307 list_add_item(&vnode->stream.u.dir.cookies, cookie);
1308 *_cookie = cookie;
1310 return B_OK;
1314 static status_t
1315 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1317 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1318 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1319 struct devfs* fs = (struct devfs*)_volume->private_volume;
1321 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie));
1323 RecursiveLocker locker(&fs->lock);
1325 list_remove_item(&vnode->stream.u.dir.cookies, cookie);
1326 free(cookie);
1327 return B_OK;
1331 static status_t
1332 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1333 struct dirent* dirent, size_t bufferSize, uint32* _num)
1335 struct devfs_vnode* vnode = (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 status_t status = B_OK;
1339 struct devfs_vnode* childNode = NULL;
1340 const char* name = NULL;
1341 struct devfs_vnode* nextChildNode = NULL;
1342 int32 nextState = cookie->state;
1344 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n",
1345 _vnode, cookie, dirent, bufferSize));
1347 if (!S_ISDIR(vnode->stream.type))
1348 return B_BAD_VALUE;
1350 RecursiveLocker locker(&fs->lock);
1352 switch (cookie->state) {
1353 case ITERATION_STATE_DOT:
1354 childNode = vnode;
1355 name = ".";
1356 nextChildNode = vnode->stream.u.dir.dir_head;
1357 nextState = cookie->state + 1;
1358 break;
1359 case ITERATION_STATE_DOT_DOT:
1360 childNode = vnode->parent;
1361 name = "..";
1362 nextChildNode = vnode->stream.u.dir.dir_head;
1363 nextState = cookie->state + 1;
1364 break;
1365 default:
1366 childNode = cookie->current;
1367 if (childNode) {
1368 name = childNode->name;
1369 nextChildNode = childNode->dir_next;
1371 break;
1374 if (!childNode) {
1375 *_num = 0;
1376 return B_OK;
1379 dirent->d_dev = fs->id;
1380 dirent->d_ino = childNode->id;
1381 dirent->d_reclen = strlen(name) + sizeof(struct dirent);
1383 if (dirent->d_reclen > bufferSize)
1384 return ENOBUFS;
1386 status = user_strlcpy(dirent->d_name, name,
1387 bufferSize - sizeof(struct dirent));
1388 if (status < B_OK)
1389 return status;
1391 cookie->current = nextChildNode;
1392 cookie->state = nextState;
1393 *_num = 1;
1395 return B_OK;
1399 static status_t
1400 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1402 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1403 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1404 struct devfs* fs = (struct devfs*)_volume->private_volume;
1406 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie));
1408 if (!S_ISDIR(vnode->stream.type))
1409 return B_BAD_VALUE;
1411 RecursiveLocker locker(&fs->lock);
1413 cookie->current = vnode->stream.u.dir.dir_head;
1414 cookie->state = ITERATION_STATE_BEGIN;
1416 return B_OK;
1420 /*! Forwards the opcode to the device driver, but also handles some devfs
1421 specific functionality, like partitions.
1423 static status_t
1424 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op,
1425 void* buffer, size_t length)
1427 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1428 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1430 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n",
1431 vnode, cookie, op, buffer, length));
1433 // we are actually checking for a *device* here, we don't make the
1434 // distinction between char and block devices
1435 if (S_ISCHR(vnode->stream.type)) {
1436 switch (op) {
1437 case B_GET_GEOMETRY:
1439 struct devfs_partition* partition
1440 = vnode->stream.u.dev.partition;
1441 if (partition == NULL)
1442 break;
1444 device_geometry geometry;
1445 status_t status = vnode->stream.u.dev.device->Control(
1446 cookie->device_cookie, op, &geometry, length);
1447 if (status != B_OK)
1448 return status;
1450 // patch values to match partition size
1451 if (geometry.bytes_per_sector == 0)
1452 geometry.bytes_per_sector = 512;
1454 devfs_compute_geometry_size(&geometry,
1455 partition->info.size / geometry.bytes_per_sector,
1456 geometry.bytes_per_sector);
1458 return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1461 case B_GET_DRIVER_FOR_DEVICE:
1463 #if 0
1464 const char* path;
1465 if (!vnode->stream.u.dev.driver)
1466 return B_ENTRY_NOT_FOUND;
1467 path = vnode->stream.u.dev.driver->path;
1468 if (path == NULL)
1469 return B_ENTRY_NOT_FOUND;
1471 return user_strlcpy((char*)buffer, path, B_FILE_NAME_LENGTH);
1472 #endif
1473 return B_ERROR;
1476 case B_GET_PARTITION_INFO:
1478 struct devfs_partition* partition
1479 = vnode->stream.u.dev.partition;
1480 if (!S_ISCHR(vnode->stream.type)
1481 || partition == NULL
1482 || length != sizeof(partition_info))
1483 return B_BAD_VALUE;
1485 return user_memcpy(buffer, &partition->info,
1486 sizeof(partition_info));
1489 case B_SET_PARTITION:
1490 return B_NOT_ALLOWED;
1492 case B_GET_PATH_FOR_DEVICE:
1494 char path[256];
1495 // TODO: we might want to actually find the mountpoint
1496 // of that instance of devfs...
1497 // but for now we assume it's mounted on /dev
1498 strcpy(path, "/dev/");
1499 get_device_name(vnode, path + 5, sizeof(path) - 5);
1500 if (length && (length <= strlen(path)))
1501 return ERANGE;
1502 return user_strlcpy((char*)buffer, path, sizeof(path));
1505 // old unsupported R5 private stuff
1507 case B_GET_NEXT_OPEN_DEVICE:
1508 dprintf("devfs: unsupported legacy ioctl B_GET_NEXT_OPEN_DEVICE\n");
1509 return B_UNSUPPORTED;
1510 case B_ADD_FIXED_DRIVER:
1511 dprintf("devfs: unsupported legacy ioctl B_ADD_FIXED_DRIVER\n");
1512 return B_UNSUPPORTED;
1513 case B_REMOVE_FIXED_DRIVER:
1514 dprintf("devfs: unsupported legacy ioctl B_REMOVE_FIXED_DRIVER\n");
1515 return B_UNSUPPORTED;
1519 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1520 op, buffer, length);
1523 return B_BAD_VALUE;
1527 static status_t
1528 devfs_set_flags(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1529 int flags)
1531 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1532 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1534 // we need to pass the O_NONBLOCK flag to the underlying device
1536 if (!S_ISCHR(vnode->stream.type))
1537 return B_NOT_ALLOWED;
1539 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1540 flags & O_NONBLOCK ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0);
1544 static status_t
1545 devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1546 uint8 event, selectsync* sync)
1548 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1549 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1551 if (!S_ISCHR(vnode->stream.type))
1552 return B_NOT_ALLOWED;
1554 // If the device has no select() hook, notify select() now.
1555 if (!vnode->stream.u.dev.device->HasSelect())
1556 return notify_select_event((selectsync*)sync, event);
1558 return vnode->stream.u.dev.device->Select(cookie->device_cookie, event,
1559 (selectsync*)sync);
1563 static status_t
1564 devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1565 uint8 event, selectsync* sync)
1567 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1568 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1570 if (!S_ISCHR(vnode->stream.type))
1571 return B_NOT_ALLOWED;
1573 // If the device has no select() hook, notify select() now.
1574 if (!vnode->stream.u.dev.device->HasDeselect())
1575 return B_OK;
1577 return vnode->stream.u.dev.device->Deselect(cookie->device_cookie, event,
1578 (selectsync*)sync);
1582 static bool
1583 devfs_can_page(fs_volume* _volume, fs_vnode* _vnode, void* cookie)
1585 #if 0
1586 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1588 //TRACE(("devfs_canpage: vnode %p\n", vnode));
1590 if (!S_ISCHR(vnode->stream.type)
1591 || vnode->stream.u.dev.device->Node() == NULL
1592 || cookie == NULL)
1593 return false;
1595 return vnode->stream.u.dev.device->HasRead()
1596 || vnode->stream.u.dev.device->HasIO();
1597 #endif
1598 // TODO: Obsolete hook!
1599 return false;
1603 static status_t
1604 devfs_read_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1605 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1607 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1608 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1610 //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1612 if (!S_ISCHR(vnode->stream.type)
1613 || (!vnode->stream.u.dev.device->HasRead()
1614 && !vnode->stream.u.dev.device->HasIO())
1615 || cookie == NULL)
1616 return B_NOT_ALLOWED;
1618 if (pos < 0)
1619 return B_BAD_VALUE;
1621 if (vnode->stream.u.dev.partition != NULL) {
1622 if (pos >= vnode->stream.u.dev.partition->info.size)
1623 return B_BAD_VALUE;
1625 translate_partition_access(vnode->stream.u.dev.partition, pos,
1626 *_numBytes);
1629 if (vnode->stream.u.dev.device->HasIO()) {
1630 // TODO: use io_requests for this!
1633 // emulate read_pages() using read()
1635 status_t error = B_OK;
1636 size_t bytesTransferred = 0;
1638 size_t remainingBytes = *_numBytes;
1639 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1640 size_t toRead = min_c(vecs[i].iov_len, remainingBytes);
1641 size_t length = toRead;
1643 error = vnode->stream.u.dev.device->Read(cookie->device_cookie, pos,
1644 vecs[i].iov_base, &length);
1645 if (error != B_OK)
1646 break;
1648 pos += length;
1649 bytesTransferred += length;
1650 remainingBytes -= length;
1652 if (length < toRead)
1653 break;
1656 *_numBytes = bytesTransferred;
1658 return bytesTransferred > 0 ? B_OK : error;
1662 static status_t
1663 devfs_write_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1664 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1666 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1667 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1669 //TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1671 if (!S_ISCHR(vnode->stream.type)
1672 || (!vnode->stream.u.dev.device->HasWrite()
1673 && !vnode->stream.u.dev.device->HasIO())
1674 || cookie == NULL)
1675 return B_NOT_ALLOWED;
1677 if (pos < 0)
1678 return B_BAD_VALUE;
1680 if (vnode->stream.u.dev.partition != NULL) {
1681 if (pos >= vnode->stream.u.dev.partition->info.size)
1682 return B_BAD_VALUE;
1684 translate_partition_access(vnode->stream.u.dev.partition, pos,
1685 *_numBytes);
1688 if (vnode->stream.u.dev.device->HasIO()) {
1689 // TODO: use io_requests for this!
1692 // emulate write_pages() using write()
1694 status_t error = B_OK;
1695 size_t bytesTransferred = 0;
1697 size_t remainingBytes = *_numBytes;
1698 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1699 size_t toWrite = min_c(vecs[i].iov_len, remainingBytes);
1700 size_t length = toWrite;
1702 error = vnode->stream.u.dev.device->Write(cookie->device_cookie, pos,
1703 vecs[i].iov_base, &length);
1704 if (error != B_OK)
1705 break;
1707 pos += length;
1708 bytesTransferred += length;
1709 remainingBytes -= length;
1711 if (length < toWrite)
1712 break;
1715 *_numBytes = bytesTransferred;
1717 return bytesTransferred > 0 ? B_OK : error;
1721 static status_t
1722 devfs_io(fs_volume* volume, fs_vnode* _vnode, void* _cookie,
1723 io_request* request)
1725 TRACE(("[%ld] devfs_io(request: %p)\n", find_thread(NULL), request));
1727 devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1728 devfs_cookie* cookie = (devfs_cookie*)_cookie;
1730 bool isWrite = request->IsWrite();
1732 if (!S_ISCHR(vnode->stream.type)
1733 || (((isWrite && !vnode->stream.u.dev.device->HasWrite())
1734 || (!isWrite && !vnode->stream.u.dev.device->HasRead()))
1735 && !vnode->stream.u.dev.device->HasIO())
1736 || cookie == NULL) {
1737 request->SetStatusAndNotify(B_NOT_ALLOWED);
1738 return B_NOT_ALLOWED;
1741 if (vnode->stream.u.dev.partition != NULL) {
1742 if (request->Offset() + (off_t)request->Length()
1743 > vnode->stream.u.dev.partition->info.size) {
1744 request->SetStatusAndNotify(B_BAD_VALUE);
1745 return B_BAD_VALUE;
1747 translate_partition_access(vnode->stream.u.dev.partition, request);
1750 if (vnode->stream.u.dev.device->HasIO())
1751 return vnode->stream.u.dev.device->IO(cookie->device_cookie, request);
1753 synchronous_io_cookie synchronousCookie = {
1754 vnode->stream.u.dev.device,
1755 cookie->device_cookie
1758 return vfs_synchronous_io(request,
1759 request->IsWrite() ? &device_write : &device_read, &synchronousCookie);
1763 static status_t
1764 devfs_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* stat)
1766 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1768 TRACE(("devfs_read_stat: vnode %p (%Ld), stat %p\n", vnode, vnode->id,
1769 stat));
1771 stat->st_ino = vnode->id;
1772 stat->st_rdev = vnode->id;
1773 stat->st_size = 0;
1774 stat->st_mode = vnode->stream.type;
1776 stat->st_nlink = 1;
1777 stat->st_blksize = 65536;
1778 stat->st_blocks = 0;
1780 stat->st_uid = vnode->uid;
1781 stat->st_gid = vnode->gid;
1783 stat->st_atim = current_timespec();
1784 stat->st_mtim = stat->st_ctim = vnode->modification_time;
1785 stat->st_crtim = vnode->creation_time;
1787 // TODO: this only works for partitions right now - if we should decide
1788 // to keep this feature, we should have a better solution
1789 if (S_ISCHR(vnode->stream.type)) {
1790 //device_geometry geometry;
1792 // if it's a real block device, then let's report a useful size
1793 if (vnode->stream.u.dev.partition != NULL) {
1794 stat->st_size = vnode->stream.u.dev.partition->info.size;
1795 #if 0
1796 } else if (vnode->stream.u.dev.info->control(cookie->device_cookie,
1797 B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) {
1798 stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count
1799 * geometry.sectors_per_track * geometry.bytes_per_sector;
1800 #endif
1803 // is this a real block device? then let's have it reported like that
1804 if (stat->st_size != 0)
1805 stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK);
1806 } else if (S_ISLNK(vnode->stream.type)) {
1807 stat->st_size = vnode->stream.u.symlink.length;
1810 return B_OK;
1814 static status_t
1815 devfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat,
1816 uint32 statMask)
1818 struct devfs* fs = (struct devfs*)_volume->private_volume;
1819 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1821 TRACE(("devfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id,
1822 stat));
1824 // we cannot change the size of anything
1825 if (statMask & B_STAT_SIZE)
1826 return B_BAD_VALUE;
1828 RecursiveLocker locker(&fs->lock);
1830 if (statMask & B_STAT_MODE) {
1831 vnode->stream.type = (vnode->stream.type & ~S_IUMSK)
1832 | (stat->st_mode & S_IUMSK);
1835 if (statMask & B_STAT_UID)
1836 vnode->uid = stat->st_uid;
1837 if (statMask & B_STAT_GID)
1838 vnode->gid = stat->st_gid;
1840 if (statMask & B_STAT_MODIFICATION_TIME)
1841 vnode->modification_time = stat->st_mtim;
1842 if (statMask & B_STAT_CREATION_TIME)
1843 vnode->creation_time = stat->st_crtim;
1845 notify_stat_changed(fs->id, get_parent_id(vnode), vnode->id, statMask);
1846 return B_OK;
1850 static status_t
1851 devfs_std_ops(int32 op, ...)
1853 switch (op) {
1854 case B_MODULE_INIT:
1855 add_debugger_command_etc("devfs_node", &dump_node,
1856 "Print info on a private devfs node",
1857 "<address>\n"
1858 "Prints information on a devfs node given by <address>.\n",
1860 add_debugger_command_etc("devfs_cookie", &dump_cookie,
1861 "Print info on a private devfs cookie",
1862 "<address>\n"
1863 "Prints information on a devfs cookie given by <address>.\n",
1866 legacy_driver_init();
1867 return B_OK;
1869 case B_MODULE_UNINIT:
1870 remove_debugger_command("devfs_node", &dump_node);
1871 remove_debugger_command("devfs_cookie", &dump_cookie);
1872 return B_OK;
1874 default:
1875 return B_ERROR;
1879 namespace {
1881 fs_volume_ops kVolumeOps = {
1882 &devfs_unmount,
1883 NULL,
1884 NULL,
1885 &devfs_sync,
1886 &devfs_get_vnode,
1888 // the other operations are not supported (attributes, indices, queries)
1889 NULL,
1892 fs_vnode_ops kVnodeOps = {
1893 &devfs_lookup,
1894 &devfs_get_vnode_name,
1896 &devfs_put_vnode,
1897 &devfs_remove_vnode,
1899 &devfs_can_page,
1900 &devfs_read_pages,
1901 &devfs_write_pages,
1903 &devfs_io,
1904 NULL, // cancel_io()
1906 NULL, // get_file_map
1908 /* common */
1909 &devfs_ioctl,
1910 &devfs_set_flags,
1911 &devfs_select,
1912 &devfs_deselect,
1913 &devfs_fsync,
1915 &devfs_read_link,
1916 NULL, // symlink
1917 NULL, // link
1918 NULL, // unlink
1919 NULL, // rename
1921 NULL, // access
1922 &devfs_read_stat,
1923 &devfs_write_stat,
1924 NULL,
1926 /* file */
1927 NULL, // create
1928 &devfs_open,
1929 &devfs_close,
1930 &devfs_free_cookie,
1931 &devfs_read,
1932 &devfs_write,
1934 /* directory */
1935 &devfs_create_dir,
1936 NULL, // remove_dir
1937 &devfs_open_dir,
1938 &devfs_close,
1939 // same as for files - it does nothing for directories, anyway
1940 &devfs_free_dir_cookie,
1941 &devfs_read_dir,
1942 &devfs_rewind_dir,
1944 // attributes operations are not supported
1945 NULL,
1948 } // namespace
1950 file_system_module_info gDeviceFileSystem = {
1952 "file_systems/devfs" B_CURRENT_FS_API_VERSION,
1954 devfs_std_ops,
1957 "devfs", // short_name
1958 "Device File System", // pretty_name
1959 0, // DDM flags
1961 NULL, // identify_partition()
1962 NULL, // scan_partition()
1963 NULL, // free_identify_partition_cookie()
1964 NULL, // free_partition_content_cookie()
1966 &devfs_mount,
1970 // #pragma mark - kernel private API
1973 extern "C" status_t
1974 devfs_unpublish_file_device(const char* path)
1976 // get the device node
1977 devfs_vnode* node;
1978 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
1979 if (status != B_OK)
1980 return status;
1982 if (!S_ISCHR(node->stream.type)) {
1983 put_vnode(sDeviceFileSystem->volume, node->id);
1984 return B_BAD_VALUE;
1987 // if it is indeed a file device, unpublish it
1988 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device);
1989 if (device == NULL) {
1990 put_vnode(sDeviceFileSystem->volume, node->id);
1991 return B_BAD_VALUE;
1994 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
1996 put_vnode(sDeviceFileSystem->volume, node->id);
1997 return status;
2001 extern "C" status_t
2002 devfs_publish_file_device(const char* path, const char* filePath)
2004 // create a FileDevice for the file
2005 FileDevice* device = new(std::nothrow) FileDevice;
2006 if (device == NULL)
2007 return B_NO_MEMORY;
2008 ObjectDeleter<FileDevice> deviceDeleter(device);
2010 status_t error = device->Init(filePath);
2011 if (error != B_OK)
2012 return error;
2014 // publish the device
2015 error = publish_device(sDeviceFileSystem, path, device);
2016 if (error != B_OK)
2017 return error;
2019 deviceDeleter.Detach();
2020 return B_OK;
2024 extern "C" status_t
2025 devfs_unpublish_partition(const char* path)
2027 devfs_vnode* node;
2028 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2029 if (status != B_OK)
2030 return status;
2032 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2033 put_vnode(sDeviceFileSystem->volume, node->id);
2034 return status;
2038 extern "C" status_t
2039 devfs_publish_partition(const char* name, const partition_info* info)
2041 if (name == NULL || info == NULL)
2042 return B_BAD_VALUE;
2043 TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n",
2044 name, info->device, info->offset, info->size));
2046 devfs_vnode* device;
2047 status_t status = get_node_for_path(sDeviceFileSystem, info->device,
2048 &device);
2049 if (status != B_OK)
2050 return status;
2052 status = add_partition(sDeviceFileSystem, device, name, *info);
2054 put_vnode(sDeviceFileSystem->volume, device->id);
2055 return status;
2059 extern "C" status_t
2060 devfs_rename_partition(const char* devicePath, const char* oldName,
2061 const char* newName)
2063 if (oldName == NULL || newName == NULL)
2064 return B_BAD_VALUE;
2066 devfs_vnode* device;
2067 status_t status = get_node_for_path(sDeviceFileSystem, devicePath, &device);
2068 if (status != B_OK)
2069 return status;
2071 RecursiveLocker locker(sDeviceFileSystem->lock);
2072 devfs_vnode* node = devfs_find_in_dir(device->parent, oldName);
2073 if (node == NULL)
2074 return B_ENTRY_NOT_FOUND;
2076 // check if the new path already exists
2077 if (devfs_find_in_dir(device->parent, newName))
2078 return B_BAD_VALUE;
2080 char* name = strdup(newName);
2081 if (name == NULL)
2082 return B_NO_MEMORY;
2084 devfs_remove_from_dir(device->parent, node, false);
2086 free(node->name);
2087 node->name = name;
2089 devfs_insert_in_dir(device->parent, node, false);
2091 notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName,
2092 device->parent->id, newName, node->id);
2093 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(device->parent),
2094 device->parent->id, B_STAT_MODIFICATION_TIME);
2096 return B_OK;
2100 extern "C" status_t
2101 devfs_publish_directory(const char* path)
2103 RecursiveLocker locker(&sDeviceFileSystem->lock);
2105 return publish_directory(sDeviceFileSystem, path);
2109 extern "C" status_t
2110 devfs_unpublish_device(const char* path, bool disconnect)
2112 devfs_vnode* node;
2113 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2114 if (status != B_OK)
2115 return status;
2117 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2119 if (status == B_OK && disconnect)
2120 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2122 put_vnode(sDeviceFileSystem->volume, node->id);
2123 return status;
2127 // #pragma mark - device_manager private API
2130 status_t
2131 devfs_publish_device(const char* path, BaseDevice* device)
2133 return publish_device(sDeviceFileSystem, path, device);
2137 status_t
2138 devfs_unpublish_device(BaseDevice* device, bool disconnect)
2140 devfs_vnode* node;
2141 status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(),
2142 (void**)&node);
2143 if (status != B_OK)
2144 return status;
2146 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2148 if (status == B_OK && disconnect)
2149 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2151 put_vnode(sDeviceFileSystem->volume, node->id);
2152 return status;
2156 /*! Gets the device for a given devfs relative path.
2157 If successful the call must be balanced with a call to devfs_put_device().
2159 status_t
2160 devfs_get_device(const char* path, BaseDevice*& _device)
2162 devfs_vnode* node;
2163 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2164 if (status != B_OK)
2165 return status;
2167 if (!S_ISCHR(node->stream.type) || node->stream.u.dev.partition != NULL) {
2168 put_vnode(sDeviceFileSystem->volume, node->id);
2169 return B_BAD_VALUE;
2172 _device = node->stream.u.dev.device;
2173 return B_OK;
2177 void
2178 devfs_put_device(BaseDevice* device)
2180 put_vnode(sDeviceFileSystem->volume, device->ID());
2184 void
2185 devfs_compute_geometry_size(device_geometry* geometry, uint64 blockCount,
2186 uint32 blockSize)
2188 if (blockCount > UINT32_MAX)
2189 geometry->head_count = (blockCount + UINT32_MAX - 1) / UINT32_MAX;
2190 else
2191 geometry->head_count = 1;
2193 geometry->cylinder_count = 1;
2194 geometry->sectors_per_track = blockCount / geometry->head_count;
2195 geometry->bytes_per_sector = blockSize;
2199 // #pragma mark - support API for legacy drivers
2202 extern "C" status_t
2203 devfs_rescan_driver(const char* driverName)
2205 TRACE(("devfs_rescan_driver: %s\n", driverName));
2207 return legacy_driver_rescan(driverName);
2211 extern "C" status_t
2212 devfs_publish_device(const char* path, device_hooks* hooks)
2214 return legacy_driver_publish(path, hooks);