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