BPicture: Fix archive constructor.
[haiku.git] / src / system / kernel / device_manager / devfs.cpp
blob7876ae3754494f1c7f0264253d44dd3c71d0872f
1 /*
2 * Copyright 2002-2012, 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 struct devfs_partition {
54 struct devfs_vnode* raw_device;
55 partition_info info;
58 struct driver_entry;
60 enum {
61 kNotScanned = 0,
62 kBootScan,
63 kNormalScan,
66 struct devfs_stream {
67 mode_t type;
68 union {
69 struct stream_dir {
70 struct devfs_vnode* dir_head;
71 struct list cookies;
72 mutex scan_lock;
73 int32 scanned;
74 } dir;
75 struct stream_dev {
76 BaseDevice* device;
77 struct devfs_partition* partition;
78 } dev;
79 struct stream_symlink {
80 const char* path;
81 size_t length;
82 } symlink;
83 } u;
86 struct devfs_vnode {
87 struct devfs_vnode* all_next;
88 ino_t id;
89 char* name;
90 timespec modification_time;
91 timespec creation_time;
92 uid_t uid;
93 gid_t gid;
94 struct devfs_vnode* parent;
95 struct devfs_vnode* dir_next;
96 struct devfs_stream stream;
99 #define DEVFS_HASH_SIZE 16
102 struct NodeHash {
103 typedef ino_t KeyType;
104 typedef devfs_vnode ValueType;
106 size_t HashKey(KeyType key) const
108 return key ^ (key >> 32);
111 size_t Hash(ValueType* value) const
113 return HashKey(value->id);
116 bool Compare(KeyType key, ValueType* value) const
118 return value->id == key;
121 ValueType*& GetLink(ValueType* value) const
123 return value->all_next;
127 typedef BOpenHashTable<NodeHash> NodeTable;
129 struct devfs {
130 dev_t id;
131 fs_volume* volume;
132 recursive_lock lock;
133 int32 next_vnode_id;
134 NodeTable* vnode_hash;
135 struct devfs_vnode* root_vnode;
138 struct devfs_dir_cookie {
139 struct list_link link;
140 struct devfs_vnode* current;
141 int32 state; // iteration state
144 struct devfs_cookie {
145 void* device_cookie;
148 struct synchronous_io_cookie {
149 BaseDevice* device;
150 void* cookie;
153 // directory iteration states
154 enum {
155 ITERATION_STATE_DOT = 0,
156 ITERATION_STATE_DOT_DOT = 1,
157 ITERATION_STATE_OTHERS = 2,
158 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT,
161 // extern and in a private namespace only to make forward declaration possible
162 namespace {
163 extern fs_volume_ops kVolumeOps;
164 extern fs_vnode_ops kVnodeOps;
168 static status_t get_node_for_path(struct devfs* fs, const char* path,
169 struct devfs_vnode** _node);
170 static void get_device_name(struct devfs_vnode* vnode, char* buffer,
171 size_t size);
172 static status_t unpublish_node(struct devfs* fs, devfs_vnode* node,
173 mode_t type);
174 static status_t publish_device(struct devfs* fs, const char* path,
175 BaseDevice* device);
178 // The one and only allowed devfs instance
179 static struct devfs* sDeviceFileSystem = NULL;
182 // #pragma mark - devfs private
185 static timespec
186 current_timespec()
188 bigtime_t time = real_time_clock_usecs();
190 timespec tv;
191 tv.tv_sec = time / 1000000;
192 tv.tv_nsec = (time % 1000000) * 1000;
193 return tv;
197 static int32
198 scan_mode(void)
200 // We may scan every device twice:
201 // - once before there is a boot device,
202 // - and once when there is one
204 return gBootDevice >= 0 ? kNormalScan : kBootScan;
208 static status_t
209 scan_for_drivers_if_needed(devfs_vnode* dir)
211 ASSERT(S_ISDIR(dir->stream.type));
213 MutexLocker _(dir->stream.u.dir.scan_lock);
215 if (dir->stream.u.dir.scanned >= scan_mode())
216 return B_OK;
218 KPath path;
219 if (path.InitCheck() != B_OK)
220 return B_NO_MEMORY;
222 get_device_name(dir, path.LockBuffer(), path.BufferSize());
223 path.UnlockBuffer();
225 TRACE(("scan_for_drivers_if_needed: mode %ld: %s\n", scan_mode(),
226 path.Path()));
228 // scan for drivers at this path
229 static int32 updateCycle = 1;
230 device_manager_probe(path.Path(), updateCycle++);
231 legacy_driver_probe(path.Path());
233 dir->stream.u.dir.scanned = scan_mode();
234 return B_OK;
238 static void
239 init_directory_vnode(struct devfs_vnode* vnode, int permissions)
241 vnode->stream.type = S_IFDIR | permissions;
242 mutex_init(&vnode->stream.u.dir.scan_lock, "devfs scan");
243 vnode->stream.u.dir.dir_head = NULL;
244 list_init(&vnode->stream.u.dir.cookies);
248 static struct devfs_vnode*
249 devfs_create_vnode(struct devfs* fs, devfs_vnode* parent, const char* name)
251 struct devfs_vnode* vnode;
253 vnode = (struct devfs_vnode*)malloc(sizeof(struct devfs_vnode));
254 if (vnode == NULL)
255 return NULL;
257 memset(vnode, 0, sizeof(struct devfs_vnode));
258 vnode->id = fs->next_vnode_id++;
260 vnode->name = strdup(name);
261 if (vnode->name == NULL) {
262 free(vnode);
263 return NULL;
266 vnode->creation_time = vnode->modification_time = current_timespec();
267 vnode->uid = geteuid();
268 vnode->gid = parent ? parent->gid : getegid();
269 // inherit group from parent if possible
271 return vnode;
275 static status_t
276 devfs_delete_vnode(struct devfs* fs, struct devfs_vnode* vnode,
277 bool forceDelete)
279 // Can't delete it if it's in a directory or is a directory
280 // and has children
281 if (!forceDelete && ((S_ISDIR(vnode->stream.type)
282 && vnode->stream.u.dir.dir_head != NULL)
283 || vnode->dir_next != NULL))
284 return B_NOT_ALLOWED;
286 // remove it from the global hash table
287 fs->vnode_hash->Remove(vnode);
289 if (S_ISCHR(vnode->stream.type)) {
290 if (vnode->stream.u.dev.partition == NULL) {
291 // pass the call through to the underlying device
292 vnode->stream.u.dev.device->Removed();
293 } else {
294 // for partitions, we have to release the raw device but must
295 // not free the device info as it was inherited from the raw
296 // device and is still in use there
297 put_vnode(fs->volume, vnode->stream.u.dev.partition->raw_device->id);
299 } else if (S_ISDIR(vnode->stream.type)) {
300 mutex_destroy(&vnode->stream.u.dir.scan_lock);
303 free(vnode->name);
304 free(vnode);
306 return B_OK;
310 /*! Makes sure none of the dircookies point to the vnode passed in */
311 static void
312 update_dir_cookies(struct devfs_vnode* dir, struct devfs_vnode* vnode)
314 struct devfs_dir_cookie* cookie = NULL;
316 while ((cookie = (devfs_dir_cookie*)list_get_next_item(
317 &dir->stream.u.dir.cookies, cookie)) != NULL) {
318 if (cookie->current == vnode)
319 cookie->current = vnode->dir_next;
324 static struct devfs_vnode*
325 devfs_find_in_dir(struct devfs_vnode* dir, const char* path)
327 struct devfs_vnode* vnode;
329 if (!S_ISDIR(dir->stream.type))
330 return NULL;
332 if (!strcmp(path, "."))
333 return dir;
334 if (!strcmp(path, ".."))
335 return dir->parent;
337 for (vnode = dir->stream.u.dir.dir_head; vnode; vnode = vnode->dir_next) {
338 //TRACE(("devfs_find_in_dir: looking at entry '%s'\n", vnode->name));
339 if (strcmp(vnode->name, path) == 0) {
340 //TRACE(("devfs_find_in_dir: found it at %p\n", vnode));
341 return vnode;
344 return NULL;
348 static status_t
349 devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode,
350 bool notify = true)
352 if (!S_ISDIR(dir->stream.type))
353 return B_BAD_VALUE;
355 // make sure the directory stays sorted alphabetically
357 devfs_vnode* node = dir->stream.u.dir.dir_head;
358 devfs_vnode* last = NULL;
359 while (node && strcmp(node->name, vnode->name) < 0) {
360 last = node;
361 node = node->dir_next;
363 if (last == NULL) {
364 // the new vnode is the first entry in the list
365 vnode->dir_next = dir->stream.u.dir.dir_head;
366 dir->stream.u.dir.dir_head = vnode;
367 } else {
368 // insert after that node
369 vnode->dir_next = last->dir_next;
370 last->dir_next = vnode;
373 vnode->parent = dir;
374 dir->modification_time = current_timespec();
376 if (notify) {
377 notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name,
378 vnode->id);
379 notify_stat_changed(sDeviceFileSystem->id, dir->id,
380 B_STAT_MODIFICATION_TIME);
382 return B_OK;
386 static status_t
387 devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode,
388 bool notify = true)
390 struct devfs_vnode* vnode = dir->stream.u.dir.dir_head;
391 struct devfs_vnode* lastNode = NULL;
393 for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) {
394 if (vnode == removeNode) {
395 // make sure no dircookies point to this vnode
396 update_dir_cookies(dir, vnode);
398 if (lastNode)
399 lastNode->dir_next = vnode->dir_next;
400 else
401 dir->stream.u.dir.dir_head = vnode->dir_next;
402 vnode->dir_next = NULL;
403 dir->modification_time = current_timespec();
405 if (notify) {
406 notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name,
407 vnode->id);
408 notify_stat_changed(sDeviceFileSystem->id, dir->id,
409 B_STAT_MODIFICATION_TIME);
411 return B_OK;
414 return B_ENTRY_NOT_FOUND;
418 static status_t
419 add_partition(struct devfs* fs, struct devfs_vnode* device, const char* name,
420 const partition_info& info)
422 struct devfs_vnode* partitionNode;
423 status_t status;
425 if (!S_ISCHR(device->stream.type))
426 return B_BAD_VALUE;
428 // we don't support nested partitions
429 if (device->stream.u.dev.partition != NULL)
430 return B_BAD_VALUE;
432 // reduce checks to a minimum - things like negative offsets could be useful
433 if (info.size < 0)
434 return B_BAD_VALUE;
436 // create partition
437 struct devfs_partition* partition = (struct devfs_partition*)malloc(
438 sizeof(struct devfs_partition));
439 if (partition == NULL)
440 return B_NO_MEMORY;
442 memcpy(&partition->info, &info, sizeof(partition_info));
444 RecursiveLocker locker(fs->lock);
446 // you cannot change a partition once set
447 if (devfs_find_in_dir(device->parent, name)) {
448 status = B_BAD_VALUE;
449 goto err1;
452 // increase reference count of raw device -
453 // the partition device really needs it
454 status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device);
455 if (status < B_OK)
456 goto err1;
458 // now create the partition vnode
459 partitionNode = devfs_create_vnode(fs, device->parent, name);
460 if (partitionNode == NULL) {
461 status = B_NO_MEMORY;
462 goto err2;
465 partitionNode->stream.type = device->stream.type;
466 partitionNode->stream.u.dev.device = device->stream.u.dev.device;
467 partitionNode->stream.u.dev.partition = partition;
469 fs->vnode_hash->Insert(partitionNode);
470 devfs_insert_in_dir(device->parent, partitionNode);
472 TRACE(("add_partition(name = %s, offset = %Ld, size = %Ld)\n",
473 name, info.offset, info.size));
474 return B_OK;
476 err2:
477 put_vnode(fs->volume, device->id);
478 err1:
479 free(partition);
480 return status;
484 static inline void
485 translate_partition_access(devfs_partition* partition, off_t& offset,
486 size_t& size)
488 ASSERT(offset >= 0);
489 ASSERT(offset < partition->info.size);
491 size = (size_t)min_c((off_t)size, partition->info.size - offset);
492 offset += partition->info.offset;
496 static inline void
497 translate_partition_access(devfs_partition* partition, io_request* request)
499 off_t offset = request->Offset();
501 ASSERT(offset >= 0);
502 ASSERT(offset + (off_t)request->Length() <= partition->info.size);
504 request->SetOffset(offset + partition->info.offset);
508 static status_t
509 get_node_for_path(struct devfs* fs, const char* path,
510 struct devfs_vnode** _node)
512 return vfs_get_fs_node_from_path(fs->volume, path, false, true,
513 (void**)_node);
517 static status_t
518 unpublish_node(struct devfs* fs, devfs_vnode* node, mode_t type)
520 if ((node->stream.type & S_IFMT) != type)
521 return B_BAD_TYPE;
523 recursive_lock_lock(&fs->lock);
525 status_t status = devfs_remove_from_dir(node->parent, node);
526 if (status < B_OK)
527 goto out;
529 status = remove_vnode(fs->volume, node->id);
531 out:
532 recursive_lock_unlock(&fs->lock);
533 return status;
537 static void
538 publish_node(devfs* fs, devfs_vnode* dirNode, struct devfs_vnode* node)
540 fs->vnode_hash->Insert(node);
541 devfs_insert_in_dir(dirNode, node);
545 static status_t
546 publish_directory(struct devfs* fs, const char* path)
548 ASSERT_LOCKED_RECURSIVE(&fs->lock);
550 // copy the path over to a temp buffer so we can munge it
551 KPath tempPath(path);
552 if (tempPath.InitCheck() != B_OK)
553 return B_NO_MEMORY;
555 TRACE(("devfs: publish directory \"%s\"\n", path));
556 char* temp = tempPath.LockBuffer();
558 // create the path leading to the device
559 // parse the path passed in, stripping out '/'
561 struct devfs_vnode* dir = fs->root_vnode;
562 struct devfs_vnode* vnode = NULL;
563 status_t status = B_OK;
564 int32 i = 0, last = 0;
566 while (temp[last]) {
567 if (temp[i] == '/') {
568 temp[i] = '\0';
569 i++;
570 } else if (temp[i] != '\0') {
571 i++;
572 continue;
575 //TRACE(("\tpath component '%s'\n", &temp[last]));
577 // we have a path component
578 vnode = devfs_find_in_dir(dir, &temp[last]);
579 if (vnode) {
580 if (S_ISDIR(vnode->stream.type)) {
581 last = i;
582 dir = vnode;
583 continue;
586 // we hit something on our path that's not a directory
587 status = B_FILE_EXISTS;
588 goto out;
589 } else {
590 vnode = devfs_create_vnode(fs, dir, &temp[last]);
591 if (!vnode) {
592 status = B_NO_MEMORY;
593 goto out;
597 // set up the new directory
598 init_directory_vnode(vnode, 0755);
599 publish_node(sDeviceFileSystem, dir, vnode);
601 last = i;
602 dir = vnode;
605 out:
606 return status;
610 static status_t
611 new_node(struct devfs* fs, const char* path, struct devfs_vnode** _node,
612 struct devfs_vnode** _dir)
614 ASSERT_LOCKED_RECURSIVE(&fs->lock);
616 // copy the path over to a temp buffer so we can munge it
617 KPath tempPath(path);
618 if (tempPath.InitCheck() != B_OK)
619 return B_NO_MEMORY;
621 char* temp = tempPath.LockBuffer();
623 // create the path leading to the device
624 // parse the path passed in, stripping out '/'
626 struct devfs_vnode* dir = fs->root_vnode;
627 struct devfs_vnode* vnode = NULL;
628 status_t status = B_OK;
629 int32 i = 0, last = 0;
630 bool atLeaf = false;
632 for (;;) {
633 if (temp[i] == '\0') {
634 atLeaf = true; // we'll be done after this one
635 } else if (temp[i] == '/') {
636 temp[i] = '\0';
637 i++;
638 } else {
639 i++;
640 continue;
643 //TRACE(("\tpath component '%s'\n", &temp[last]));
645 // we have a path component
646 vnode = devfs_find_in_dir(dir, &temp[last]);
647 if (vnode) {
648 if (!atLeaf) {
649 // we are not at the leaf of the path, so as long as
650 // this is a dir we're okay
651 if (S_ISDIR(vnode->stream.type)) {
652 last = i;
653 dir = vnode;
654 continue;
657 // we are at the leaf and hit another node
658 // or we aren't but hit a non-dir node.
659 // we're screwed
660 status = B_FILE_EXISTS;
661 goto out;
662 } else {
663 vnode = devfs_create_vnode(fs, dir, &temp[last]);
664 if (!vnode) {
665 status = B_NO_MEMORY;
666 goto out;
670 // set up the new vnode
671 if (!atLeaf) {
672 // this is a dir
673 init_directory_vnode(vnode, 0755);
674 publish_node(fs, dir, vnode);
675 } else {
676 // this is the last component
677 // Note: We do not yet insert the node into the directory, as it
678 // is not yet fully initialized. Instead we return the directory
679 // vnode so that the calling function can insert it after all
680 // initialization is done. This ensures that no create notification
681 // is sent out for a vnode that is not yet fully valid.
682 *_node = vnode;
683 *_dir = dir;
684 break;
687 last = i;
688 dir = vnode;
691 out:
692 return status;
696 static status_t
697 publish_device(struct devfs* fs, const char* path, BaseDevice* device)
699 TRACE(("publish_device(path = \"%s\", device = %p)\n", path, device));
701 if (sDeviceFileSystem == NULL) {
702 panic("publish_device() called before devfs mounted\n");
703 return B_ERROR;
706 if (device == NULL || path == NULL || path[0] == '\0' || path[0] == '/')
707 return B_BAD_VALUE;
709 // TODO: this has to be done in the BaseDevice sub classes!
710 #if 0
711 // are the provided device hooks okay?
712 if (info->device_open == NULL || info->device_close == NULL
713 || info->device_free == NULL
714 || ((info->device_read == NULL || info->device_write == NULL)
715 && info->device_io == NULL))
716 return B_BAD_VALUE;
717 #endif
719 struct devfs_vnode* node;
720 struct devfs_vnode* dirNode;
721 status_t status;
723 RecursiveLocker locker(&fs->lock);
725 status = new_node(fs, path, &node, &dirNode);
726 if (status != B_OK)
727 return status;
729 // all went fine, let's initialize the node
730 node->stream.type = S_IFCHR | 0644;
731 node->stream.u.dev.device = device;
732 device->SetID(node->id);
734 // the node is now fully valid and we may insert it into the dir
735 publish_node(fs, dirNode, node);
736 return B_OK;
740 /*! Construct complete device name (as used for device_open()).
741 This is safe to use only when the device is in use (and therefore
742 cannot be unpublished during the iteration).
744 static void
745 get_device_name(struct devfs_vnode* vnode, char* buffer, size_t size)
747 RecursiveLocker _(sDeviceFileSystem->lock);
749 struct devfs_vnode* leaf = vnode;
750 size_t offset = 0;
752 // count levels
754 for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) {
755 offset += strlen(vnode->name) + 1;
758 // construct full path name
760 for (vnode = leaf; vnode->parent && vnode->parent != vnode;
761 vnode = vnode->parent) {
762 size_t length = strlen(vnode->name);
763 size_t start = offset - length - 1;
765 if (size >= offset) {
766 strcpy(buffer + start, vnode->name);
767 if (vnode != leaf)
768 buffer[offset - 1] = '/';
771 offset = start;
776 static status_t
777 device_read(void* _cookie, off_t offset, void* buffer, size_t* length)
779 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie;
780 return cookie->device->Read(cookie->cookie, offset, buffer, length);
784 static status_t
785 device_write(void* _cookie, off_t offset, void* buffer, size_t* length)
787 synchronous_io_cookie* cookie = (synchronous_io_cookie*)_cookie;
788 return cookie->device->Write(cookie->cookie, offset, buffer, length);
792 static int
793 dump_node(int argc, char** argv)
795 if (argc != 2) {
796 print_debugger_command_usage(argv[0]);
797 return 0;
800 struct devfs_vnode* vnode = (struct devfs_vnode*)parse_expression(argv[1]);
801 if (vnode == NULL) {
802 kprintf("invalid node address\n");
803 return 0;
806 kprintf("DEVFS NODE: %p\n", vnode);
807 kprintf(" id: %" B_PRIdINO "\n", vnode->id);
808 kprintf(" name: \"%s\"\n", vnode->name);
809 kprintf(" type: %x\n", vnode->stream.type);
810 kprintf(" parent: %p\n", vnode->parent);
811 kprintf(" dir next: %p\n", vnode->dir_next);
813 if (S_ISDIR(vnode->stream.type)) {
814 kprintf(" dir scanned: %" B_PRId32 "\n", vnode->stream.u.dir.scanned);
815 kprintf(" contents:\n");
817 devfs_vnode* children = vnode->stream.u.dir.dir_head;
818 while (children != NULL) {
819 kprintf(" %p, id %" B_PRIdINO "\n", children, children->id);
820 children = children->dir_next;
822 } else if (S_ISLNK(vnode->stream.type)) {
823 kprintf(" symlink to: %s\n", vnode->stream.u.symlink.path);
824 } else {
825 kprintf(" device: %p\n", vnode->stream.u.dev.device);
826 kprintf(" partition: %p\n", vnode->stream.u.dev.partition);
827 if (vnode->stream.u.dev.partition != NULL) {
828 partition_info& info = vnode->stream.u.dev.partition->info;
829 kprintf(" raw device node: %p\n",
830 vnode->stream.u.dev.partition->raw_device);
831 kprintf(" offset: %" B_PRIdOFF "\n", info.offset);
832 kprintf(" size: %" B_PRIdOFF "\n", info.size);
833 kprintf(" block size: %" B_PRId32 "\n", info.logical_block_size);
834 kprintf(" session: %" B_PRId32 "\n", info.session);
835 kprintf(" partition: %" B_PRId32 "\n", info.partition);
836 kprintf(" device: %s\n", info.device);
837 set_debug_variable("_raw",
838 (addr_t)vnode->stream.u.dev.partition->raw_device);
842 return 0;
846 static int
847 dump_cookie(int argc, char** argv)
849 if (argc != 2) {
850 print_debugger_command_usage(argv[0]);
851 return 0;
854 uint64 address;
855 if (!evaluate_debug_expression(argv[1], &address, false))
856 return 0;
858 struct devfs_cookie* cookie = (devfs_cookie*)(addr_t)address;
860 kprintf("DEVFS COOKIE: %p\n", cookie);
861 kprintf(" device_cookie: %p\n", cookie->device_cookie);
863 return 0;
867 // #pragma mark - file system interface
870 static status_t
871 devfs_mount(fs_volume* volume, const char* devfs, uint32 flags,
872 const char* args, ino_t* _rootNodeID)
874 struct devfs_vnode* vnode;
875 struct devfs* fs;
876 status_t err;
878 TRACE(("devfs_mount: entry\n"));
880 if (sDeviceFileSystem) {
881 TRACE(("double mount of devfs attempted\n"));
882 err = B_ERROR;
883 goto err;
886 fs = (struct devfs*)malloc(sizeof(struct devfs));
887 if (fs == NULL) {
888 err = B_NO_MEMORY;
889 goto err;
892 volume->private_volume = fs;
893 volume->ops = &kVolumeOps;
894 fs->volume = volume;
895 fs->id = volume->id;
896 fs->next_vnode_id = 0;
898 recursive_lock_init(&fs->lock, "devfs lock");
900 fs->vnode_hash = new(std::nothrow) NodeTable();
901 if (fs->vnode_hash == NULL || fs->vnode_hash->Init(DEVFS_HASH_SIZE) != B_OK) {
902 err = B_NO_MEMORY;
903 goto err2;
906 // create a vnode
907 vnode = devfs_create_vnode(fs, NULL, "");
908 if (vnode == NULL) {
909 err = B_NO_MEMORY;
910 goto err3;
913 // set it up
914 vnode->parent = vnode;
916 // create a dir stream for it to hold
917 init_directory_vnode(vnode, 0755);
918 fs->root_vnode = vnode;
920 fs->vnode_hash->Insert(vnode);
921 publish_vnode(volume, vnode->id, vnode, &kVnodeOps, vnode->stream.type, 0);
923 *_rootNodeID = vnode->id;
924 sDeviceFileSystem = fs;
925 return B_OK;
927 err3:
928 delete fs->vnode_hash;
929 err2:
930 recursive_lock_destroy(&fs->lock);
931 free(fs);
932 err:
933 return err;
937 static status_t
938 devfs_unmount(fs_volume* _volume)
940 struct devfs* fs = (struct devfs*)_volume->private_volume;
941 struct devfs_vnode* vnode;
943 TRACE(("devfs_unmount: entry fs = %p\n", fs));
945 recursive_lock_lock(&fs->lock);
947 // release the reference to the root
948 put_vnode(fs->volume, fs->root_vnode->id);
950 // delete all of the vnodes
951 NodeTable::Iterator i(fs->vnode_hash);
952 while (i.HasNext()) {
953 vnode = i.Next();
954 devfs_delete_vnode(fs, vnode, true);
956 delete fs->vnode_hash;
958 recursive_lock_destroy(&fs->lock);
959 free(fs);
961 return B_OK;
965 static status_t
966 devfs_sync(fs_volume* _volume)
968 TRACE(("devfs_sync: entry\n"));
970 return B_OK;
974 static status_t
975 devfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
977 struct devfs* fs = (struct devfs*)_volume->private_volume;
978 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
979 struct devfs_vnode* vnode;
980 status_t status;
982 TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name));
984 if (!S_ISDIR(dir->stream.type))
985 return B_NOT_A_DIRECTORY;
987 // Make sure the directory contents are up to date
988 scan_for_drivers_if_needed(dir);
990 RecursiveLocker locker(&fs->lock);
992 // look it up
993 vnode = devfs_find_in_dir(dir, name);
994 if (vnode == NULL) {
995 // We don't have to rescan here, because thanks to node monitoring
996 // we already know it does not exist
997 return B_ENTRY_NOT_FOUND;
1000 status = get_vnode(fs->volume, vnode->id, NULL);
1001 if (status < B_OK)
1002 return status;
1004 *_id = vnode->id;
1006 return B_OK;
1010 static status_t
1011 devfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer,
1012 size_t bufferSize)
1014 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1016 TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode));
1018 strlcpy(buffer, vnode->name, bufferSize);
1019 return B_OK;
1023 static status_t
1024 devfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type,
1025 uint32* _flags, bool reenter)
1027 struct devfs* fs = (struct devfs*)_volume->private_volume;
1029 TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
1031 RecursiveLocker _(fs->lock);
1033 struct devfs_vnode* vnode = fs->vnode_hash->Lookup(id);
1034 if (vnode == NULL)
1035 return B_ENTRY_NOT_FOUND;
1037 TRACE(("devfs_get_vnode: looked it up at %p\n", vnode));
1039 _vnode->private_node = vnode;
1040 _vnode->ops = &kVnodeOps;
1041 *_type = vnode->stream.type;
1042 *_flags = 0;
1043 return B_OK;
1047 static status_t
1048 devfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
1050 #ifdef TRACE_DEVFS
1051 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1053 TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n",
1054 vnode, vnode->id, reenter));
1055 #endif
1057 return B_OK;
1061 static status_t
1062 devfs_remove_vnode(fs_volume* _volume, fs_vnode* _v, bool reenter)
1064 struct devfs* fs = (struct devfs*)_volume->private_volume;
1065 struct devfs_vnode* vnode = (struct devfs_vnode*)_v->private_node;
1067 TRACE(("devfs_removevnode: remove %p (%Ld), reenter %d\n", vnode, vnode->id, reenter));
1069 RecursiveLocker locker(&fs->lock);
1071 if (vnode->dir_next) {
1072 // can't remove node if it's linked to the dir
1073 panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode);
1076 devfs_delete_vnode(fs, vnode, false);
1078 return B_OK;
1082 static status_t
1083 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode,
1084 void** _cookie)
1086 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1087 struct devfs_cookie* cookie;
1088 status_t status = B_OK;
1090 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie));
1091 if (cookie == NULL)
1092 return B_NO_MEMORY;
1094 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode,
1095 cookie));
1097 cookie->device_cookie = NULL;
1099 if (S_ISCHR(vnode->stream.type)) {
1100 BaseDevice* device = vnode->stream.u.dev.device;
1101 status = device->InitDevice();
1102 if (status != B_OK) {
1103 free(cookie);
1104 return status;
1107 char path[B_FILE_NAME_LENGTH];
1108 get_device_name(vnode, path, sizeof(path));
1110 status = device->Open(path, openMode, &cookie->device_cookie);
1111 if (status != B_OK)
1112 device->UninitDevice();
1115 if (status != B_OK)
1116 free(cookie);
1117 else
1118 *_cookie = cookie;
1120 return status;
1124 static status_t
1125 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1127 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1128 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1130 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie));
1132 if (S_ISCHR(vnode->stream.type)) {
1133 // pass the call through to the underlying device
1134 return vnode->stream.u.dev.device->Close(cookie->device_cookie);
1137 return B_OK;
1141 static status_t
1142 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1144 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1145 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1147 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie));
1149 if (S_ISCHR(vnode->stream.type)) {
1150 // pass the call through to the underlying device
1151 vnode->stream.u.dev.device->Free(cookie->device_cookie);
1152 vnode->stream.u.dev.device->UninitDevice();
1155 free(cookie);
1156 return B_OK;
1160 static status_t
1161 devfs_fsync(fs_volume* _volume, fs_vnode* _v)
1163 return B_OK;
1167 static status_t
1168 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer,
1169 size_t* _bufferSize)
1171 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node;
1173 if (!S_ISLNK(link->stream.type))
1174 return B_BAD_VALUE;
1176 if (link->stream.u.symlink.length < *_bufferSize)
1177 *_bufferSize = link->stream.u.symlink.length;
1179 memcpy(buffer, link->stream.u.symlink.path, *_bufferSize);
1180 return B_OK;
1184 static status_t
1185 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1186 void* buffer, size_t* _length)
1188 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1189 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1191 //TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n",
1192 // vnode, cookie, pos, _length));
1194 if (!S_ISCHR(vnode->stream.type))
1195 return B_BAD_VALUE;
1197 if (pos < 0)
1198 return B_BAD_VALUE;
1200 if (vnode->stream.u.dev.partition != NULL) {
1201 if (pos >= vnode->stream.u.dev.partition->info.size)
1202 return B_BAD_VALUE;
1204 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length);
1207 if (*_length == 0)
1208 return B_OK;
1210 // pass the call through to the device
1211 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer,
1212 _length);
1216 static status_t
1217 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1218 const void* buffer, size_t* _length)
1220 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1221 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1223 //TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n",
1224 // vnode, cookie, pos, _length));
1226 if (!S_ISCHR(vnode->stream.type))
1227 return B_BAD_VALUE;
1229 if (pos < 0)
1230 return B_BAD_VALUE;
1232 if (vnode->stream.u.dev.partition != NULL) {
1233 if (pos >= vnode->stream.u.dev.partition->info.size)
1234 return B_BAD_VALUE;
1236 translate_partition_access(vnode->stream.u.dev.partition, pos, *_length);
1239 if (*_length == 0)
1240 return B_OK;
1242 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer,
1243 _length);
1247 static status_t
1248 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name,
1249 int perms)
1251 struct devfs* fs = (struct devfs*)_volume->private_volume;
1252 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
1254 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name);
1255 if (vnode != NULL) {
1256 return EEXIST;
1259 vnode = devfs_create_vnode(fs, dir, name);
1260 if (vnode == NULL) {
1261 return B_NO_MEMORY;
1264 // set up the new directory
1265 init_directory_vnode(vnode, perms);
1266 publish_node(sDeviceFileSystem, dir, vnode);
1268 return B_OK;
1272 static status_t
1273 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie)
1275 struct devfs* fs = (struct devfs*)_volume->private_volume;
1276 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1277 struct devfs_dir_cookie* cookie;
1279 TRACE(("devfs_open_dir: vnode %p\n", vnode));
1281 if (!S_ISDIR(vnode->stream.type))
1282 return B_BAD_VALUE;
1284 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie));
1285 if (cookie == NULL)
1286 return B_NO_MEMORY;
1288 // make sure the directory has up-to-date contents
1289 scan_for_drivers_if_needed(vnode);
1291 RecursiveLocker locker(&fs->lock);
1293 cookie->current = vnode->stream.u.dir.dir_head;
1294 cookie->state = ITERATION_STATE_BEGIN;
1296 list_add_item(&vnode->stream.u.dir.cookies, cookie);
1297 *_cookie = cookie;
1299 return B_OK;
1303 static status_t
1304 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1306 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1307 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1308 struct devfs* fs = (struct devfs*)_volume->private_volume;
1310 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie));
1312 RecursiveLocker locker(&fs->lock);
1314 list_remove_item(&vnode->stream.u.dir.cookies, cookie);
1315 free(cookie);
1316 return B_OK;
1320 static status_t
1321 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1322 struct dirent* dirent, size_t bufferSize, uint32* _num)
1324 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1325 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1326 struct devfs* fs = (struct devfs*)_volume->private_volume;
1327 status_t status = B_OK;
1328 struct devfs_vnode* childNode = NULL;
1329 const char* name = NULL;
1330 struct devfs_vnode* nextChildNode = NULL;
1331 int32 nextState = cookie->state;
1333 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n",
1334 _vnode, cookie, dirent, bufferSize));
1336 if (!S_ISDIR(vnode->stream.type))
1337 return B_BAD_VALUE;
1339 RecursiveLocker locker(&fs->lock);
1341 switch (cookie->state) {
1342 case ITERATION_STATE_DOT:
1343 childNode = vnode;
1344 name = ".";
1345 nextChildNode = vnode->stream.u.dir.dir_head;
1346 nextState = cookie->state + 1;
1347 break;
1348 case ITERATION_STATE_DOT_DOT:
1349 childNode = vnode->parent;
1350 name = "..";
1351 nextChildNode = vnode->stream.u.dir.dir_head;
1352 nextState = cookie->state + 1;
1353 break;
1354 default:
1355 childNode = cookie->current;
1356 if (childNode) {
1357 name = childNode->name;
1358 nextChildNode = childNode->dir_next;
1360 break;
1363 if (!childNode) {
1364 *_num = 0;
1365 return B_OK;
1368 dirent->d_dev = fs->id;
1369 dirent->d_ino = childNode->id;
1370 dirent->d_reclen = strlen(name) + sizeof(struct dirent);
1372 if (dirent->d_reclen > bufferSize)
1373 return ENOBUFS;
1375 status = user_strlcpy(dirent->d_name, name,
1376 bufferSize - sizeof(struct dirent));
1377 if (status < B_OK)
1378 return status;
1380 cookie->current = nextChildNode;
1381 cookie->state = nextState;
1382 *_num = 1;
1384 return B_OK;
1388 static status_t
1389 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1391 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1392 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1393 struct devfs* fs = (struct devfs*)_volume->private_volume;
1395 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie));
1397 if (!S_ISDIR(vnode->stream.type))
1398 return B_BAD_VALUE;
1400 RecursiveLocker locker(&fs->lock);
1402 cookie->current = vnode->stream.u.dir.dir_head;
1403 cookie->state = ITERATION_STATE_BEGIN;
1405 return B_OK;
1409 /*! Forwards the opcode to the device driver, but also handles some devfs
1410 specific functionality, like partitions.
1412 static status_t
1413 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op,
1414 void* buffer, size_t length)
1416 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1417 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1419 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n",
1420 vnode, cookie, op, buffer, length));
1422 // we are actually checking for a *device* here, we don't make the
1423 // distinction between char and block devices
1424 if (S_ISCHR(vnode->stream.type)) {
1425 switch (op) {
1426 case B_GET_GEOMETRY:
1428 struct devfs_partition* partition
1429 = vnode->stream.u.dev.partition;
1430 if (partition == NULL)
1431 break;
1433 device_geometry geometry;
1434 status_t status = vnode->stream.u.dev.device->Control(
1435 cookie->device_cookie, op, &geometry, length);
1436 if (status != B_OK)
1437 return status;
1439 // patch values to match partition size
1440 if (geometry.bytes_per_sector == 0)
1441 geometry.bytes_per_sector = 512;
1443 devfs_compute_geometry_size(&geometry,
1444 partition->info.size / geometry.bytes_per_sector,
1445 geometry.bytes_per_sector);
1447 return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1450 case B_GET_DRIVER_FOR_DEVICE:
1452 #if 0
1453 const char* path;
1454 if (!vnode->stream.u.dev.driver)
1455 return B_ENTRY_NOT_FOUND;
1456 path = vnode->stream.u.dev.driver->path;
1457 if (path == NULL)
1458 return B_ENTRY_NOT_FOUND;
1460 return user_strlcpy((char*)buffer, path, B_FILE_NAME_LENGTH);
1461 #endif
1462 return B_ERROR;
1465 case B_GET_PARTITION_INFO:
1467 struct devfs_partition* partition
1468 = vnode->stream.u.dev.partition;
1469 if (!S_ISCHR(vnode->stream.type)
1470 || partition == NULL
1471 || length != sizeof(partition_info))
1472 return B_BAD_VALUE;
1474 return user_memcpy(buffer, &partition->info,
1475 sizeof(partition_info));
1478 case B_SET_PARTITION:
1479 return B_NOT_ALLOWED;
1481 case B_GET_PATH_FOR_DEVICE:
1483 char path[256];
1484 // TODO: we might want to actually find the mountpoint
1485 // of that instance of devfs...
1486 // but for now we assume it's mounted on /dev
1487 strcpy(path, "/dev/");
1488 get_device_name(vnode, path + 5, sizeof(path) - 5);
1489 if (length && (length <= strlen(path)))
1490 return ERANGE;
1491 return user_strlcpy((char*)buffer, path, sizeof(path));
1494 // old unsupported R5 private stuff
1496 case B_GET_NEXT_OPEN_DEVICE:
1497 dprintf("devfs: unsupported legacy ioctl B_GET_NEXT_OPEN_DEVICE\n");
1498 return B_UNSUPPORTED;
1499 case B_ADD_FIXED_DRIVER:
1500 dprintf("devfs: unsupported legacy ioctl B_ADD_FIXED_DRIVER\n");
1501 return B_UNSUPPORTED;
1502 case B_REMOVE_FIXED_DRIVER:
1503 dprintf("devfs: unsupported legacy ioctl B_REMOVE_FIXED_DRIVER\n");
1504 return B_UNSUPPORTED;
1508 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1509 op, buffer, length);
1512 return B_BAD_VALUE;
1516 static status_t
1517 devfs_set_flags(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1518 int flags)
1520 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1521 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1523 // we need to pass the O_NONBLOCK flag to the underlying device
1525 if (!S_ISCHR(vnode->stream.type))
1526 return B_NOT_ALLOWED;
1528 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1529 flags & O_NONBLOCK ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0);
1533 static status_t
1534 devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1535 uint8 event, selectsync* sync)
1537 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1538 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1540 if (!S_ISCHR(vnode->stream.type))
1541 return B_NOT_ALLOWED;
1543 // If the device has no select() hook, notify select() now.
1544 if (!vnode->stream.u.dev.device->HasSelect())
1545 return notify_select_event((selectsync*)sync, event);
1547 return vnode->stream.u.dev.device->Select(cookie->device_cookie, event,
1548 (selectsync*)sync);
1552 static status_t
1553 devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1554 uint8 event, selectsync* sync)
1556 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1557 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1559 if (!S_ISCHR(vnode->stream.type))
1560 return B_NOT_ALLOWED;
1562 // If the device has no select() hook, notify select() now.
1563 if (!vnode->stream.u.dev.device->HasDeselect())
1564 return B_OK;
1566 return vnode->stream.u.dev.device->Deselect(cookie->device_cookie, event,
1567 (selectsync*)sync);
1571 static bool
1572 devfs_can_page(fs_volume* _volume, fs_vnode* _vnode, void* cookie)
1574 #if 0
1575 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1577 //TRACE(("devfs_canpage: vnode %p\n", vnode));
1579 if (!S_ISCHR(vnode->stream.type)
1580 || vnode->stream.u.dev.device->Node() == NULL
1581 || cookie == NULL)
1582 return false;
1584 return vnode->stream.u.dev.device->HasRead()
1585 || vnode->stream.u.dev.device->HasIO();
1586 #endif
1587 // TODO: Obsolete hook!
1588 return false;
1592 static status_t
1593 devfs_read_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1594 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1596 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1597 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1599 //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1601 if (!S_ISCHR(vnode->stream.type)
1602 || (!vnode->stream.u.dev.device->HasRead()
1603 && !vnode->stream.u.dev.device->HasIO())
1604 || cookie == NULL)
1605 return B_NOT_ALLOWED;
1607 if (pos < 0)
1608 return B_BAD_VALUE;
1610 if (vnode->stream.u.dev.partition != NULL) {
1611 if (pos >= vnode->stream.u.dev.partition->info.size)
1612 return B_BAD_VALUE;
1614 translate_partition_access(vnode->stream.u.dev.partition, pos,
1615 *_numBytes);
1618 if (vnode->stream.u.dev.device->HasIO()) {
1619 // TODO: use io_requests for this!
1622 // emulate read_pages() using read()
1624 status_t error = B_OK;
1625 size_t bytesTransferred = 0;
1627 size_t remainingBytes = *_numBytes;
1628 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1629 size_t toRead = min_c(vecs[i].iov_len, remainingBytes);
1630 size_t length = toRead;
1632 error = vnode->stream.u.dev.device->Read(cookie->device_cookie, pos,
1633 vecs[i].iov_base, &length);
1634 if (error != B_OK)
1635 break;
1637 pos += length;
1638 bytesTransferred += length;
1639 remainingBytes -= length;
1641 if (length < toRead)
1642 break;
1645 *_numBytes = bytesTransferred;
1647 return bytesTransferred > 0 ? B_OK : error;
1651 static status_t
1652 devfs_write_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1653 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1655 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1656 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1658 //TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1660 if (!S_ISCHR(vnode->stream.type)
1661 || (!vnode->stream.u.dev.device->HasWrite()
1662 && !vnode->stream.u.dev.device->HasIO())
1663 || cookie == NULL)
1664 return B_NOT_ALLOWED;
1666 if (pos < 0)
1667 return B_BAD_VALUE;
1669 if (vnode->stream.u.dev.partition != NULL) {
1670 if (pos >= vnode->stream.u.dev.partition->info.size)
1671 return B_BAD_VALUE;
1673 translate_partition_access(vnode->stream.u.dev.partition, pos,
1674 *_numBytes);
1677 if (vnode->stream.u.dev.device->HasIO()) {
1678 // TODO: use io_requests for this!
1681 // emulate write_pages() using write()
1683 status_t error = B_OK;
1684 size_t bytesTransferred = 0;
1686 size_t remainingBytes = *_numBytes;
1687 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1688 size_t toWrite = min_c(vecs[i].iov_len, remainingBytes);
1689 size_t length = toWrite;
1691 error = vnode->stream.u.dev.device->Write(cookie->device_cookie, pos,
1692 vecs[i].iov_base, &length);
1693 if (error != B_OK)
1694 break;
1696 pos += length;
1697 bytesTransferred += length;
1698 remainingBytes -= length;
1700 if (length < toWrite)
1701 break;
1704 *_numBytes = bytesTransferred;
1706 return bytesTransferred > 0 ? B_OK : error;
1710 static status_t
1711 devfs_io(fs_volume* volume, fs_vnode* _vnode, void* _cookie,
1712 io_request* request)
1714 TRACE(("[%ld] devfs_io(request: %p)\n", find_thread(NULL), request));
1716 devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1717 devfs_cookie* cookie = (devfs_cookie*)_cookie;
1719 bool isWrite = request->IsWrite();
1721 if (!S_ISCHR(vnode->stream.type)
1722 || (((isWrite && !vnode->stream.u.dev.device->HasWrite())
1723 || (!isWrite && !vnode->stream.u.dev.device->HasRead()))
1724 && !vnode->stream.u.dev.device->HasIO())
1725 || cookie == NULL) {
1726 request->SetStatusAndNotify(B_NOT_ALLOWED);
1727 return B_NOT_ALLOWED;
1730 if (vnode->stream.u.dev.partition != NULL) {
1731 if (request->Offset() + (off_t)request->Length()
1732 > vnode->stream.u.dev.partition->info.size) {
1733 request->SetStatusAndNotify(B_BAD_VALUE);
1734 return B_BAD_VALUE;
1736 translate_partition_access(vnode->stream.u.dev.partition, request);
1739 if (vnode->stream.u.dev.device->HasIO())
1740 return vnode->stream.u.dev.device->IO(cookie->device_cookie, request);
1742 synchronous_io_cookie synchronousCookie = {
1743 vnode->stream.u.dev.device,
1744 cookie->device_cookie
1747 return vfs_synchronous_io(request,
1748 request->IsWrite() ? &device_write : &device_read, &synchronousCookie);
1752 static status_t
1753 devfs_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* stat)
1755 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1757 TRACE(("devfs_read_stat: vnode %p (%Ld), stat %p\n", vnode, vnode->id,
1758 stat));
1760 stat->st_ino = vnode->id;
1761 stat->st_size = 0;
1762 stat->st_mode = vnode->stream.type;
1764 stat->st_nlink = 1;
1765 stat->st_blksize = 65536;
1766 stat->st_blocks = 0;
1768 stat->st_uid = vnode->uid;
1769 stat->st_gid = vnode->gid;
1771 stat->st_atim = current_timespec();
1772 stat->st_mtim = stat->st_ctim = vnode->modification_time;
1773 stat->st_crtim = vnode->creation_time;
1775 // TODO: this only works for partitions right now - if we should decide
1776 // to keep this feature, we should have a better solution
1777 if (S_ISCHR(vnode->stream.type)) {
1778 //device_geometry geometry;
1780 // if it's a real block device, then let's report a useful size
1781 if (vnode->stream.u.dev.partition != NULL) {
1782 stat->st_size = vnode->stream.u.dev.partition->info.size;
1783 #if 0
1784 } else if (vnode->stream.u.dev.info->control(cookie->device_cookie,
1785 B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) {
1786 stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count
1787 * geometry.sectors_per_track * geometry.bytes_per_sector;
1788 #endif
1791 // is this a real block device? then let's have it reported like that
1792 if (stat->st_size != 0)
1793 stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK);
1794 } else if (S_ISLNK(vnode->stream.type)) {
1795 stat->st_size = vnode->stream.u.symlink.length;
1798 return B_OK;
1802 static status_t
1803 devfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat,
1804 uint32 statMask)
1806 struct devfs* fs = (struct devfs*)_volume->private_volume;
1807 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1809 TRACE(("devfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id,
1810 stat));
1812 // we cannot change the size of anything
1813 if (statMask & B_STAT_SIZE)
1814 return B_BAD_VALUE;
1816 RecursiveLocker locker(&fs->lock);
1818 if (statMask & B_STAT_MODE) {
1819 vnode->stream.type = (vnode->stream.type & ~S_IUMSK)
1820 | (stat->st_mode & S_IUMSK);
1823 if (statMask & B_STAT_UID)
1824 vnode->uid = stat->st_uid;
1825 if (statMask & B_STAT_GID)
1826 vnode->gid = stat->st_gid;
1828 if (statMask & B_STAT_MODIFICATION_TIME)
1829 vnode->modification_time = stat->st_mtim;
1830 if (statMask & B_STAT_CREATION_TIME)
1831 vnode->creation_time = stat->st_crtim;
1833 notify_stat_changed(fs->id, vnode->id, statMask);
1834 return B_OK;
1838 static status_t
1839 devfs_std_ops(int32 op, ...)
1841 switch (op) {
1842 case B_MODULE_INIT:
1843 add_debugger_command_etc("devfs_node", &dump_node,
1844 "Print info on a private devfs node",
1845 "<address>\n"
1846 "Prints information on a devfs node given by <address>.\n",
1848 add_debugger_command_etc("devfs_cookie", &dump_cookie,
1849 "Print info on a private devfs cookie",
1850 "<address>\n"
1851 "Prints information on a devfs cookie given by <address>.\n",
1854 legacy_driver_init();
1855 return B_OK;
1857 case B_MODULE_UNINIT:
1858 remove_debugger_command("devfs_node", &dump_node);
1859 remove_debugger_command("devfs_cookie", &dump_cookie);
1860 return B_OK;
1862 default:
1863 return B_ERROR;
1867 namespace {
1869 fs_volume_ops kVolumeOps = {
1870 &devfs_unmount,
1871 NULL,
1872 NULL,
1873 &devfs_sync,
1874 &devfs_get_vnode,
1876 // the other operations are not supported (attributes, indices, queries)
1877 NULL,
1880 fs_vnode_ops kVnodeOps = {
1881 &devfs_lookup,
1882 &devfs_get_vnode_name,
1884 &devfs_put_vnode,
1885 &devfs_remove_vnode,
1887 &devfs_can_page,
1888 &devfs_read_pages,
1889 &devfs_write_pages,
1891 &devfs_io,
1892 NULL, // cancel_io()
1894 NULL, // get_file_map
1896 /* common */
1897 &devfs_ioctl,
1898 &devfs_set_flags,
1899 &devfs_select,
1900 &devfs_deselect,
1901 &devfs_fsync,
1903 &devfs_read_link,
1904 NULL, // symlink
1905 NULL, // link
1906 NULL, // unlink
1907 NULL, // rename
1909 NULL, // access
1910 &devfs_read_stat,
1911 &devfs_write_stat,
1912 NULL,
1914 /* file */
1915 NULL, // create
1916 &devfs_open,
1917 &devfs_close,
1918 &devfs_free_cookie,
1919 &devfs_read,
1920 &devfs_write,
1922 /* directory */
1923 &devfs_create_dir,
1924 NULL, // remove_dir
1925 &devfs_open_dir,
1926 &devfs_close,
1927 // same as for files - it does nothing for directories, anyway
1928 &devfs_free_dir_cookie,
1929 &devfs_read_dir,
1930 &devfs_rewind_dir,
1932 // attributes operations are not supported
1933 NULL,
1936 } // namespace
1938 file_system_module_info gDeviceFileSystem = {
1940 "file_systems/devfs" B_CURRENT_FS_API_VERSION,
1942 devfs_std_ops,
1945 "devfs", // short_name
1946 "Device File System", // pretty_name
1947 0, // DDM flags
1949 NULL, // identify_partition()
1950 NULL, // scan_partition()
1951 NULL, // free_identify_partition_cookie()
1952 NULL, // free_partition_content_cookie()
1954 &devfs_mount,
1958 // #pragma mark - kernel private API
1961 extern "C" status_t
1962 devfs_unpublish_file_device(const char* path)
1964 // get the device node
1965 devfs_vnode* node;
1966 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
1967 if (status != B_OK)
1968 return status;
1970 if (!S_ISCHR(node->stream.type)) {
1971 put_vnode(sDeviceFileSystem->volume, node->id);
1972 return B_BAD_VALUE;
1975 // if it is indeed a file device, unpublish it
1976 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device);
1977 if (device == NULL) {
1978 put_vnode(sDeviceFileSystem->volume, node->id);
1979 return B_BAD_VALUE;
1982 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
1984 put_vnode(sDeviceFileSystem->volume, node->id);
1985 return status;
1989 extern "C" status_t
1990 devfs_publish_file_device(const char* path, const char* filePath)
1992 // create a FileDevice for the file
1993 FileDevice* device = new(std::nothrow) FileDevice;
1994 if (device == NULL)
1995 return B_NO_MEMORY;
1996 ObjectDeleter<FileDevice> deviceDeleter(device);
1998 status_t error = device->Init(filePath);
1999 if (error != B_OK)
2000 return error;
2002 // publish the device
2003 error = publish_device(sDeviceFileSystem, path, device);
2004 if (error != B_OK)
2005 return error;
2007 deviceDeleter.Detach();
2008 return B_OK;
2012 extern "C" status_t
2013 devfs_unpublish_partition(const char* path)
2015 devfs_vnode* node;
2016 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2017 if (status != B_OK)
2018 return status;
2020 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2021 put_vnode(sDeviceFileSystem->volume, node->id);
2022 return status;
2026 extern "C" status_t
2027 devfs_publish_partition(const char* name, const partition_info* info)
2029 if (name == NULL || info == NULL)
2030 return B_BAD_VALUE;
2031 TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n",
2032 name, info->device, info->offset, info->size));
2034 devfs_vnode* device;
2035 status_t status = get_node_for_path(sDeviceFileSystem, info->device,
2036 &device);
2037 if (status != B_OK)
2038 return status;
2040 status = add_partition(sDeviceFileSystem, device, name, *info);
2042 put_vnode(sDeviceFileSystem->volume, device->id);
2043 return status;
2047 extern "C" status_t
2048 devfs_rename_partition(const char* devicePath, const char* oldName,
2049 const char* newName)
2051 if (oldName == NULL || newName == NULL)
2052 return B_BAD_VALUE;
2054 devfs_vnode* device;
2055 status_t status = get_node_for_path(sDeviceFileSystem, devicePath, &device);
2056 if (status != B_OK)
2057 return status;
2059 RecursiveLocker locker(sDeviceFileSystem->lock);
2060 devfs_vnode* node = devfs_find_in_dir(device->parent, oldName);
2061 if (node == NULL)
2062 return B_ENTRY_NOT_FOUND;
2064 // check if the new path already exists
2065 if (devfs_find_in_dir(device->parent, newName))
2066 return B_BAD_VALUE;
2068 char* name = strdup(newName);
2069 if (name == NULL)
2070 return B_NO_MEMORY;
2072 devfs_remove_from_dir(device->parent, node, false);
2074 free(node->name);
2075 node->name = name;
2077 devfs_insert_in_dir(device->parent, node, false);
2079 notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName,
2080 device->parent->id, newName, node->id);
2081 notify_stat_changed(sDeviceFileSystem->id, device->parent->id,
2082 B_STAT_MODIFICATION_TIME);
2084 return B_OK;
2088 extern "C" status_t
2089 devfs_publish_directory(const char* path)
2091 RecursiveLocker locker(&sDeviceFileSystem->lock);
2093 return publish_directory(sDeviceFileSystem, path);
2097 extern "C" status_t
2098 devfs_unpublish_device(const char* path, bool disconnect)
2100 devfs_vnode* node;
2101 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2102 if (status != B_OK)
2103 return status;
2105 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2107 if (status == B_OK && disconnect)
2108 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2110 put_vnode(sDeviceFileSystem->volume, node->id);
2111 return status;
2115 // #pragma mark - device_manager private API
2118 status_t
2119 devfs_publish_device(const char* path, BaseDevice* device)
2121 return publish_device(sDeviceFileSystem, path, device);
2125 status_t
2126 devfs_unpublish_device(BaseDevice* device, bool disconnect)
2128 devfs_vnode* node;
2129 status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(),
2130 (void**)&node);
2131 if (status != B_OK)
2132 return status;
2134 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2136 if (status == B_OK && disconnect)
2137 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2139 put_vnode(sDeviceFileSystem->volume, node->id);
2140 return status;
2144 /*! Gets the device for a given devfs relative path.
2145 If successful the call must be balanced with a call to devfs_put_device().
2147 status_t
2148 devfs_get_device(const char* path, BaseDevice*& _device)
2150 devfs_vnode* node;
2151 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2152 if (status != B_OK)
2153 return status;
2155 if (!S_ISCHR(node->stream.type) || node->stream.u.dev.partition != NULL) {
2156 put_vnode(sDeviceFileSystem->volume, node->id);
2157 return B_BAD_VALUE;
2160 _device = node->stream.u.dev.device;
2161 return B_OK;
2165 void
2166 devfs_put_device(BaseDevice* device)
2168 put_vnode(sDeviceFileSystem->volume, device->ID());
2172 void
2173 devfs_compute_geometry_size(device_geometry* geometry, uint64 blockCount,
2174 uint32 blockSize)
2176 if (blockCount > UINT32_MAX)
2177 geometry->head_count = (blockCount + UINT32_MAX - 1) / UINT32_MAX;
2178 else
2179 geometry->head_count = 1;
2181 geometry->cylinder_count = 1;
2182 geometry->sectors_per_track = blockCount / geometry->head_count;
2183 geometry->bytes_per_sector = blockSize;
2187 // #pragma mark - support API for legacy drivers
2190 extern "C" status_t
2191 devfs_rescan_driver(const char* driverName)
2193 TRACE(("devfs_rescan_driver: %s\n", driverName));
2195 return legacy_driver_rescan(driverName);
2199 extern "C" status_t
2200 devfs_publish_device(const char* path, device_hooks* hooks)
2202 return legacy_driver_publish(path, hooks);