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