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