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