1/*
2 * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9
10#if FS_SHELL
11#	include "fssh_api_wrapper.h"
12
13#	include "hash.h"
14#	include "list.h"
15#else
16#	include <stdio.h>
17#	include <stdlib.h>
18#	include <string.h>
19#	include <sys/stat.h>
20
21#	include <fs_cache.h>
22#	include <KernelExport.h>
23#	include <NodeMonitor.h>
24
25#	include <debug.h>
26#	include <khash.h>
27#	include <lock.h>
28#	include <util/AutoLock.h>
29#	include <vfs.h>
30#	include <vm/vm.h>
31#endif
32
33
34#if FS_SHELL
35	using namespace FSShell;
36#	define user_strlcpy(to, from, len)	(strlcpy(to, from, len), FSSH_B_OK)
37#endif
38
39
40//#define TRACE_ROOTFS
41#ifdef TRACE_ROOTFS
42#	define TRACE(x) dprintf x
43#else
44#	define TRACE(x)
45#endif
46
47
48struct rootfs_stream {
49	mode_t						type;
50	struct stream_dir {
51		struct rootfs_vnode*	dir_head;
52		struct list				cookies;
53		mutex					cookie_lock;
54	} dir;
55	struct stream_symlink {
56		char*					path;
57		size_t					length;
58	} symlink;
59};
60
61struct rootfs_vnode {
62	struct rootfs_vnode*		all_next;
63	ino_t						id;
64	char*						name;
65	timespec					modification_time;
66	timespec					creation_time;
67	uid_t						uid;
68	gid_t						gid;
69	struct rootfs_vnode*		parent;
70	struct rootfs_vnode*		dir_next;
71	struct rootfs_stream		stream;
72};
73
74struct rootfs {
75	fs_volume*					volume;
76	dev_t						id;
77	rw_lock						lock;
78	ino_t						next_vnode_id;
79	hash_table*					vnode_list_hash;
80	struct rootfs_vnode*		root_vnode;
81};
82
83// dircookie, dirs are only types of streams supported by rootfs
84struct rootfs_dir_cookie {
85	struct list_link			link;
86	mutex						lock;
87	struct rootfs_vnode*		current;
88	int32						iteration_state;
89};
90
91// directory iteration states
92enum {
93	ITERATION_STATE_DOT		= 0,
94	ITERATION_STATE_DOT_DOT	= 1,
95	ITERATION_STATE_OTHERS	= 2,
96	ITERATION_STATE_BEGIN	= ITERATION_STATE_DOT,
97};
98
99// extern and in a private namespace only to make forward declaration possible
100namespace {
101	extern fs_volume_ops sVolumeOps;
102	extern fs_vnode_ops sVnodeOps;
103}
104
105#define ROOTFS_HASH_SIZE 16
106
107
108static timespec
109current_timespec()
110{
111	bigtime_t time = real_time_clock_usecs();
112
113	timespec tv;
114	tv.tv_sec = time / 1000000;
115	tv.tv_nsec = (time % 1000000) * 1000;
116	return tv;
117}
118
119
120static uint32
121rootfs_vnode_hash_func(void* _v, const void* _key, uint32 range)
122{
123	struct rootfs_vnode* vnode = (rootfs_vnode*)_v;
124	const ino_t* key = (const ino_t*)_key;
125
126	if (vnode != NULL)
127		return vnode->id % range;
128
129	return (uint64)*key % range;
130}
131
132
133static int
134rootfs_vnode_compare_func(void* _v, const void* _key)
135{
136	struct rootfs_vnode* v = (rootfs_vnode*)_v;
137	const ino_t* key = (const ino_t*)_key;
138
139	if (v->id == *key)
140		return 0;
141
142	return -1;
143}
144
145
146static struct rootfs_vnode*
147rootfs_create_vnode(struct rootfs* fs, struct rootfs_vnode* parent,
148	const char* name, int type)
149{
150	struct rootfs_vnode* vnode;
151
152	vnode = (rootfs_vnode*)malloc(sizeof(struct rootfs_vnode));
153	if (vnode == NULL)
154		return NULL;
155
156	memset(vnode, 0, sizeof(struct rootfs_vnode));
157
158	if (name != NULL) {
159		vnode->name = strdup(name);
160		if (vnode->name == NULL) {
161			free(vnode);
162			return NULL;
163		}
164	}
165
166	vnode->id = fs->next_vnode_id++;
167	vnode->stream.type = type;
168	vnode->creation_time = vnode->modification_time = current_timespec();
169	vnode->uid = geteuid();
170	vnode->gid = parent ? parent->gid : getegid();
171		// inherit group from parent if possible
172
173	if (S_ISDIR(type)) {
174		list_init(&vnode->stream.dir.cookies);
175		mutex_init(&vnode->stream.dir.cookie_lock, "rootfs dir cookies");
176	}
177
178	return vnode;
179}
180
181
182static status_t
183rootfs_delete_vnode(struct rootfs* fs, struct rootfs_vnode* v, bool force_delete)
184{
185	// cant delete it if it's in a directory or is a directory
186	// and has children
187	if (!force_delete && (v->stream.dir.dir_head != NULL || v->dir_next != NULL))
188		return EPERM;
189
190	// remove it from the global hash table
191	hash_remove(fs->vnode_list_hash, v);
192
193	if (S_ISDIR(v->stream.type))
194		mutex_destroy(&v->stream.dir.cookie_lock);
195
196	free(v->name);
197	free(v);
198
199	return 0;
200}
201
202
203/*! Makes sure none of the dircookies point to the vnode passed in. */
204static void
205update_dir_cookies(struct rootfs_vnode* dir, struct rootfs_vnode* vnode)
206{
207	struct rootfs_dir_cookie* cookie = NULL;
208
209	while ((cookie = (rootfs_dir_cookie*)list_get_next_item(
210			&dir->stream.dir.cookies, cookie)) != NULL) {
211		MutexLocker cookieLocker(cookie->lock);
212		if (cookie->current == vnode)
213			cookie->current = vnode->dir_next;
214	}
215}
216
217
218static struct rootfs_vnode*
219rootfs_find_in_dir(struct rootfs_vnode* dir, const char* path)
220{
221	struct rootfs_vnode* vnode;
222
223	if (!strcmp(path, "."))
224		return dir;
225	if (!strcmp(path, ".."))
226		return dir->parent;
227
228	for (vnode = dir->stream.dir.dir_head; vnode; vnode = vnode->dir_next) {
229		if (!strcmp(vnode->name, path))
230			return vnode;
231	}
232	return NULL;
233}
234
235
236static status_t
237rootfs_insert_in_dir(struct rootfs* fs, struct rootfs_vnode* dir,
238	struct rootfs_vnode* vnode)
239{
240	// make sure the directory stays sorted alphabetically
241
242	struct rootfs_vnode* node = dir->stream.dir.dir_head;
243	struct rootfs_vnode* last = NULL;
244	while (node != NULL && strcmp(node->name, vnode->name) < 0) {
245		last = node;
246		node = node->dir_next;
247	}
248	if (last == NULL) {
249		// the new vnode is the first entry in the list
250		vnode->dir_next = dir->stream.dir.dir_head;
251		dir->stream.dir.dir_head = vnode;
252	} else {
253		// insert after that node
254		vnode->dir_next = last->dir_next;
255		last->dir_next = vnode;
256	}
257
258	vnode->parent = dir;
259	dir->modification_time = current_timespec();
260
261	notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME);
262	return B_OK;
263}
264
265
266static status_t
267rootfs_remove_from_dir(struct rootfs* fs, struct rootfs_vnode* dir,
268	struct rootfs_vnode* removeVnode)
269{
270	struct rootfs_vnode* vnode;
271	struct rootfs_vnode* lastVnode;
272
273	for (vnode = dir->stream.dir.dir_head, lastVnode = NULL; vnode != NULL;
274			lastVnode = vnode, vnode = vnode->dir_next) {
275		if (vnode == removeVnode) {
276			// make sure all dircookies dont point to this vnode
277			update_dir_cookies(dir, vnode);
278
279			if (lastVnode)
280				lastVnode->dir_next = vnode->dir_next;
281			else
282				dir->stream.dir.dir_head = vnode->dir_next;
283			vnode->dir_next = NULL;
284
285			dir->modification_time = current_timespec();
286			notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME);
287			return B_OK;
288		}
289	}
290	return B_ENTRY_NOT_FOUND;
291}
292
293
294static bool
295rootfs_is_dir_empty(struct rootfs_vnode* dir)
296{
297	return !dir->stream.dir.dir_head;
298}
299
300
301/*! You must hold the FS write lock when calling this function */
302static status_t
303remove_node(struct rootfs* fs, struct rootfs_vnode* directory,
304	struct rootfs_vnode* vnode)
305{
306	// schedule this vnode to be removed when it's ref goes to zero
307
308	bool gotNode = (get_vnode(fs->volume, vnode->id, NULL) == B_OK);
309
310	status_t status = B_OK;
311	if (gotNode)
312		status = remove_vnode(fs->volume, vnode->id);
313
314	if (status == B_OK) {
315		rootfs_remove_from_dir(fs, directory, vnode);
316		notify_entry_removed(fs->id, directory->id, vnode->name, vnode->id);
317	}
318
319	if (gotNode)
320		put_vnode(fs->volume, vnode->id);
321
322	return status;
323}
324
325
326static status_t
327rootfs_remove(struct rootfs* fs, struct rootfs_vnode* dir, const char* name,
328	bool isDirectory)
329{
330	struct rootfs_vnode* vnode;
331	status_t status = B_OK;
332
333	WriteLocker locker(fs->lock);
334
335	vnode = rootfs_find_in_dir(dir, name);
336	if (!vnode)
337		status = B_ENTRY_NOT_FOUND;
338	else if (isDirectory && !S_ISDIR(vnode->stream.type))
339		status = B_NOT_A_DIRECTORY;
340	else if (!isDirectory && S_ISDIR(vnode->stream.type))
341		status = B_IS_A_DIRECTORY;
342	else if (isDirectory && !rootfs_is_dir_empty(vnode))
343		status = B_DIRECTORY_NOT_EMPTY;
344
345	if (status != B_OK)
346		return status;
347
348	entry_cache_remove(fs->volume->id, dir->id, name);
349
350	return remove_node(fs, dir, vnode);
351}
352
353
354//	#pragma mark -
355
356
357static status_t
358rootfs_mount(fs_volume* volume, const char* device, uint32 flags,
359	const char* args, ino_t* _rootID)
360{
361	struct rootfs* fs;
362	struct rootfs_vnode* vnode;
363	status_t err;
364
365	TRACE(("rootfs_mount: entry\n"));
366
367	fs = (rootfs*)malloc(sizeof(struct rootfs));
368	if (fs == NULL)
369		return B_NO_MEMORY;
370
371	volume->private_volume = fs;
372	volume->ops = &sVolumeOps;
373	fs->volume = volume;
374	fs->id = volume->id;
375	fs->next_vnode_id = 1;
376
377	rw_lock_init(&fs->lock, "rootfs");
378
379	fs->vnode_list_hash = hash_init(ROOTFS_HASH_SIZE,
380		(addr_t)&vnode->all_next - (addr_t)vnode, &rootfs_vnode_compare_func,
381		&rootfs_vnode_hash_func);
382	if (fs->vnode_list_hash == NULL) {
383		err = B_NO_MEMORY;
384		goto err2;
385	}
386
387	// create the root vnode
388	vnode = rootfs_create_vnode(fs, NULL, ".", S_IFDIR | 0777);
389	if (vnode == NULL) {
390		err = B_NO_MEMORY;
391		goto err3;
392	}
393	vnode->parent = vnode;
394
395	fs->root_vnode = vnode;
396	hash_insert(fs->vnode_list_hash, vnode);
397	publish_vnode(volume, vnode->id, vnode, &sVnodeOps, vnode->stream.type, 0);
398
399	*_rootID = vnode->id;
400
401	return B_OK;
402
403err3:
404	hash_uninit(fs->vnode_list_hash);
405err2:
406	rw_lock_destroy(&fs->lock);
407	free(fs);
408
409	return err;
410}
411
412
413static status_t
414rootfs_unmount(fs_volume* _volume)
415{
416	struct rootfs* fs = (struct rootfs*)_volume->private_volume;
417
418	TRACE(("rootfs_unmount: entry fs = %p\n", fs));
419
420	// release the reference to the root
421	put_vnode(fs->volume, fs->root_vnode->id);
422
423	// delete all of the vnodes
424	struct hash_iterator i;
425	hash_open(fs->vnode_list_hash, &i);
426
427	while (struct rootfs_vnode* vnode = (struct rootfs_vnode*)
428			hash_next(fs->vnode_list_hash, &i)) {
429		rootfs_delete_vnode(fs, vnode, true);
430	}
431
432	hash_close(fs->vnode_list_hash, &i, false);
433
434	hash_uninit(fs->vnode_list_hash);
435	rw_lock_destroy(&fs->lock);
436	free(fs);
437
438	return B_OK;
439}
440
441
442static status_t
443rootfs_sync(fs_volume* _volume)
444{
445	TRACE(("rootfs_sync: entry\n"));
446
447	return B_OK;
448}
449
450
451static status_t
452rootfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
453{
454	struct rootfs* fs = (struct rootfs*)_volume->private_volume;
455	struct rootfs_vnode* dir = (struct rootfs_vnode*)_dir->private_node;
456	struct rootfs_vnode* vnode;
457
458	TRACE(("rootfs_lookup: entry dir %p, name '%s'\n", dir, name));
459	if (!S_ISDIR(dir->stream.type))
460		return B_NOT_A_DIRECTORY;
461
462	ReadLocker locker(fs->lock);
463
464	// look it up
465	vnode = rootfs_find_in_dir(dir, name);
466	if (!vnode)
467		return B_ENTRY_NOT_FOUND;
468
469	status_t status = get_vnode(fs->volume, vnode->id, NULL);
470	if (status != B_OK)
471		return status;
472
473	entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
474
475	*_id = vnode->id;
476	return B_OK;
477}
478
479
480static status_t
481rootfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer,
482	size_t bufferSize)
483{
484	struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node;
485
486	TRACE(("rootfs_get_vnode_name: vnode = %p (name = %s)\n", vnode,
487		vnode->name));
488
489	strlcpy(buffer, vnode->name, bufferSize);
490	return B_OK;
491}
492
493
494static status_t
495rootfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type,
496	uint32* _flags, bool reenter)
497{
498	struct rootfs* fs = (struct rootfs*)_volume->private_volume;
499	struct rootfs_vnode* vnode;
500
501	TRACE(("rootfs_getvnode: asking for vnode %Ld, r %d\n", id, reenter));
502
503	if (!reenter)
504		rw_lock_read_lock(&fs->lock);
505
506	vnode = (rootfs_vnode*)hash_lookup(fs->vnode_list_hash, &id);
507
508	if (!reenter)
509		rw_lock_read_unlock(&fs->lock);
510
511	TRACE(("rootfs_getnvnode: looked it up at %p\n", vnode));
512
513	if (vnode == NULL)
514		return B_ENTRY_NOT_FOUND;
515
516	_vnode->private_node = vnode;
517	_vnode->ops = &sVnodeOps;
518	*_type = vnode->stream.type;
519	*_flags = 0;
520
521	return B_OK;
522}
523
524
525static status_t
526rootfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
527{
528#ifdef TRACE_ROOTFS
529	struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node;
530
531	TRACE(("rootfs_putvnode: entry on vnode 0x%Lx, r %d\n", vnode->id, reenter));
532#endif
533	return B_OK; // whatever
534}
535
536
537static status_t
538rootfs_remove_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
539{
540	struct rootfs* fs = (struct rootfs*)_volume->private_volume;
541	struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node;
542
543	TRACE(("rootfs_remove_vnode: remove %p (0x%Lx), r %d\n", vnode, vnode->id,
544		reenter));
545
546	if (!reenter)
547		rw_lock_write_lock(&fs->lock);
548
549	if (vnode->dir_next) {
550		// can't remove node if it's linked to the dir
551		panic("rootfs_remove_vnode: vnode %p asked to be removed is present in "
552			"dir\n", vnode);
553	}
554
555	rootfs_delete_vnode(fs, vnode, false);
556
557	if (!reenter)
558		rw_lock_write_unlock(&fs->lock);
559
560	return B_OK;
561}
562
563
564static status_t
565rootfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int omode,
566	int perms, void** _cookie, ino_t* _newID)
567{
568	return B_BAD_VALUE;
569}
570
571
572static status_t
573rootfs_open(fs_volume* _volume, fs_vnode* _v, int oflags, void** _cookie)
574{
575	// allow to open the file, but it can't be done anything with it
576
577	*_cookie = NULL;
578	return B_OK;
579}
580
581
582static status_t
583rootfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
584{
585	TRACE(("rootfs_close: entry vnode %p, cookie %p\n", _vnode->private_node,
586		_cookie));
587	return B_OK;
588}
589
590
591static status_t
592rootfs_free_cookie(fs_volume* _volume, fs_vnode* _v, void* _cookie)
593{
594	return B_OK;
595}
596
597
598static status_t
599rootfs_fsync(fs_volume* _volume, fs_vnode* _v)
600{
601	return B_OK;
602}
603
604
605static status_t
606rootfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
607	off_t pos, void* buffer, size_t* _length)
608{
609	return EINVAL;
610}
611
612
613static status_t
614rootfs_write(fs_volume* _volume, fs_vnode* vnode, void* cookie,
615	off_t pos, const void* buffer, size_t* _length)
616{
617	TRACE(("rootfs_write: vnode %p, cookie %p, pos 0x%Lx , len %#x\n",
618		vnode, cookie, pos, (int)*_length));
619
620	return EPERM;
621}
622
623
624static status_t
625rootfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name,
626	int mode)
627{
628	struct rootfs* fs = (rootfs*)_volume->private_volume;
629	struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node;
630	struct rootfs_vnode* vnode;
631
632	TRACE(("rootfs_create_dir: dir %p, name = '%s', perms = %d\n", dir, name,
633		mode));
634
635	WriteLocker locker(fs->lock);
636
637	vnode = rootfs_find_in_dir(dir, name);
638	if (vnode != NULL)
639		return B_FILE_EXISTS;
640
641	TRACE(("rootfs_create: creating new vnode\n"));
642	vnode = rootfs_create_vnode(fs, dir, name, S_IFDIR | (mode & S_IUMSK));
643	if (vnode == NULL)
644		return B_NO_MEMORY;
645
646	rootfs_insert_in_dir(fs, dir, vnode);
647	hash_insert(fs->vnode_list_hash, vnode);
648
649	entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
650	notify_entry_created(fs->id, dir->id, name, vnode->id);
651
652	return B_OK;
653}
654
655
656static status_t
657rootfs_remove_dir(fs_volume* _volume, fs_vnode* _dir, const char* name)
658{
659	struct rootfs* fs = (rootfs*)_volume->private_volume;
660	struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node;
661
662	TRACE(("rootfs_remove_dir: dir %p (0x%Lx), name '%s'\n", dir, dir->id,
663		name));
664
665	return rootfs_remove(fs, dir, name, true);
666}
667
668
669static status_t
670rootfs_open_dir(fs_volume* _volume, fs_vnode* _v, void** _cookie)
671{
672	struct rootfs* fs = (struct rootfs*)_volume->private_volume;
673	struct rootfs_vnode* vnode = (struct rootfs_vnode*)_v->private_node;
674	struct rootfs_dir_cookie* cookie;
675
676	TRACE(("rootfs_open: vnode %p\n", vnode));
677
678	if (!S_ISDIR(vnode->stream.type))
679		return B_BAD_VALUE;
680
681	cookie = (rootfs_dir_cookie*)malloc(sizeof(struct rootfs_dir_cookie));
682	if (cookie == NULL)
683		return B_NO_MEMORY;
684
685	mutex_init(&cookie->lock, "rootfs dir cookie");
686
687	ReadLocker locker(fs->lock);
688
689	cookie->current = vnode->stream.dir.dir_head;
690	cookie->iteration_state = ITERATION_STATE_BEGIN;
691
692	mutex_lock(&vnode->stream.dir.cookie_lock);
693	list_add_item(&vnode->stream.dir.cookies, cookie);
694	mutex_unlock(&vnode->stream.dir.cookie_lock);
695
696	*_cookie = cookie;
697
698	return B_OK;
699}
700
701
702static status_t
703rootfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
704{
705	struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie;
706	struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node;
707	struct rootfs* fs = (rootfs*)_volume->private_volume;
708
709	ReadLocker locker(fs->lock);
710
711	mutex_lock(&vnode->stream.dir.cookie_lock);
712	list_remove_item(&vnode->stream.dir.cookies, cookie);
713	mutex_unlock(&vnode->stream.dir.cookie_lock);
714
715	locker.Unlock();
716
717	mutex_destroy(&cookie->lock);
718
719	free(cookie);
720	return B_OK;
721}
722
723
724static status_t
725rootfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
726	struct dirent* dirent, size_t bufferSize, uint32* _num)
727{
728	struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node;
729	struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie;
730	struct rootfs* fs = (rootfs*)_volume->private_volume;
731	struct rootfs_vnode* childNode = NULL;
732	const char* name = NULL;
733	struct rootfs_vnode* nextChildNode = NULL;
734
735	TRACE(("rootfs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %d, "
736		"num = %p\n", _vnode, cookie, dirent, (int)bufferSize, _num));
737
738	ReadLocker locker(fs->lock);
739
740	MutexLocker cookieLocker(cookie->lock);
741	int nextState = cookie->iteration_state;
742
743	switch (cookie->iteration_state) {
744		case ITERATION_STATE_DOT:
745			childNode = vnode;
746			name = ".";
747			nextChildNode = vnode->stream.dir.dir_head;
748			nextState = cookie->iteration_state + 1;
749			break;
750		case ITERATION_STATE_DOT_DOT:
751			childNode = vnode->parent;
752			name = "..";
753			nextChildNode = vnode->stream.dir.dir_head;
754			nextState = cookie->iteration_state + 1;
755			break;
756		default:
757			childNode = cookie->current;
758			if (childNode) {
759				name = childNode->name;
760				nextChildNode = childNode->dir_next;
761			}
762			break;
763	}
764
765	if (!childNode) {
766		// we're at the end of the directory
767		*_num = 0;
768		return B_OK;
769	}
770
771	dirent->d_dev = fs->id;
772	dirent->d_ino = childNode->id;
773	dirent->d_reclen = strlen(name) + sizeof(struct dirent);
774
775	if (dirent->d_reclen > bufferSize)
776		return ENOBUFS;
777
778	int nameLength = user_strlcpy(dirent->d_name, name,
779		bufferSize - sizeof(struct dirent));
780	if (nameLength < B_OK)
781		return nameLength;
782
783	cookie->current = nextChildNode;
784	cookie->iteration_state = nextState;
785	*_num = 1;
786	return B_OK;
787}
788
789
790static status_t
791rootfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
792{
793	struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie;
794	struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node;
795	struct rootfs* fs = (rootfs*)_volume->private_volume;
796
797	ReadLocker locker(fs->lock);
798	MutexLocker cookieLocker(cookie->lock);
799
800	cookie->current = vnode->stream.dir.dir_head;
801	cookie->iteration_state = ITERATION_STATE_BEGIN;
802
803	return B_OK;
804}
805
806
807static status_t
808rootfs_ioctl(fs_volume* _volume, fs_vnode* _v, void* _cookie, uint32 op,
809	void* buffer, size_t length)
810{
811	TRACE(("rootfs_ioctl: vnode %p, cookie %p, op %d, buf %p, length %d\n",
812		_volume, _cookie, (int)op, buffer, (int)length));
813
814	return B_BAD_VALUE;
815}
816
817
818static bool
819rootfs_can_page(fs_volume* _volume, fs_vnode* _v, void* cookie)
820{
821	return false;
822}
823
824
825static status_t
826rootfs_read_pages(fs_volume* _volume, fs_vnode* _v, void* cookie, off_t pos,
827	const iovec* vecs, size_t count, size_t* _numBytes)
828{
829	return B_NOT_ALLOWED;
830}
831
832
833static status_t
834rootfs_write_pages(fs_volume* _volume, fs_vnode* _v, void* cookie, off_t pos,
835	const iovec* vecs, size_t count, size_t* _numBytes)
836{
837	return B_NOT_ALLOWED;
838}
839
840
841static status_t
842rootfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer,
843	size_t* _bufferSize)
844{
845	struct rootfs_vnode* link = (rootfs_vnode*)_link->private_node;
846
847	if (!S_ISLNK(link->stream.type))
848		return B_BAD_VALUE;
849
850	if (link->stream.symlink.length < *_bufferSize)
851		*_bufferSize = link->stream.symlink.length;
852
853	memcpy(buffer, link->stream.symlink.path, *_bufferSize);
854	return B_OK;
855}
856
857
858static status_t
859rootfs_symlink(fs_volume* _volume, fs_vnode* _dir, const char* name,
860	const char* path, int mode)
861{
862	struct rootfs* fs = (rootfs*)_volume->private_volume;
863	struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node;
864	struct rootfs_vnode* vnode;
865
866	TRACE(("rootfs_symlink: dir %p, name = '%s', path = %s\n", dir, name, path));
867
868	WriteLocker locker(fs->lock);
869
870	vnode = rootfs_find_in_dir(dir, name);
871	if (vnode != NULL)
872		return B_FILE_EXISTS;
873
874	TRACE(("rootfs_create: creating new symlink\n"));
875	vnode = rootfs_create_vnode(fs, dir, name, S_IFLNK | (mode & S_IUMSK));
876	if (vnode == NULL)
877		return B_NO_MEMORY;
878
879	rootfs_insert_in_dir(fs, dir, vnode);
880	hash_insert(fs->vnode_list_hash, vnode);
881
882	vnode->stream.symlink.path = strdup(path);
883	if (vnode->stream.symlink.path == NULL) {
884		rootfs_delete_vnode(fs, vnode, false);
885		return B_NO_MEMORY;
886	}
887	vnode->stream.symlink.length = strlen(path);
888
889	entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
890
891	notify_entry_created(fs->id, dir->id, name, vnode->id);
892
893	return B_OK;
894}
895
896
897static status_t
898rootfs_unlink(fs_volume* _volume, fs_vnode* _dir, const char* name)
899{
900	struct rootfs* fs = (rootfs*)_volume->private_volume;
901	struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node;
902
903	TRACE(("rootfs_unlink: dir %p (0x%Lx), name '%s'\n", dir, dir->id, name));
904
905	return rootfs_remove(fs, dir, name, false);
906}
907
908
909static status_t
910rootfs_rename(fs_volume* _volume, fs_vnode* _fromDir, const char* fromName,
911	fs_vnode* _toDir, const char* toName)
912{
913	struct rootfs* fs = (rootfs*)_volume->private_volume;
914	struct rootfs_vnode* fromDirectory = (rootfs_vnode*)_fromDir->private_node;
915	struct rootfs_vnode* toDirectory = (rootfs_vnode*)_toDir->private_node;
916
917	TRACE(("rootfs_rename: from %p (0x%Lx, %s), fromName '%s', to %p "
918		"(0x%Lx, %s), toName '%s'\n", fromDirectory, fromDirectory->id,
919		fromDirectory->name != NULL ? fromDirectory->name : "NULL",
920		fromName, toDirectory, toDirectory->id,
921		toDirectory->name != NULL ? toDirectory->name : "NULL",
922		toName));
923
924	// Prevent renaming /boot, since that will stop everything from working.
925	// TODO: This should be solved differently. Either root should still be
926	// able to do this or a mechanism should be introduced that does this
927	// at the VFS level, for example by checking nodes for a specific
928	// attribute.
929	if (fromDirectory->id == 1 && strcmp(fromName, "boot") == 0)
930		return EPERM;
931
932	WriteLocker locker(fs->lock);
933
934	struct rootfs_vnode* vnode = rootfs_find_in_dir(fromDirectory, fromName);
935	if (vnode == NULL)
936		return B_ENTRY_NOT_FOUND;
937
938	// make sure the target is not a subdirectory of us
939	struct rootfs_vnode* parent = toDirectory->parent;
940	while (parent != NULL && parent != parent->parent) {
941		if (parent == vnode)
942			return B_BAD_VALUE;
943
944		parent = parent->parent;
945	}
946
947	struct rootfs_vnode* targetVnode = rootfs_find_in_dir(toDirectory, toName);
948	if (targetVnode != NULL) {
949		// target node exists, let's see if it is an empty directory
950		if (S_ISDIR(targetVnode->stream.type)
951			&& !rootfs_is_dir_empty(targetVnode))
952			return B_NAME_IN_USE;
953
954		// so we can cleanly remove it
955		entry_cache_remove(fs->volume->id, toDirectory->id, toName);
956		remove_node(fs, toDirectory, targetVnode);
957	}
958
959	// we try to reuse the existing name buffer if possible
960	if (strlen(fromName) >= strlen(toName)) {
961		char* nameBuffer = strdup(toName);
962		if (nameBuffer == NULL)
963			return B_NO_MEMORY;
964
965		free(vnode->name);
966		vnode->name = nameBuffer;
967	} else {
968		// we can just copy it
969		strcpy(vnode->name, toName);
970	}
971
972	// remove it from the dir
973	entry_cache_remove(fs->volume->id, fromDirectory->id, fromName);
974	rootfs_remove_from_dir(fs, fromDirectory, vnode);
975
976	// Add it back to the dir with the new name.
977	// We need to do this even in the same directory,
978	// so that it keeps sorted correctly.
979	rootfs_insert_in_dir(fs, toDirectory, vnode);
980
981	entry_cache_add(fs->volume->id, toDirectory->id, toName, vnode->id);
982
983	notify_entry_moved(fs->id, fromDirectory->id, fromName, toDirectory->id,
984		toName, vnode->id);
985
986	return B_OK;
987}
988
989
990static status_t
991rootfs_read_stat(fs_volume* _volume, fs_vnode* _v, struct stat* stat)
992{
993	struct rootfs* fs = (rootfs*)_volume->private_volume;
994	struct rootfs_vnode* vnode = (rootfs_vnode*)_v->private_node;
995
996	TRACE(("rootfs_read_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id,
997		stat));
998
999	// stream exists, but we know to return size 0, since we can only hold
1000	// directories
1001	stat->st_dev = fs->id;
1002	stat->st_ino = vnode->id;
1003	if (S_ISLNK(vnode->stream.type))
1004		stat->st_size = vnode->stream.symlink.length;
1005	else
1006		stat->st_size = 0;
1007	stat->st_mode = vnode->stream.type;
1008
1009	stat->st_nlink = 1;
1010	stat->st_blksize = 65536;
1011	stat->st_blocks = 0;
1012
1013	stat->st_uid = vnode->uid;
1014	stat->st_gid = vnode->gid;
1015
1016	stat->st_atim.tv_sec = real_time_clock();
1017	stat->st_atim.tv_nsec = 0;
1018	stat->st_mtim = stat->st_ctim = vnode->modification_time;
1019	stat->st_crtim = vnode->creation_time;
1020
1021	return B_OK;
1022}
1023
1024
1025static status_t
1026rootfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat,
1027	uint32 statMask)
1028{
1029	struct rootfs* fs = (rootfs*)_volume->private_volume;
1030	struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node;
1031
1032	TRACE(("rootfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id,
1033		stat));
1034
1035	// we cannot change the size of anything
1036	if (statMask & B_STAT_SIZE)
1037		return B_BAD_VALUE;
1038
1039	WriteLocker locker(fs->lock);
1040
1041	if ((statMask & B_STAT_MODE) != 0) {
1042		vnode->stream.type = (vnode->stream.type & ~S_IUMSK)
1043			| (stat->st_mode & S_IUMSK);
1044	}
1045
1046	if ((statMask & B_STAT_UID) != 0)
1047		vnode->uid = stat->st_uid;
1048	if ((statMask & B_STAT_GID) != 0)
1049		vnode->gid = stat->st_gid;
1050
1051	if ((statMask & B_STAT_MODIFICATION_TIME) != 0)
1052		vnode->modification_time = stat->st_mtim;
1053	if ((statMask & B_STAT_CREATION_TIME) != 0)
1054		vnode->creation_time = stat->st_crtim;
1055
1056	locker.Unlock();
1057
1058	notify_stat_changed(fs->id, vnode->id, statMask);
1059	return B_OK;
1060}
1061
1062
1063static status_t
1064rootfs_create_special_node(fs_volume* _volume, fs_vnode* _dir, const char* name,
1065	fs_vnode* subVnode, mode_t mode, uint32 flags, fs_vnode* _superVnode,
1066	ino_t* _nodeID)
1067{
1068	struct rootfs* fs = (rootfs*)_volume->private_volume;
1069	struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node;
1070	struct rootfs_vnode* vnode;
1071
1072	WriteLocker locker(fs->lock);
1073
1074	if (name != NULL) {
1075		vnode = rootfs_find_in_dir(dir, name);
1076		if (vnode != NULL)
1077			return B_FILE_EXISTS;
1078	}
1079
1080	vnode = rootfs_create_vnode(fs, dir, name, mode);
1081	if (vnode == NULL)
1082		return B_NO_MEMORY;
1083
1084	if (name != NULL)
1085		rootfs_insert_in_dir(fs, dir, vnode);
1086	else
1087		flags |= B_VNODE_PUBLISH_REMOVED;
1088
1089	hash_insert(fs->vnode_list_hash, vnode);
1090
1091	_superVnode->private_node = vnode;
1092	_superVnode->ops = &sVnodeOps;
1093	*_nodeID = vnode->id;
1094
1095	if (subVnode == NULL)
1096		subVnode = _superVnode;
1097
1098	status_t status = publish_vnode(fs->volume, vnode->id,
1099		subVnode->private_node, subVnode->ops, mode, flags);
1100	if (status != B_OK) {
1101		if (name != NULL)
1102			rootfs_remove_from_dir(fs, dir, vnode);
1103		rootfs_delete_vnode(fs, vnode, false);
1104		return status;
1105	}
1106
1107	if (name != NULL) {
1108		entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
1109		notify_entry_created(fs->id, dir->id, name, vnode->id);
1110	}
1111
1112	return B_OK;
1113}
1114
1115
1116static status_t
1117rootfs_std_ops(int32 op, ...)
1118{
1119	switch (op) {
1120		case B_MODULE_INIT:
1121			return B_OK;
1122
1123		case B_MODULE_UNINIT:
1124			return B_OK;
1125
1126		default:
1127			return B_ERROR;
1128	}
1129}
1130
1131
1132namespace {
1133
1134fs_volume_ops sVolumeOps = {
1135	&rootfs_unmount,
1136	NULL,
1137	NULL,
1138	&rootfs_sync,
1139	&rootfs_get_vnode,
1140
1141	// the other operations are not supported (indices, queries)
1142	NULL,
1143};
1144
1145fs_vnode_ops sVnodeOps = {
1146	&rootfs_lookup,
1147	&rootfs_get_vnode_name,
1148
1149	&rootfs_put_vnode,
1150	&rootfs_remove_vnode,
1151
1152	&rootfs_can_page,
1153	&rootfs_read_pages,
1154	&rootfs_write_pages,
1155
1156	NULL,	// io()
1157	NULL,	// cancel_io()
1158
1159	NULL,	// get_file_map()
1160
1161	/* common */
1162	&rootfs_ioctl,
1163	NULL,	// fs_set_flags()
1164	NULL,	// select
1165	NULL,	// deselect
1166	&rootfs_fsync,
1167
1168	&rootfs_read_link,
1169	&rootfs_symlink,
1170	NULL,	// fs_link()
1171	&rootfs_unlink,
1172	&rootfs_rename,
1173
1174	NULL,	// fs_access()
1175	&rootfs_read_stat,
1176	&rootfs_write_stat,
1177	NULL,
1178
1179	/* file */
1180	&rootfs_create,
1181	&rootfs_open,
1182	&rootfs_close,
1183	&rootfs_free_cookie,
1184	&rootfs_read,
1185	&rootfs_write,
1186
1187	/* directory */
1188	&rootfs_create_dir,
1189	&rootfs_remove_dir,
1190	&rootfs_open_dir,
1191	&rootfs_close,			// same as for files - it does nothing, anyway
1192	&rootfs_free_dir_cookie,
1193	&rootfs_read_dir,
1194	&rootfs_rewind_dir,
1195
1196	/* attribute directory operations */
1197	NULL,	// open_attr_dir
1198	NULL,	// close_attr_dir
1199	NULL,	// free_attr_dir_cookie
1200	NULL,	// read_attr_dir
1201	NULL,	// rewind_attr_dir
1202
1203	/* attribute operations */
1204	NULL,	// create_attr
1205	NULL,	// open_attr
1206	NULL,	// close_attr
1207	NULL,	// free_attr_cookie
1208	NULL,	// read_attr
1209	NULL,	// write_attr
1210
1211	NULL,	// read_attr_stat
1212	NULL,	// write_attr_stat
1213	NULL,	// rename_attr
1214	NULL,	// remove_attr
1215
1216	/* support for node and FS layers */
1217	&rootfs_create_special_node,
1218	NULL,	// get_super_vnode,
1219};
1220
1221}	// namespace
1222
1223file_system_module_info gRootFileSystem = {
1224	{
1225		"file_systems/rootfs" B_CURRENT_FS_API_VERSION,
1226		0,
1227		rootfs_std_ops,
1228	},
1229
1230	"rootfs",				// short_name
1231	"Root File System",		// pretty_name
1232	0,						// DDM flags
1233
1234	NULL,	// identify_partition()
1235	NULL,	// scan_partition()
1236	NULL,	// free_identify_partition_cookie()
1237	NULL,	// free_partition_content_cookie()
1238
1239	&rootfs_mount,
1240};
1241