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