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