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