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