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