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.
19 #include <KernelExport.h>
20 #include <NodeMonitor.h>
23 #include <AutoDeleter.h>
24 #include <boot/kernel_args.h>
25 #include <boot_device.h>
28 #include <FindDirectory.h>
31 #include <fs/node_monitor.h>
32 #include <kdevice_manager.h>
34 #include <Notifications.h>
35 #include <util/AutoLock.h>
39 #include "BaseDevice.h"
40 #include "FileDevice.h"
41 #include "IORequest.h"
42 #include "legacy_drivers.h"
47 # define TRACE(x) dprintf x
53 struct devfs_partition
{
54 struct devfs_vnode
* raw_device
;
70 struct devfs_vnode
* dir_head
;
77 struct devfs_partition
* partition
;
79 struct stream_symlink
{
87 struct devfs_vnode
* all_next
;
90 timespec modification_time
;
91 timespec creation_time
;
94 struct devfs_vnode
* parent
;
95 struct devfs_vnode
* dir_next
;
96 struct devfs_stream stream
;
99 #define DEVFS_HASH_SIZE 16
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
;
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
{
148 struct synchronous_io_cookie
{
153 // directory iteration states
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
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
,
172 static status_t
unpublish_node(struct devfs
* fs
, devfs_vnode
* node
,
174 static status_t
publish_device(struct devfs
* fs
, const char* path
,
178 // The one and only allowed devfs instance
179 static struct devfs
* sDeviceFileSystem
= NULL
;
182 // #pragma mark - devfs private
188 bigtime_t time
= real_time_clock_usecs();
191 tv
.tv_sec
= time
/ 1000000;
192 tv
.tv_nsec
= (time
% 1000000) * 1000;
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
;
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())
219 if (path
.InitCheck() != B_OK
)
222 get_device_name(dir
, path
.LockBuffer(), path
.BufferSize());
225 TRACE(("scan_for_drivers_if_needed: mode %ld: %s\n", scan_mode(),
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();
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
));
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
) {
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
276 devfs_delete_vnode(struct devfs
* fs
, struct devfs_vnode
* vnode
,
279 // Can't delete it if it's in a directory or is a directory
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();
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
);
310 /*! Makes sure none of the dircookies point to the vnode passed in */
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
))
332 if (!strcmp(path
, "."))
334 if (!strcmp(path
, ".."))
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));
349 devfs_insert_in_dir(struct devfs_vnode
* dir
, struct devfs_vnode
* vnode
,
352 if (!S_ISDIR(dir
->stream
.type
))
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) {
361 node
= node
->dir_next
;
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
;
368 // insert after that node
369 vnode
->dir_next
= last
->dir_next
;
370 last
->dir_next
= vnode
;
374 dir
->modification_time
= current_timespec();
377 notify_entry_created(sDeviceFileSystem
->id
, dir
->id
, vnode
->name
,
379 notify_stat_changed(sDeviceFileSystem
->id
, dir
->id
,
380 B_STAT_MODIFICATION_TIME
);
387 devfs_remove_from_dir(struct devfs_vnode
* dir
, struct devfs_vnode
* removeNode
,
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
);
399 lastNode
->dir_next
= vnode
->dir_next
;
401 dir
->stream
.u
.dir
.dir_head
= vnode
->dir_next
;
402 vnode
->dir_next
= NULL
;
403 dir
->modification_time
= current_timespec();
406 notify_entry_removed(sDeviceFileSystem
->id
, dir
->id
, vnode
->name
,
408 notify_stat_changed(sDeviceFileSystem
->id
, dir
->id
,
409 B_STAT_MODIFICATION_TIME
);
414 return B_ENTRY_NOT_FOUND
;
419 add_partition(struct devfs
* fs
, struct devfs_vnode
* device
, const char* name
,
420 const partition_info
& info
)
422 struct devfs_vnode
* partitionNode
;
425 if (!S_ISCHR(device
->stream
.type
))
428 // we don't support nested partitions
429 if (device
->stream
.u
.dev
.partition
!= NULL
)
432 // reduce checks to a minimum - things like negative offsets could be useful
437 struct devfs_partition
* partition
= (struct devfs_partition
*)malloc(
438 sizeof(struct devfs_partition
));
439 if (partition
== NULL
)
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
;
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
);
458 // now create the partition vnode
459 partitionNode
= devfs_create_vnode(fs
, device
->parent
, name
);
460 if (partitionNode
== NULL
) {
461 status
= B_NO_MEMORY
;
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
));
477 put_vnode(fs
->volume
, device
->id
);
485 translate_partition_access(devfs_partition
* partition
, off_t
& offset
,
489 ASSERT(offset
< partition
->info
.size
);
491 size
= (size_t)min_c((off_t
)size
, partition
->info
.size
- offset
);
492 offset
+= partition
->info
.offset
;
497 translate_partition_access(devfs_partition
* partition
, io_request
* request
)
499 off_t offset
= request
->Offset();
502 ASSERT(offset
+ (off_t
)request
->Length() <= partition
->info
.size
);
504 request
->SetOffset(offset
+ partition
->info
.offset
);
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,
518 unpublish_node(struct devfs
* fs
, devfs_vnode
* node
, mode_t type
)
520 if ((node
->stream
.type
& S_IFMT
) != type
)
523 recursive_lock_lock(&fs
->lock
);
525 status_t status
= devfs_remove_from_dir(node
->parent
, node
);
529 status
= remove_vnode(fs
->volume
, node
->id
);
532 recursive_lock_unlock(&fs
->lock
);
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
);
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
)
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;
567 if (temp
[i
] == '/') {
570 } else if (temp
[i
] != '\0') {
575 //TRACE(("\tpath component '%s'\n", &temp[last]));
577 // we have a path component
578 vnode
= devfs_find_in_dir(dir
, &temp
[last
]);
580 if (S_ISDIR(vnode
->stream
.type
)) {
586 // we hit something on our path that's not a directory
587 status
= B_FILE_EXISTS
;
590 vnode
= devfs_create_vnode(fs
, dir
, &temp
[last
]);
592 status
= B_NO_MEMORY
;
597 // set up the new directory
598 init_directory_vnode(vnode
, 0755);
599 publish_node(sDeviceFileSystem
, dir
, vnode
);
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
)
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;
633 if (temp
[i
] == '\0') {
634 atLeaf
= true; // we'll be done after this one
635 } else if (temp
[i
] == '/') {
643 //TRACE(("\tpath component '%s'\n", &temp[last]));
645 // we have a path component
646 vnode
= devfs_find_in_dir(dir
, &temp
[last
]);
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
)) {
657 // we are at the leaf and hit another node
658 // or we aren't but hit a non-dir node.
660 status
= B_FILE_EXISTS
;
663 vnode
= devfs_create_vnode(fs
, dir
, &temp
[last
]);
665 status
= B_NO_MEMORY
;
670 // set up the new vnode
673 init_directory_vnode(vnode
, 0755);
674 publish_node(fs
, dir
, vnode
);
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.
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");
706 if (device
== NULL
|| path
== NULL
|| path
[0] == '\0' || path
[0] == '/')
709 // TODO: this has to be done in the BaseDevice sub classes!
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
))
719 struct devfs_vnode
* node
;
720 struct devfs_vnode
* dirNode
;
723 RecursiveLocker
locker(&fs
->lock
);
725 status
= new_node(fs
, path
, &node
, &dirNode
);
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
);
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).
745 get_device_name(struct devfs_vnode
* vnode
, char* buffer
, size_t size
)
747 RecursiveLocker
_(sDeviceFileSystem
->lock
);
749 struct devfs_vnode
* leaf
= vnode
;
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
);
768 buffer
[offset
- 1] = '/';
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
);
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
);
793 dump_node(int argc
, char** argv
)
796 print_debugger_command_usage(argv
[0]);
800 struct devfs_vnode
* vnode
= (struct devfs_vnode
*)parse_expression(argv
[1]);
802 kprintf("invalid node address\n");
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
);
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
);
847 dump_cookie(int argc
, char** argv
)
850 print_debugger_command_usage(argv
[0]);
855 if (!evaluate_debug_expression(argv
[1], &address
, false))
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
);
867 // #pragma mark - file system interface
871 devfs_mount(fs_volume
* volume
, const char* devfs
, uint32 flags
,
872 const char* args
, ino_t
* _rootNodeID
)
874 struct devfs_vnode
* vnode
;
878 TRACE(("devfs_mount: entry\n"));
880 if (sDeviceFileSystem
) {
881 TRACE(("double mount of devfs attempted\n"));
886 fs
= (struct devfs
*)malloc(sizeof(struct devfs
));
892 volume
->private_volume
= fs
;
893 volume
->ops
= &kVolumeOps
;
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
) {
907 vnode
= devfs_create_vnode(fs
, NULL
, "");
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
;
928 delete fs
->vnode_hash
;
930 recursive_lock_destroy(&fs
->lock
);
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()) {
954 devfs_delete_vnode(fs
, vnode
, true);
956 delete fs
->vnode_hash
;
958 recursive_lock_destroy(&fs
->lock
);
966 devfs_sync(fs_volume
* _volume
)
968 TRACE(("devfs_sync: entry\n"));
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
;
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
);
993 vnode
= devfs_find_in_dir(dir
, name
);
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
);
1011 devfs_get_vnode_name(fs_volume
* _volume
, fs_vnode
* _vnode
, char* buffer
,
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
);
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
);
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
;
1048 devfs_put_vnode(fs_volume
* _volume
, fs_vnode
* _vnode
, bool reenter
)
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
));
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);
1083 devfs_open(fs_volume
* _volume
, fs_vnode
* _vnode
, int openMode
,
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
));
1094 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode
, openMode
,
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
) {
1107 char path
[B_FILE_NAME_LENGTH
];
1108 get_device_name(vnode
, path
, sizeof(path
));
1110 status
= device
->Open(path
, openMode
, &cookie
->device_cookie
);
1112 device
->UninitDevice();
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
);
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();
1161 devfs_fsync(fs_volume
* _volume
, fs_vnode
* _v
)
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
))
1176 if (link
->stream
.u
.symlink
.length
< *_bufferSize
)
1177 *_bufferSize
= link
->stream
.u
.symlink
.length
;
1179 memcpy(buffer
, link
->stream
.u
.symlink
.path
, *_bufferSize
);
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
))
1200 if (vnode
->stream
.u
.dev
.partition
!= NULL
) {
1201 if (pos
>= vnode
->stream
.u
.dev
.partition
->info
.size
)
1204 translate_partition_access(vnode
->stream
.u
.dev
.partition
, pos
, *_length
);
1210 // pass the call through to the device
1211 return vnode
->stream
.u
.dev
.device
->Read(cookie
->device_cookie
, pos
, buffer
,
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
))
1232 if (vnode
->stream
.u
.dev
.partition
!= NULL
) {
1233 if (pos
>= vnode
->stream
.u
.dev
.partition
->info
.size
)
1236 translate_partition_access(vnode
->stream
.u
.dev
.partition
, pos
, *_length
);
1242 return vnode
->stream
.u
.dev
.device
->Write(cookie
->device_cookie
, pos
, buffer
,
1248 devfs_create_dir(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
,
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
) {
1259 vnode
= devfs_create_vnode(fs
, dir
, name
);
1260 if (vnode
== NULL
) {
1264 // set up the new directory
1265 init_directory_vnode(vnode
, perms
);
1266 publish_node(sDeviceFileSystem
, dir
, vnode
);
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
))
1284 cookie
= (devfs_dir_cookie
*)malloc(sizeof(devfs_dir_cookie
));
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
);
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
);
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
))
1339 RecursiveLocker
locker(&fs
->lock
);
1341 switch (cookie
->state
) {
1342 case ITERATION_STATE_DOT
:
1345 nextChildNode
= vnode
->stream
.u
.dir
.dir_head
;
1346 nextState
= cookie
->state
+ 1;
1348 case ITERATION_STATE_DOT_DOT
:
1349 childNode
= vnode
->parent
;
1351 nextChildNode
= vnode
->stream
.u
.dir
.dir_head
;
1352 nextState
= cookie
->state
+ 1;
1355 childNode
= cookie
->current
;
1357 name
= childNode
->name
;
1358 nextChildNode
= childNode
->dir_next
;
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
)
1375 status
= user_strlcpy(dirent
->d_name
, name
,
1376 bufferSize
- sizeof(struct dirent
));
1380 cookie
->current
= nextChildNode
;
1381 cookie
->state
= nextState
;
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
))
1400 RecursiveLocker
locker(&fs
->lock
);
1402 cookie
->current
= vnode
->stream
.u
.dir
.dir_head
;
1403 cookie
->state
= ITERATION_STATE_BEGIN
;
1409 /*! Forwards the opcode to the device driver, but also handles some devfs
1410 specific functionality, like partitions.
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
)) {
1426 case B_GET_GEOMETRY
:
1428 struct devfs_partition
* partition
1429 = vnode
->stream
.u
.dev
.partition
;
1430 if (partition
== NULL
)
1433 device_geometry geometry
;
1434 status_t status
= vnode
->stream
.u
.dev
.device
->Control(
1435 cookie
->device_cookie
, op
, &geometry
, length
);
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
:
1454 if (!vnode
->stream
.u
.dev
.driver
)
1455 return B_ENTRY_NOT_FOUND
;
1456 path
= vnode
->stream
.u
.dev
.driver
->path
;
1458 return B_ENTRY_NOT_FOUND
;
1460 return user_strlcpy((char*)buffer
, path
, B_FILE_NAME_LENGTH
);
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
))
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
:
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
)))
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
);
1517 devfs_set_flags(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
,
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);
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
,
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())
1566 return vnode
->stream
.u
.dev
.device
->Deselect(cookie
->device_cookie
, event
,
1572 devfs_can_page(fs_volume
* _volume
, fs_vnode
* _vnode
, void* cookie
)
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
1584 return vnode
->stream
.u
.dev
.device
->HasRead()
1585 || vnode
->stream
.u
.dev
.device
->HasIO();
1587 // TODO: Obsolete hook!
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())
1605 return B_NOT_ALLOWED
;
1610 if (vnode
->stream
.u
.dev
.partition
!= NULL
) {
1611 if (pos
>= vnode
->stream
.u
.dev
.partition
->info
.size
)
1614 translate_partition_access(vnode
->stream
.u
.dev
.partition
, pos
,
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
);
1638 bytesTransferred
+= length
;
1639 remainingBytes
-= length
;
1641 if (length
< toRead
)
1645 *_numBytes
= bytesTransferred
;
1647 return bytesTransferred
> 0 ? B_OK
: error
;
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())
1664 return B_NOT_ALLOWED
;
1669 if (vnode
->stream
.u
.dev
.partition
!= NULL
) {
1670 if (pos
>= vnode
->stream
.u
.dev
.partition
->info
.size
)
1673 translate_partition_access(vnode
->stream
.u
.dev
.partition
, pos
,
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
);
1697 bytesTransferred
+= length
;
1698 remainingBytes
-= length
;
1700 if (length
< toWrite
)
1704 *_numBytes
= bytesTransferred
;
1706 return bytesTransferred
> 0 ? B_OK
: error
;
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
);
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
);
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
,
1760 stat
->st_ino
= vnode
->id
;
1762 stat
->st_mode
= vnode
->stream
.type
;
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
;
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
;
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
;
1803 devfs_write_stat(fs_volume
* _volume
, fs_vnode
* _vnode
, const struct stat
* stat
,
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
,
1812 // we cannot change the size of anything
1813 if (statMask
& B_STAT_SIZE
)
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
);
1839 devfs_std_ops(int32 op
, ...)
1843 add_debugger_command_etc("devfs_node", &dump_node
,
1844 "Print info on a private devfs node",
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",
1851 "Prints information on a devfs cookie given by <address>.\n",
1854 legacy_driver_init();
1857 case B_MODULE_UNINIT
:
1858 remove_debugger_command("devfs_node", &dump_node
);
1859 remove_debugger_command("devfs_cookie", &dump_cookie
);
1869 fs_volume_ops kVolumeOps
= {
1876 // the other operations are not supported (attributes, indices, queries)
1880 fs_vnode_ops kVnodeOps
= {
1882 &devfs_get_vnode_name
,
1885 &devfs_remove_vnode
,
1892 NULL
, // cancel_io()
1894 NULL
, // get_file_map
1927 // same as for files - it does nothing for directories, anyway
1928 &devfs_free_dir_cookie
,
1932 // attributes operations are not supported
1938 file_system_module_info gDeviceFileSystem
= {
1940 "file_systems/devfs" B_CURRENT_FS_API_VERSION
,
1945 "devfs", // short_name
1946 "Device File System", // pretty_name
1949 NULL
, // identify_partition()
1950 NULL
, // scan_partition()
1951 NULL
, // free_identify_partition_cookie()
1952 NULL
, // free_partition_content_cookie()
1958 // #pragma mark - kernel private API
1962 devfs_unpublish_file_device(const char* path
)
1964 // get the device node
1966 status_t status
= get_node_for_path(sDeviceFileSystem
, path
, &node
);
1970 if (!S_ISCHR(node
->stream
.type
)) {
1971 put_vnode(sDeviceFileSystem
->volume
, node
->id
);
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
);
1982 status
= unpublish_node(sDeviceFileSystem
, node
, S_IFCHR
);
1984 put_vnode(sDeviceFileSystem
->volume
, node
->id
);
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
;
1996 ObjectDeleter
<FileDevice
> deviceDeleter(device
);
1998 status_t error
= device
->Init(filePath
);
2002 // publish the device
2003 error
= publish_device(sDeviceFileSystem
, path
, device
);
2007 deviceDeleter
.Detach();
2013 devfs_unpublish_partition(const char* path
)
2016 status_t status
= get_node_for_path(sDeviceFileSystem
, path
, &node
);
2020 status
= unpublish_node(sDeviceFileSystem
, node
, S_IFCHR
);
2021 put_vnode(sDeviceFileSystem
->volume
, node
->id
);
2027 devfs_publish_partition(const char* name
, const partition_info
* info
)
2029 if (name
== NULL
|| info
== NULL
)
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
,
2040 status
= add_partition(sDeviceFileSystem
, device
, name
, *info
);
2042 put_vnode(sDeviceFileSystem
->volume
, device
->id
);
2048 devfs_rename_partition(const char* devicePath
, const char* oldName
,
2049 const char* newName
)
2051 if (oldName
== NULL
|| newName
== NULL
)
2054 devfs_vnode
* device
;
2055 status_t status
= get_node_for_path(sDeviceFileSystem
, devicePath
, &device
);
2059 RecursiveLocker
locker(sDeviceFileSystem
->lock
);
2060 devfs_vnode
* node
= devfs_find_in_dir(device
->parent
, oldName
);
2062 return B_ENTRY_NOT_FOUND
;
2064 // check if the new path already exists
2065 if (devfs_find_in_dir(device
->parent
, newName
))
2068 char* name
= strdup(newName
);
2072 devfs_remove_from_dir(device
->parent
, node
, false);
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
);
2089 devfs_publish_directory(const char* path
)
2091 RecursiveLocker
locker(&sDeviceFileSystem
->lock
);
2093 return publish_directory(sDeviceFileSystem
, path
);
2098 devfs_unpublish_device(const char* path
, bool disconnect
)
2101 status_t status
= get_node_for_path(sDeviceFileSystem
, path
, &node
);
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
);
2115 // #pragma mark - device_manager private API
2119 devfs_publish_device(const char* path
, BaseDevice
* device
)
2121 return publish_device(sDeviceFileSystem
, path
, device
);
2126 devfs_unpublish_device(BaseDevice
* device
, bool disconnect
)
2129 status_t status
= get_vnode(sDeviceFileSystem
->volume
, device
->ID(),
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
);
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().
2148 devfs_get_device(const char* path
, BaseDevice
*& _device
)
2151 status_t status
= get_node_for_path(sDeviceFileSystem
, path
, &node
);
2155 if (!S_ISCHR(node
->stream
.type
) || node
->stream
.u
.dev
.partition
!= NULL
) {
2156 put_vnode(sDeviceFileSystem
->volume
, node
->id
);
2160 _device
= node
->stream
.u
.dev
.device
;
2166 devfs_put_device(BaseDevice
* device
)
2168 put_vnode(sDeviceFileSystem
->volume
, device
->ID());
2173 devfs_compute_geometry_size(device_geometry
* geometry
, uint64 blockCount
,
2176 if (blockCount
> UINT32_MAX
)
2177 geometry
->head_count
= (blockCount
+ UINT32_MAX
- 1) / UINT32_MAX
;
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
2191 devfs_rescan_driver(const char* driverName
)
2193 TRACE(("devfs_rescan_driver: %s\n", driverName
));
2195 return legacy_driver_rescan(driverName
);
2200 devfs_publish_device(const char* path
, device_hooks
* hooks
)
2202 return legacy_driver_publish(path
, hooks
);