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