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