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