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