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