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