xref: /haiku/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 // kernel_interface.cpp
2 //
3 // Copyright (c) 2003-2008, Axel Dörfler (axeld@pinc-software.de)
4 // Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 //
20 // You can alternatively use *this file* under the terms of the the MIT
21 // license included in this package.
22 
23 #include <ctype.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 
34 #include <fs_index.h>
35 #include <fs_info.h>
36 #include <fs_interface.h>
37 #include <fs_query.h>
38 #include <fs_volume.h>
39 #include <KernelExport.h>
40 #include <NodeMonitor.h>
41 #include <TypeConstants.h>
42 
43 #include <AutoDeleter.h>
44 
45 #include "AllocationInfo.h"
46 #include "AttributeIndex.h"
47 #include "AttributeIterator.h"
48 #include "DebugSupport.h"
49 #include "Directory.h"
50 #include "Entry.h"
51 #include "EntryIterator.h"
52 #include "File.h"
53 #include "Index.h"
54 #include "IndexDirectory.h"
55 #include "Locking.h"
56 #include "Misc.h"
57 #include "Node.h"
58 #include "Query.h"
59 #include "ramfs_ioctl.h"
60 #include "SymLink.h"
61 #include "Volume.h"
62 
63 
64 static const size_t kOptimalIOSize = 65536;
65 static const bigtime_t kNotificationInterval = 1000000LL;
66 
67 // notify_if_stat_changed
68 void
69 notify_if_stat_changed(Volume *volume, Node *node)
70 {
71 	if (volume && node && node->IsModified()) {
72 		uint32 statFields = node->MarkUnmodified();
73 		notify_stat_changed(volume->GetID(), -1, node->GetID(), statFields);
74 	}
75 }
76 
77 
78 // #pragma mark - FS
79 
80 
81 // ramfs_mount
82 static status_t
83 ramfs_mount(fs_volume* _volume, const char* /*device*/, uint32 flags,
84 	const char* /*args*/, ino_t* _rootID)
85 {
86 	FUNCTION_START();
87 	// parameters are ignored for now
88 
89 	// fail, if read-only mounting is requested
90 	if (flags & B_MOUNT_READ_ONLY)
91 		return B_BAD_VALUE;
92 
93 	// allocate and init the volume
94 	Volume *volume = new(std::nothrow) Volume(_volume);
95 
96 	if (volume == NULL)
97 		return B_NO_MEMORY;
98 
99 	status_t status = volume->Mount(flags);
100 
101 	if (status != B_OK) {
102 		delete volume;
103 		RETURN_ERROR(status);
104 	}
105 
106 	*_rootID = volume->GetRootDirectory()->GetID();
107 	_volume->private_volume = volume;
108 
109 	RETURN_ERROR(B_OK);
110 }
111 
112 
113 // ramfs_unmount
114 static status_t
115 ramfs_unmount(fs_volume* _volume)
116 {
117 	FUNCTION_START();
118 	Volume* volume = (Volume*)_volume->private_volume;
119 
120 	status_t error = volume->Unmount();
121 	if (error == B_OK)
122 		delete volume;
123 	if (error != B_OK)
124 		REPORT_ERROR(error);
125 	return error;
126 }
127 
128 
129 // ramfs_read_fs_info
130 static status_t
131 ramfs_read_fs_info(fs_volume* _volume, struct fs_info *info)
132 {
133 	FUNCTION_START();
134 	Volume* volume = (Volume*)_volume->private_volume;
135 	status_t error = B_OK;
136 	if (VolumeReadLocker locker = volume) {
137 		info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
138 			| B_FS_HAS_QUERY;
139 		info->block_size = volume->GetBlockSize();
140 		info->io_size = kOptimalIOSize;
141 		info->total_blocks = volume->CountBlocks();
142 		info->free_blocks = volume->CountFreeBlocks();
143 		info->device_name[0] = '\0';
144 		strncpy(info->volume_name, volume->GetName(),
145 			sizeof(info->volume_name));
146 		strcpy(info->fsh_name, "ramfs");
147 	} else
148 		SET_ERROR(error, B_ERROR);
149 	return B_OK;
150 }
151 
152 
153 // ramfs_write_fs_info
154 static status_t
155 ramfs_write_fs_info(fs_volume* _volume, const struct fs_info *info, uint32 mask)
156 
157 {
158 	FUNCTION_START();
159 	Volume* volume = (Volume*)_volume->private_volume;
160 	status_t error = B_OK;
161 	if (VolumeWriteLocker locker = volume) {
162 		if (mask & FS_WRITE_FSINFO_NAME)
163 			error = volume->SetName(info->volume_name);
164 	} else
165 		SET_ERROR(error, B_ERROR);
166 	RETURN_ERROR(error);
167 }
168 
169 
170 // ramfs_sync
171 static status_t
172 ramfs_sync(fs_volume* /*fs*/)
173 {
174 	FUNCTION_START();
175 	return B_OK;
176 }
177 
178 
179 // #pragma mark - VNodes
180 
181 
182 // ramfs_lookup
183 static status_t
184 ramfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* entryName,
185 	ino_t* _vnodeID)
186 {
187 //	FUNCTION_START();
188 	Volume* volume = (Volume*)_volume->private_volume;
189 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir);
190 
191 	FUNCTION(("dir: (%llu), entry: `%s'\n", (dir ? dir->GetID() : -1),
192 		entryName));
193 
194 	// check for non-directories
195 	if (!dir)
196 		RETURN_ERROR(B_NOT_A_DIRECTORY);
197 
198 	status_t error = B_OK;
199 	if (VolumeReadLocker locker = volume) {
200 		Node *node = NULL;
201 
202 		// special entries: "." and ".."
203 		if (!strcmp(entryName, ".")) {
204 			*_vnodeID = dir->GetID();
205 			if (volume->GetVNode(*_vnodeID, &node) != B_OK)
206 				error = B_BAD_VALUE;
207 		} else if (!strcmp(entryName, "..")) {
208 			Directory *parent = dir->GetParent();
209 			if (parent && volume->GetVNode(parent->GetID(), &node) == B_OK)
210 				*_vnodeID = node->GetID();
211 			else
212 				error = B_BAD_VALUE;
213 
214 		// ordinary entries
215 		} else {
216 			// find the entry
217 			error = dir->FindAndGetNode(entryName, &node);
218 SET_ERROR(error, error);
219 			if (error == B_OK)
220 				*_vnodeID = node->GetID();
221 		}
222 
223 	} else
224 		SET_ERROR(error, B_ERROR);
225 	RETURN_ERROR(error);
226 }
227 
228 
229 // ramfs_read_vnode
230 static status_t
231 ramfs_read_vnode(fs_volume* _volume, ino_t vnid, fs_vnode* node, int* _type,
232 	uint32* _flags, bool reenter)
233 {
234 //	FUNCTION_START();
235 	FUNCTION(("node: %Ld\n", vnid));
236 	Volume* volume = (Volume*)_volume->private_volume;
237 	Node *foundNode = NULL;
238 	status_t error = B_OK;
239 	if (VolumeReadLocker locker = volume) {
240 		error = volume->FindNode(vnid, &foundNode);
241 		if (error == B_OK)
242 			node->private_node = foundNode;
243 	} else
244 		SET_ERROR(error, B_ERROR);
245 	RETURN_ERROR(error);
246 }
247 
248 
249 // ramfs_write_vnode
250 static status_t
251 ramfs_write_vnode(fs_volume* /*fs*/, fs_vnode* DARG(_node), bool /*reenter*/)
252 {
253 // DANGER: If dbg_printf() is used, this thread will enter another FS and
254 // even perform a write operation. The is dangerous here, since this hook
255 // may be called out of the other FSs, since, for instance a put_vnode()
256 // called from another FS may cause the VFS layer to free vnodes and thus
257 // invoke this hook.
258 //	FUNCTION_START();
259 //FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID()));
260 	status_t error = B_OK;
261 	RETURN_ERROR(error);
262 }
263 
264 
265 // ramfs_remove_vnode
266 static status_t
267 ramfs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool /*reenter*/)
268 {
269 	FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID()));
270 	Volume* volume = (Volume*)_volume->private_volume;
271 	Node* node = (Node*)_node->private_node;
272 
273 	status_t error = B_OK;
274 	if (VolumeWriteLocker locker = volume) {
275 		volume->NodeRemoved(node);
276 		delete node;
277 	} else
278 		SET_ERROR(error, B_ERROR);
279 	RETURN_ERROR(error);
280 }
281 
282 
283 // #pragma mark - Nodes
284 
285 
286 // ramfs_ioctl
287 static status_t
288 ramfs_ioctl(fs_volume* _volume, fs_vnode* /*node*/, void* /*cookie*/,
289 	uint32 cmd, void *buffer, size_t /*length*/)
290 {
291 	FUNCTION_START();
292 	Volume* volume = (Volume*)_volume->private_volume;
293 
294 	status_t error = B_OK;
295 	switch (cmd) {
296 		case RAMFS_IOCTL_GET_ALLOCATION_INFO:
297 		{
298 			if (buffer) {
299 				if (VolumeReadLocker locker = volume) {
300 					AllocationInfo *info = (AllocationInfo*)buffer;
301 					volume->GetAllocationInfo(*info);
302 				} else
303 					SET_ERROR(error, B_ERROR);
304 			} else
305 				SET_ERROR(error, B_BAD_VALUE);
306 			break;
307 		}
308 		case RAMFS_IOCTL_DUMP_INDEX:
309 		{
310 			if (buffer) {
311 				if (VolumeReadLocker locker = volume) {
312 					const char *name = (const char*)buffer;
313 PRINT(("  RAMFS_IOCTL_DUMP_INDEX, `%s'\n", name));
314 					IndexDirectory *indexDir = volume->GetIndexDirectory();
315 					if (indexDir) {
316 						if (Index *index = indexDir->FindIndex(name))
317 							index->Dump();
318 						else
319 							SET_ERROR(error, B_ENTRY_NOT_FOUND);
320 					} else
321 						SET_ERROR(error, B_ENTRY_NOT_FOUND);
322 				} else
323 					SET_ERROR(error, B_ERROR);
324 			} else
325 				SET_ERROR(error, B_BAD_VALUE);
326 			break;
327 		}
328 		default:
329 			error = B_DEV_INVALID_IOCTL;
330 			break;
331 	}
332 	RETURN_ERROR(error);
333 }
334 
335 
336 // ramfs_set_flags
337 static status_t
338 ramfs_set_flags(fs_volume* /*fs*/, fs_vnode* /*node*/, void* /*cookie*/,
339 	int /*flags*/)
340 {
341 	FUNCTION_START();
342 	// TODO : ramfs_set_flags
343 	return B_OK;
344 }
345 
346 
347 // ramfs_fsync
348 static status_t
349 ramfs_fsync(fs_volume* /*fs*/, fs_vnode* /*node*/)
350 {
351 	FUNCTION_START();
352 	return B_OK;
353 }
354 
355 
356 // ramfs_read_symlink
357 static status_t
358 ramfs_read_symlink(fs_volume* _volume, fs_vnode* _node, char *buffer,
359 	size_t *bufferSize)
360 {
361 	FUNCTION_START();
362 	Volume* volume = (Volume*)_volume->private_volume;
363 	Node* node = (Node*)_node->private_node;
364 
365 	status_t error = B_OK;
366 	if (VolumeReadLocker locker = volume) {
367 		// read symlinks only
368 		if (!node->IsSymLink())
369 			error = B_BAD_VALUE;
370 		if (error == B_OK) {
371 			if (SymLink *symLink = dynamic_cast<SymLink*>(node)) {
372 				// copy the link contents
373 				size_t toRead = min(*bufferSize,
374 									symLink->GetLinkedPathLength());
375 				if (toRead > 0)
376 					memcpy(buffer, symLink->GetLinkedPath(), toRead);
377 				*bufferSize = toRead;
378 			} else {
379 				FATAL("Node %Ld pretends to be a SymLink, but isn't!\n",
380 					node->GetID());
381 				error = B_BAD_VALUE;
382 			}
383 		}
384 	} else
385 		SET_ERROR(error, B_ERROR);
386 	RETURN_ERROR(error);
387 }
388 
389 
390 // ramfs_create_symlink
391 static status_t
392 ramfs_create_symlink(fs_volume* _volume, fs_vnode* _dir, const char *name,
393 	const char *path, int mode)
394 {
395 	FUNCTION(("name: `%s', path: `%s'\n", name, path));
396 	Volume* volume = (Volume*)_volume->private_volume;
397 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
398 
399 	status_t error = B_OK;
400 	// check name
401 	if (!name || *name == '\0') {
402 		SET_ERROR(error, B_BAD_VALUE);
403 	// check directory
404 	} else if (!dir) {
405 		SET_ERROR(error, B_BAD_VALUE);
406 	} else if (VolumeWriteLocker locker = volume) {
407 		NodeMTimeUpdater mTimeUpdater(dir);
408 		// directory deleted?
409 		bool removed;
410 		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
411 			!= B_OK || removed) {
412 			SET_ERROR(error, B_NOT_ALLOWED);
413 		}
414 		// check directory write permissions
415 		error = dir->CheckPermissions(ACCESS_W);
416 		Node *node = NULL;
417 		if (error == B_OK) {
418 			// check if entry does already exist
419 			if (dir->FindNode(name, &node) == B_OK) {
420 				SET_ERROR(error, B_FILE_EXISTS);
421 			} else {
422 				// entry doesn't exist: create a symlink
423 				SymLink *symLink = NULL;
424 				error = dir->CreateSymLink(name, path, &symLink);
425 				if (error == B_OK) {
426 					node = symLink;
427 					// set permissions, owner and group
428 					node->SetMode(mode);
429 					node->SetUID(geteuid());
430 					node->SetGID(getegid());
431 					// put the node
432 					volume->PutVNode(node->GetID());
433 				}
434 			}
435 		}
436 		NodeMTimeUpdater mTimeUpdater2(node);
437 		// notify listeners
438 		if (error == B_OK) {
439 			notify_entry_created(volume->GetID(), dir->GetID(), name,
440 				node->GetID());
441 		}
442 	} else
443 		SET_ERROR(error, B_ERROR);
444 	RETURN_ERROR(error);
445 }
446 
447 
448 // ramfs_link
449 static status_t
450 ramfs_link(fs_volume* _volume, fs_vnode* _dir, const char *name,
451 	fs_vnode* _node)
452 {
453 	FUNCTION(("name: `%s'\n", name));
454 	Volume* volume = (Volume*)_volume->private_volume;
455 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
456 	Node* node = (Node*)_node->private_node;
457 
458 	status_t error = B_OK;
459 	// check directory
460 	if (!dir) {
461 		SET_ERROR(error, B_BAD_VALUE);
462 	} else if (VolumeWriteLocker locker = volume) {
463 		NodeMTimeUpdater mTimeUpdater(dir);
464 		// directory deleted?
465 		bool removed;
466 		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
467 			!= B_OK || removed) {
468 			SET_ERROR(error, B_NOT_ALLOWED);
469 		}
470 		// check directory write permissions
471 		error = dir->CheckPermissions(ACCESS_W);
472 		Entry *entry = NULL;
473 		if (error == B_OK) {
474 			// check if entry does already exist
475 			if (dir->FindEntry(name, &entry) == B_OK) {
476 				SET_ERROR(error, B_FILE_EXISTS);
477 			} else {
478 				// entry doesn't exist: create a link
479 				error = dir->CreateEntry(node, name);
480 			}
481 		}
482 		// notify listeners
483 		if (error == B_OK) {
484 			notify_entry_created(volume->GetID(), dir->GetID(), name,
485 				node->GetID());
486 		}
487 	} else
488 		SET_ERROR(error, B_ERROR);
489 	RETURN_ERROR(error);
490 }
491 
492 
493 // ramfs_unlink
494 static status_t
495 ramfs_unlink(fs_volume* _volume, fs_vnode* _dir, const char *name)
496 {
497 	FUNCTION(("name: `%s'\n", name));
498 	Volume* volume = (Volume*)_volume->private_volume;
499 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
500 	status_t error = B_OK;
501 
502 	// check name
503 	if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) {
504 		SET_ERROR(error, B_BAD_VALUE);
505 	// check node
506 	} else if (!dir) {
507 		SET_ERROR(error, B_BAD_VALUE);
508 	} else if (VolumeWriteLocker locker = volume) {
509 		NodeMTimeUpdater mTimeUpdater(dir);
510 		// check directory write permissions
511 		error = dir->CheckPermissions(ACCESS_W);
512 		ino_t nodeID = -1;
513 		if (error == B_OK) {
514 			// check if entry exists
515 			Node *node = NULL;
516 			Entry *entry = NULL;
517 			if (dir->FindAndGetNode(name, &node, &entry) == B_OK) {
518 				nodeID = node->GetID();
519 				// unlink the entry, if it isn't a non-empty directory
520 				if (node->IsDirectory()
521 					&& !dynamic_cast<Directory*>(node)->IsEmpty()) {
522 					SET_ERROR(error, B_DIRECTORY_NOT_EMPTY);
523 				} else
524 					error = dir->DeleteEntry(entry);
525 				volume->PutVNode(node);
526 			} else
527 				SET_ERROR(error, B_ENTRY_NOT_FOUND);
528 		}
529 		// notify listeners
530 		if (error == B_OK)
531 			notify_entry_removed(volume->GetID(), dir->GetID(), name, nodeID);
532 	} else
533 		SET_ERROR(error, B_ERROR);
534 	RETURN_ERROR(error);
535 }
536 
537 
538 // ramfs_rename
539 static status_t
540 ramfs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char *oldName,
541 	fs_vnode* _newDir, const char *newName)
542 {
543 	Volume* volume = (Volume*)_volume->private_volume;
544 
545 	Directory* oldDir = dynamic_cast<Directory*>((Node*)_oldDir->private_node);
546 	Directory* newDir = dynamic_cast<Directory*>((Node*)_newDir->private_node);
547 	status_t error = B_OK;
548 
549 	if (VolumeWriteLocker locker = volume) {
550 FUNCTION(("old dir: %Ld, old name: `%s', new dir: %Ld, new name: `%s'\n",
551 oldDir->GetID(), oldName, newDir->GetID(), newName));
552 		NodeMTimeUpdater mTimeUpdater1(oldDir);
553 		NodeMTimeUpdater mTimeUpdater2(newDir);
554 
555 		// target directory deleted?
556 		bool removed;
557 		if (get_vnode_removed(volume->FSVolume(), newDir->GetID(), &removed)
558 				!= B_OK || removed) {
559 			SET_ERROR(error, B_NOT_ALLOWED);
560 		}
561 
562 		// check directory write permissions
563 		if (error == B_OK)
564 			error = oldDir->CheckPermissions(ACCESS_W);
565 		if (error == B_OK)
566 			error = newDir->CheckPermissions(ACCESS_W);
567 
568 		Node *node = NULL;
569 		Entry *entry = NULL;
570 		if (error == B_OK) {
571 			// check if entry exists
572 			if (oldDir->FindAndGetNode(oldName, &node, &entry) != B_OK) {
573 				SET_ERROR(error, B_ENTRY_NOT_FOUND);
574 			} else {
575 				if (oldDir != newDir) {
576 					// check whether the entry is a descendent of the target
577 					// directory
578 					for (Directory *parent = newDir;
579 						parent;
580 						parent = parent->GetParent()) {
581 						if (parent == node) {
582 							error = B_BAD_VALUE;
583 							break;
584 						} else if (parent == oldDir)
585 							break;
586 					}
587 				}
588 			}
589 
590 			// check the target directory situation
591 			Node *clobberNode = NULL;
592 			Entry *clobberEntry = NULL;
593 			if (error == B_OK) {
594 				if (newDir->FindAndGetNode(newName, &clobberNode,
595 					&clobberEntry) == B_OK) {
596 					if (clobberNode->IsDirectory()
597 						&& !dynamic_cast<Directory*>(clobberNode)->IsEmpty()) {
598 						SET_ERROR(error, B_NAME_IN_USE);
599 					}
600 				}
601 			}
602 
603 			// do the job
604 			if (error == B_OK) {
605 				// temporarily acquire an additional reference to make
606 				// sure the node isn't deleted when we remove the entry
607 				error = node->AddReference();
608 				if (error == B_OK) {
609 					// delete the original entry
610 					error = oldDir->DeleteEntry(entry);
611 					if (error == B_OK) {
612 						// create the new one/relink the target entry
613 						if (clobberEntry)
614 							error = clobberEntry->Link(node);
615 						else
616 							error = newDir->CreateEntry(node, newName);
617 
618 						if (error == B_OK) {
619 							// send a "removed" notification for the clobbered
620 							// entry
621 							if (clobberEntry) {
622 								notify_entry_removed(volume->GetID(),
623 									newDir->GetID(), newName,
624 									clobberNode->GetID());
625 							}
626 						} else {
627 							// try to recreate the original entry, in case of
628 							// failure
629 							newDir->CreateEntry(node, oldName);
630 						}
631 					}
632 					node->RemoveReference();
633 				}
634 			}
635 
636 			// release the entries
637 			if (clobberEntry)
638 				volume->PutVNode(clobberNode);
639 			if (entry)
640 				volume->PutVNode(node);
641 		}
642 
643 		// notify listeners
644 		if (error == B_OK) {
645 			notify_entry_moved(volume->GetID(), oldDir->GetID(), oldName,
646 				newDir->GetID(), newName, node->GetID());
647 		}
648 	} else
649 		SET_ERROR(error, B_ERROR);
650 
651 	RETURN_ERROR(error);
652 }
653 
654 
655 // ramfs_access
656 static status_t
657 ramfs_access(fs_volume* _volume, fs_vnode* _node, int mode)
658 {
659 	FUNCTION_START();
660 	Volume* volume = (Volume*)_volume->private_volume;
661 	Node* node = (Node*)_node->private_node;
662 
663 	status_t error = B_OK;
664 	if (VolumeReadLocker locker = volume) {
665 		error = node->CheckPermissions(mode);
666 	} else
667 		SET_ERROR(error, B_ERROR);
668 	RETURN_ERROR(error);
669 }
670 
671 
672 // ramfs_read_stat
673 static status_t
674 ramfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat *st)
675 {
676 //	FUNCTION_START();
677 	Volume* volume = (Volume*)_volume->private_volume;
678 	Node* node = (Node*)_node->private_node;
679 
680 	FUNCTION(("node: %Ld\n", node->GetID()));
681 	status_t error = B_OK;
682 	if (VolumeReadLocker locker = volume) {
683 		st->st_dev = volume->GetID();
684 		st->st_ino = node->GetID();
685 		st->st_mode = node->GetMode();
686 		st->st_nlink = node->GetRefCount();
687 		st->st_uid = node->GetUID();
688 		st->st_gid = node->GetGID();
689 		st->st_size = node->GetSize();
690 		st->st_blksize = kOptimalIOSize;
691 		st->st_atime = node->GetATime();
692 		st->st_mtime = node->GetMTime();
693 		st->st_ctime = node->GetCTime();
694 		st->st_crtime = node->GetCrTime();
695 	} else
696 		SET_ERROR(error, B_ERROR);
697 	RETURN_ERROR(error);
698 }
699 
700 
701 // ramfs_write_stat
702 static status_t
703 ramfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat *st,
704 	uint32 mask)
705 {
706 	Volume* volume = (Volume*)_volume->private_volume;
707 	Node* node = (Node*)_node->private_node;
708 
709 	FUNCTION(("mask: %lx\n", mask));
710 	status_t error = B_OK;
711 	if (VolumeWriteLocker locker = volume) {
712 		NodeMTimeUpdater mTimeUpdater(node);
713 		// check permissions
714 		error = node->CheckPermissions(ACCESS_W);
715 		// size
716 		if (error == B_OK && (mask & B_STAT_SIZE))
717 			error = node->SetSize(st->st_size);
718 		if (error == B_OK) {
719 			// permissions
720 			if (mask & B_STAT_MODE) {
721 				node->SetMode((node->GetMode() & ~S_IUMSK)
722 					| (st->st_mode & S_IUMSK));
723 			}
724 			// UID
725 			if (mask & B_STAT_UID)
726 				node->SetUID(st->st_uid);
727 			// GID
728 			if (mask & B_STAT_GID)
729 				node->SetGID(st->st_gid);
730 			// mtime
731 			if (mask & B_STAT_MODIFICATION_TIME)
732 				node->SetMTime(st->st_mtime);
733 			// crtime
734 			if (mask & B_STAT_CREATION_TIME)
735 				node->SetCrTime(st->st_crtime);
736 		}
737 		// notify listeners
738 		if (error == B_OK)
739 			notify_if_stat_changed(volume, node);
740 	} else
741 		SET_ERROR(error, B_ERROR);
742 	RETURN_ERROR(error);
743 }
744 
745 
746 // #pragma mark - Files
747 
748 
749 // FileCookie
750 class FileCookie {
751 public:
752 	FileCookie(int openMode) : fOpenMode(openMode), fLastNotificationTime(0) {}
753 
754 	inline int GetOpenMode() { return fOpenMode; }
755 	inline bigtime_t GetLastNotificationTime() { return fLastNotificationTime; }
756 
757 	inline bool	NotificationIntervalElapsed(bool set = false)
758 	{
759 		bigtime_t currentTime = system_time();
760 		bool result = (currentTime
761 			- fLastNotificationTime > kNotificationInterval);
762 
763 		if (set && result)
764 			fLastNotificationTime = currentTime;
765 
766 		return result;
767 	}
768 
769 private:
770 	int fOpenMode;
771 	bigtime_t fLastNotificationTime;
772 };
773 
774 
775 // ramfs_create
776 static status_t
777 ramfs_create(fs_volume* _volume, fs_vnode* _dir, const char *name, int openMode,
778 	int mode, void** _cookie, ino_t *vnid)
779 {
780 //	FUNCTION_START();
781 	FUNCTION(("name: `%s', open mode: %x, mode: %x\n", name, openMode, mode));
782 	Volume* volume = (Volume*)_volume->private_volume;
783 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
784 
785 	status_t error = B_OK;
786 	// check name
787 	if (!name || *name == '\0') {
788 		SET_ERROR(error, B_BAD_VALUE);
789 	// check directory
790 	} else if (!dir) {
791 		SET_ERROR(error, B_BAD_VALUE);
792 	} else if (VolumeWriteLocker locker = volume) {
793 		NodeMTimeUpdater mTimeUpdater(dir);
794 		// directory deleted?
795 		bool removed;
796 		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
797 			!= B_OK || removed) {
798 			SET_ERROR(error, B_NOT_ALLOWED);
799 		}
800 		// create the file cookie
801 		FileCookie *cookie = NULL;
802 		if (error == B_OK) {
803 			cookie = new(nothrow) FileCookie(openMode);
804 			if (!cookie)
805 				SET_ERROR(error, B_NO_MEMORY);
806 		}
807 		Node *node = NULL;
808 		if (error == B_OK) {
809 			// check if entry does already exist
810 			if (dir->FindNode(name, &node) == B_OK) {
811 				// entry does already exist
812 				// fail, if we shall fail, when the file exists
813 				if (openMode & O_EXCL) {
814 					SET_ERROR(error, B_FILE_EXISTS);
815 				// don't create a file over an existing directory or symlink
816 				} else if (!node->IsFile()) {
817 					SET_ERROR(error, B_NOT_ALLOWED);
818 				// the user must have write permission for an existing entry
819 				} else if ((error = node->CheckPermissions(ACCESS_W)) == B_OK) {
820 					// truncate, if requested
821 					if (openMode & O_TRUNC)
822 						error = node->SetSize(0);
823 					// we ignore the supplied permissions in this case
824 					// get vnode
825 					if (error == B_OK) {
826 						*vnid = node->GetID();
827 						error = volume->GetVNode(node->GetID(), &node);
828 					}
829 				}
830 			// the user must have dir write permission to create a new entry
831 			} else if ((error = dir->CheckPermissions(ACCESS_W)) == B_OK) {
832 				// entry doesn't exist: create a file
833 				File *file = NULL;
834 				error = dir->CreateFile(name, &file);
835 				if (error == B_OK) {
836 					node = file;
837 					*vnid = node->GetID();
838 					// set permissions, owner and group
839 					node->SetMode(mode);
840 					node->SetUID(geteuid());
841 					node->SetGID(getegid());
842 				}
843 			}
844 			// set result / cleanup on failure
845 			if (error == B_OK)
846 				*_cookie = cookie;
847 			else if (cookie)
848 				delete cookie;
849 		}
850 		NodeMTimeUpdater mTimeUpdater2(node);
851 		// notify listeners
852 		if (error == B_OK)
853 			notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid);
854 	} else
855 		SET_ERROR(error, B_ERROR);
856 	RETURN_ERROR(error);
857 }
858 
859 
860 // ramfs_open
861 static status_t
862 ramfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
863 {
864 //	FUNCTION_START();
865 	Volume* volume = (Volume*)_volume->private_volume;
866 	Node* node = (Node*)_node->private_node;
867 
868 	FUNCTION(("node: %Ld\n", node->GetID()));
869 	status_t error = B_OK;
870 	if (VolumeReadLocker locker = volume) {
871 		// directory can be opened read-only
872 		if (node->IsDirectory() && (openMode & O_RWMASK))
873 			openMode &= ~O_RWMASK;
874 		int accessMode = open_mode_to_access(openMode);
875 		// truncating requires write permission
876 		if (error == B_OK && (openMode & O_TRUNC))
877 			accessMode |= ACCESS_W;
878 		// check open mode against permissions
879 		if (error == B_OK)
880 			error = node->CheckPermissions(accessMode);
881 		// create the cookie
882 		FileCookie *cookie = NULL;
883 		if (error == B_OK) {
884 			cookie = new(nothrow) FileCookie(openMode);
885 			if (!cookie)
886 				SET_ERROR(error, B_NO_MEMORY);
887 		}
888 		// truncate if requested
889 		if (error == B_OK && (openMode & O_TRUNC))
890 			error = node->SetSize(0);
891 		NodeMTimeUpdater mTimeUpdater(node);
892 		// set result / cleanup on failure
893 		if (error == B_OK)
894 			*_cookie = cookie;
895 		else if (cookie)
896 			delete cookie;
897 	} else
898 		SET_ERROR(error, B_ERROR);
899 	RETURN_ERROR(error);
900 }
901 
902 
903 // ramfs_close
904 static status_t
905 ramfs_close(fs_volume* _volume, fs_vnode* _node, void* /*cookie*/)
906 {
907 //	FUNCTION_START();
908 	Volume* volume = (Volume*)_volume->private_volume;
909 	Node* node = (Node*)_node->private_node;
910 
911 	FUNCTION(("node: %Ld\n", node->GetID()));
912 	status_t error = B_OK;
913 	// notify listeners
914 	if (VolumeReadLocker locker = volume) {
915 		notify_if_stat_changed(volume, node);
916 	} else
917 		SET_ERROR(error, B_ERROR);
918 	return B_OK;
919 
920 }
921 
922 
923 // ramfs_free_cookie
924 static status_t
925 ramfs_free_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
926 {
927 	FUNCTION_START();
928 	FileCookie *cookie = (FileCookie*)_cookie;
929 	delete cookie;
930 	return B_OK;
931 }
932 
933 
934 // ramfs_read
935 static status_t
936 ramfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
937 	void *buffer, size_t *bufferSize)
938 {
939 //	FUNCTION_START();
940 	Volume* volume = (Volume*)_volume->private_volume;
941 	Node* node = (Node*)_node->private_node;
942 	FileCookie *cookie = (FileCookie*)_cookie;
943 
944 //	FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(),
945 //			  node->GetObjectID(), pos, buffer, *bufferSize));
946 	status_t error = B_OK;
947 	if (VolumeReadLocker locker = volume) {
948 		// don't read anything but files
949 		if (!node->IsFile())
950 			SET_ERROR(error, B_BAD_VALUE);
951 		// check, if reading is allowed
952 		int rwMode = cookie->GetOpenMode() & O_RWMASK;
953 		if (error == B_OK && rwMode != O_RDONLY && rwMode != O_RDWR)
954 			SET_ERROR(error, B_FILE_ERROR);
955 		// read
956 		if (error == B_OK) {
957 			if (File *file = dynamic_cast<File*>(node))
958 				error = file->ReadAt(pos, buffer, *bufferSize, bufferSize);
959 			else {
960 				FATAL("Node %Ld pretends to be a File, but isn't!\n",
961 					node->GetID());
962 				error = B_BAD_VALUE;
963 			}
964 		}
965 	} else
966 		SET_ERROR(error, B_ERROR);
967 	RETURN_ERROR(error);
968 }
969 
970 
971 // ramfs_write
972 static status_t
973 ramfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
974 	const void *buffer, size_t *bufferSize)
975 {
976 //	FUNCTION_START();
977 	Volume* volume = (Volume*)_volume->private_volume;
978 	Node* node = (Node*)_node->private_node;
979 
980 	FileCookie *cookie = (FileCookie*)_cookie;
981 //	FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(),
982 //			  node->GetObjectID(), pos, buffer, *bufferSize));
983 	status_t error = B_OK;
984 	if (VolumeWriteLocker locker = volume) {
985 		// don't write anything but files
986 		if (!node->IsFile())
987 			SET_ERROR(error, B_BAD_VALUE);
988 		if (error == B_OK) {
989 			// check, if reading is allowed
990 			int rwMode = cookie->GetOpenMode() & O_RWMASK;
991 			if (error == B_OK && rwMode != O_WRONLY && rwMode != O_RDWR)
992 				SET_ERROR(error, B_FILE_ERROR);
993 			if (error == B_OK) {
994 				// reset the position, if opened in append mode
995 				if (cookie->GetOpenMode() & O_APPEND)
996 					pos = node->GetSize();
997 				// write
998 				if (File *file = dynamic_cast<File*>(node)) {
999 					error = file->WriteAt(pos, buffer, *bufferSize,
1000 						bufferSize);
1001 				} else {
1002 					FATAL("Node %Ld pretends to be a File, but isn't!\n",
1003 						node->GetID());
1004 					error = B_BAD_VALUE;
1005 				}
1006 			}
1007 		}
1008 		// notify listeners
1009 		if (error == B_OK && cookie->NotificationIntervalElapsed(true))
1010 			notify_if_stat_changed(volume, node);
1011 		NodeMTimeUpdater mTimeUpdater(node);
1012 	} else
1013 		SET_ERROR(error, B_ERROR);
1014 	RETURN_ERROR(error);
1015 }
1016 
1017 
1018 // #pragma mark - Directories
1019 
1020 
1021 // DirectoryCookie
1022 class DirectoryCookie {
1023 public:
1024 	DirectoryCookie(Directory *directory = NULL)
1025 		:
1026 		fIterator(directory),
1027 		fDotIndex(DOT_INDEX),
1028 		// debugging
1029 		fIteratorID(atomic_add(&fNextIteratorID, 1)),
1030 		fGetNextCounter(0)
1031 	{
1032 	}
1033 
1034 	void Unset() { fIterator.Unset(); }
1035 
1036 //	EntryIterator *GetIterator() const { return &fIterator; }
1037 
1038 	status_t GetNext(ino_t *nodeID, const char **entryName)
1039 	{
1040 fGetNextCounter++;
1041 		status_t error = B_OK;
1042 		if (fDotIndex == DOT_INDEX) {
1043 			// "."
1044 			Node *entry = fIterator.GetDirectory();
1045 			*nodeID = entry->GetID();
1046 			*entryName = ".";
1047 			fDotIndex++;
1048 		} else if (fDotIndex == DOT_DOT_INDEX) {
1049 			// ".."
1050 			Directory *dir = fIterator.GetDirectory();
1051 			if (dir->GetParent())
1052 				*nodeID = dir->GetParent()->GetID();
1053 			else
1054 				*nodeID = dir->GetID();
1055 			*entryName = "..";
1056 			fDotIndex++;
1057 		} else {
1058 			// ordinary entries
1059 			Entry *entry = NULL;
1060 			error = fIterator.GetNext(&entry);
1061 			if (error == B_OK) {
1062 				*nodeID = entry->GetNode()->GetID();
1063 				*entryName = entry->GetName();
1064 			}
1065 		}
1066 		PRINT(("EntryIterator %ld, GetNext() counter: %ld, entry: %p (%Ld)\n",
1067 		fIteratorID, fGetNextCounter, fIterator.GetCurrent(),
1068 			(fIterator.GetCurrent()
1069 				? fIterator.GetCurrent()->GetNode()->GetID() : -1)));
1070 		return error;
1071 	}
1072 
1073 	status_t Rewind()
1074 	{
1075 		fDotIndex = DOT_INDEX;
1076 		return fIterator.Rewind();
1077 	}
1078 
1079 	status_t Suspend() { return fIterator.Suspend(); }
1080 	status_t Resume() { return fIterator.Resume(); }
1081 
1082 private:
1083 	enum {
1084 		DOT_INDEX		= 0,
1085 		DOT_DOT_INDEX	= 1,
1086 		ENTRY_INDEX		= 2,
1087 	};
1088 
1089 private:
1090 	EntryIterator	fIterator;
1091 	uint32			fDotIndex;
1092 
1093 	// debugging
1094 	int32			fIteratorID;
1095 	int32			fGetNextCounter;
1096 	static int32	fNextIteratorID;
1097 };
1098 
1099 
1100 int32 DirectoryCookie::fNextIteratorID = 0;
1101 
1102 
1103 // ramfs_create_dir
1104 static status_t
1105 ramfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char *name, int mode)
1106 {
1107 	FUNCTION(("name: `%s', mode: %x\n", name, mode));
1108 	Volume* volume = (Volume*)_volume->private_volume;
1109 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
1110 
1111 	status_t error = B_OK;
1112 	// check name
1113 	if (!name || *name == '\0') {
1114 		SET_ERROR(error, B_BAD_VALUE);
1115 	// check directory
1116 	} else if (!dir) {
1117 		SET_ERROR(error, B_BAD_VALUE);
1118 	} else if (VolumeWriteLocker locker = volume) {
1119 		NodeMTimeUpdater mTimeUpdater(dir);
1120 		// directory deleted?
1121 		bool removed;
1122 		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
1123 			!= B_OK || removed) {
1124 			SET_ERROR(error, B_NOT_ALLOWED);
1125 		}
1126 		// check directory write permissions
1127 		error = dir->CheckPermissions(ACCESS_W);
1128 		Node *node = NULL;
1129 		if (error == B_OK) {
1130 			// check if entry does already exist
1131 			if (dir->FindNode(name, &node) == B_OK) {
1132 				SET_ERROR(error, B_FILE_EXISTS);
1133 			} else {
1134 				// entry doesn't exist: create a directory
1135 				Directory *newDir = NULL;
1136 				error = dir->CreateDirectory(name, &newDir);
1137 				if (error == B_OK) {
1138 					node = newDir;
1139 					// set permissions, owner and group
1140 					node->SetMode(mode);
1141 					node->SetUID(geteuid());
1142 					node->SetGID(getegid());
1143 					// put the node
1144 					volume->PutVNode(node->GetID());
1145 				}
1146 			}
1147 		}
1148 		NodeMTimeUpdater mTimeUpdater2(node);
1149 		// notify listeners
1150 		if (error == B_OK) {
1151 			notify_entry_created(volume->GetID(), dir->GetID(), name,
1152 				node->GetID());
1153 		}
1154 	} else
1155 		SET_ERROR(error, B_ERROR);
1156 	RETURN_ERROR(error);
1157 }
1158 
1159 
1160 // ramfs_remove_dir
1161 static status_t
1162 ramfs_remove_dir(fs_volume* _volume, fs_vnode* _dir, const char *name)
1163 {
1164 	FUNCTION(("name: `%s'\n", name));
1165 	Volume* volume = (Volume*)_volume->private_volume;
1166 	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
1167 	status_t error = B_OK;
1168 
1169 	// check name
1170 	if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) {
1171 		SET_ERROR(error, B_BAD_VALUE);
1172 	// check node
1173 	} else if (!dir) {
1174 		SET_ERROR(error, B_BAD_VALUE);
1175 	} else if (VolumeWriteLocker locker = volume) {
1176 		NodeMTimeUpdater mTimeUpdater(dir);
1177 		// check directory write permissions
1178 		error = dir->CheckPermissions(ACCESS_W);
1179 		ino_t nodeID = -1;
1180 		if (error == B_OK) {
1181 			// check if entry exists
1182 			Node *node = NULL;
1183 			Entry *entry = NULL;
1184 			if (dir->FindAndGetNode(name, &node, &entry) == B_OK) {
1185 				nodeID = node->GetID();
1186 				if (!node->IsDirectory()) {
1187 					SET_ERROR(error, B_NOT_A_DIRECTORY);
1188 				} else if (!dynamic_cast<Directory*>(node)->IsEmpty()) {
1189 					SET_ERROR(error, B_DIRECTORY_NOT_EMPTY);
1190 				} else
1191 					error = dir->DeleteEntry(entry);
1192 				volume->PutVNode(node);
1193 			} else
1194 				SET_ERROR(error, B_ENTRY_NOT_FOUND);
1195 		}
1196 		// notify listeners
1197 		if (error == B_OK)
1198 			notify_entry_removed(volume->GetID(), dir->GetID(), name, nodeID);
1199 	} else
1200 		SET_ERROR(error, B_ERROR);
1201 	RETURN_ERROR(error);
1202 }
1203 
1204 
1205 // ramfs_open_dir
1206 static status_t
1207 ramfs_open_dir(fs_volume* /*fs*/, fs_vnode* _node, void** _cookie)
1208 {
1209 //	FUNCTION_START();
1210 //	Volume *volume = (Volume*)fs;
1211 	Node* node = (Node*)_node->private_node;
1212 
1213 	FUNCTION(("dir: (%Lu)\n", node->GetID()));
1214 	// get the Directory
1215 	status_t error = (node->IsDirectory() ? B_OK : B_NOT_A_DIRECTORY);
1216 	Directory *dir = NULL;
1217 	if (error == B_OK) {
1218 		dir = dynamic_cast<Directory*>(node);
1219 		if (!dir) {
1220 			FATAL("Node %Ld pretends to be a Directory, but isn't!\n",
1221 				node->GetID());
1222 			error = B_NOT_A_DIRECTORY;
1223 		}
1224 	}
1225 	// create a DirectoryCookie
1226 	if (error == B_OK) {
1227 		DirectoryCookie *cookie = new(nothrow) DirectoryCookie(dir);
1228 		if (cookie) {
1229 			error = cookie->Suspend();
1230 			if (error == B_OK)
1231 				*_cookie = cookie;
1232 			else
1233 				delete cookie;
1234 		} else
1235 			SET_ERROR(error, B_NO_MEMORY);
1236 	}
1237 	FUNCTION_END();
1238 	RETURN_ERROR(error);
1239 }
1240 
1241 
1242 // ramfs_close_dir
1243 static status_t
1244 ramfs_close_dir(fs_volume* /*fs*/, fs_vnode* DARG(_node), void* _cookie)
1245 {
1246 	FUNCTION_START();
1247 	FUNCTION(("dir: (%Lu)\n", ((Node*)_node)->GetID()));
1248 	// No locking needed, since the Directory is guaranteed to live at this
1249 	// time and for iterators there is a separate locking.
1250 	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1251 	cookie->Unset();
1252 	return B_OK;
1253 }
1254 
1255 
1256 // ramfs_free_dir_cookie
1257 static status_t
1258 ramfs_free_dir_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1259 {
1260 	FUNCTION_START();
1261 	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1262 	delete cookie;
1263 	return B_OK;
1264 }
1265 
1266 
1267 // ramfs_read_dir
1268 static status_t
1269 ramfs_read_dir(fs_volume* _volume, fs_vnode* DARG(_node), void* _cookie,
1270 	struct dirent *buffer, size_t bufferSize, uint32 *count)
1271 {
1272 	FUNCTION_START();
1273 	Volume* volume = (Volume*)_volume->private_volume;
1274 	DARG(Node *node = (Node*)_node; )
1275 
1276 	FUNCTION(("dir: (%Lu)\n", node->GetID()));
1277 	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1278 	status_t error = B_OK;
1279 	if (VolumeReadLocker locker = volume) {
1280 		error = cookie->Resume();
1281 		if (error == B_OK) {
1282 			// read one entry
1283 			ino_t nodeID = -1;
1284 			const char *name = NULL;
1285 			if (cookie->GetNext(&nodeID, &name) == B_OK) {
1286 				PRINT(("  entry: `%s'\n", name));
1287 				size_t nameLen = strlen(name);
1288 				// check, whether the entry fits into the buffer,
1289 				// and fill it in
1290 				size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1291 				if (length <= bufferSize) {
1292 					buffer->d_dev = volume->GetID();
1293 					buffer->d_ino = nodeID;
1294 					memcpy(buffer->d_name, name, nameLen);
1295 					buffer->d_name[nameLen] = '\0';
1296 					buffer->d_reclen = length;
1297 					*count = 1;
1298 				} else {
1299 					SET_ERROR(error, B_BUFFER_OVERFLOW);
1300 				}
1301 			} else
1302 				*count = 0;
1303 
1304 			cookie->Suspend();
1305 		}
1306 	} else
1307 		SET_ERROR(error, B_ERROR);
1308 
1309 	RETURN_ERROR(error);
1310 }
1311 
1312 
1313 // ramfs_rewind_dir
1314 static status_t
1315 ramfs_rewind_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1316 {
1317 	FUNCTION_START();
1318 	// No locking needed, since the Directory is guaranteed to live at this
1319 	// time and for iterators there is a separate locking.
1320 	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1321 	// no need to Resume(), iterator remains suspended
1322 	status_t error = cookie->Rewind();
1323 	RETURN_ERROR(error);
1324 }
1325 
1326 
1327 // #pragma mark - Attribute Directories
1328 
1329 
1330 // ramfs_open_attr_dir
1331 static status_t
1332 ramfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1333 {
1334 	FUNCTION_START();
1335 	Volume* volume = (Volume*)_volume->private_volume;
1336 	Node* node = (Node*)_node->private_node;
1337 
1338 	status_t error = B_OK;
1339 	if (VolumeReadLocker locker = volume) {
1340 		// check permissions
1341 		error = node->CheckPermissions(ACCESS_R);
1342 		// create iterator
1343 		AttributeIterator *iterator = NULL;
1344 		if (error == B_OK) {
1345 			iterator = new(nothrow) AttributeIterator(node);
1346 			if (iterator)
1347 				error = iterator->Suspend();
1348 			else
1349 				SET_ERROR(error, B_NO_MEMORY);
1350 		}
1351 		// set result / cleanup on failure
1352 		if (error == B_OK)
1353 			*_cookie = iterator;
1354 		else
1355 			delete iterator;
1356 	} else
1357 		SET_ERROR(error, B_ERROR);
1358 	RETURN_ERROR(error);
1359 }
1360 
1361 
1362 // ramfs_close_attr_dir
1363 static status_t
1364 ramfs_close_attr_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1365 {
1366 	FUNCTION_START();
1367 	// No locking needed, since the Node is guaranteed to live at this time
1368 	// and for iterators there is a separate locking.
1369 	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1370 	iterator->Unset();
1371 	return B_OK;
1372 }
1373 
1374 
1375 // ramfs_free_attr_dir_cookie
1376 static status_t
1377 ramfs_free_attr_dir_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/,
1378 	void* _cookie)
1379 {
1380 	FUNCTION_START();
1381 	// No locking needed, since the Node is guaranteed to live at this time
1382 	// and for iterators there is a separate locking.
1383 	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1384 	delete iterator;
1385 	return B_OK;
1386 }
1387 
1388 
1389 // ramfs_read_attr_dir
1390 static status_t
1391 ramfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1392 	struct dirent *buffer, size_t bufferSize, uint32 *count)
1393 {
1394 	FUNCTION_START();
1395 	Volume* volume = (Volume*)_volume->private_volume;
1396 
1397 	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1398 	status_t error = B_OK;
1399 	if (VolumeReadLocker locker = volume) {
1400 		error = iterator->Resume();
1401 		if (error == B_OK) {
1402 			// get next attribute
1403 			Attribute *attribute = NULL;
1404 			if (iterator->GetNext(&attribute) == B_OK) {
1405 				const char *name = attribute->GetName();
1406 				size_t nameLen = strlen(name);
1407 				// check, whether the entry fits into the buffer,
1408 				// and fill it in
1409 				size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1410 				if (length <= bufferSize) {
1411 					buffer->d_dev = volume->GetID();
1412 					buffer->d_ino = -1;	// attributes don't have a node ID
1413 					memcpy(buffer->d_name, name, nameLen);
1414 					buffer->d_name[nameLen] = '\0';
1415 					buffer->d_reclen = length;
1416 					*count = 1;
1417 				} else {
1418 					SET_ERROR(error, B_BUFFER_OVERFLOW);
1419 				}
1420 			} else
1421 				*count = 0;
1422 
1423 			iterator->Suspend();
1424 		}
1425 	} else
1426 		SET_ERROR(error, B_ERROR);
1427 
1428 	RETURN_ERROR(error);
1429 }
1430 
1431 
1432 // ramfs_rewind_attr_dir
1433 static status_t
1434 ramfs_rewind_attr_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1435 {
1436 	FUNCTION_START();
1437 	// No locking needed, since the Node is guaranteed to live at this time
1438 	// and for iterators there is a separate locking.
1439 	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1440 	// no need to Resume(), iterator remains suspended
1441 	status_t error = iterator->Rewind();
1442 	RETURN_ERROR(error);
1443 }
1444 
1445 
1446 // #pragma mark - Attributes
1447 
1448 
1449 // AttributeCookie
1450 class AttributeCookie {
1451 public:
1452 	AttributeCookie() : fOpenMode(0), fLastNotificationTime(0) {}
1453 
1454 	status_t Init(const char* name, int openMode)
1455 	{
1456 		if (!fName.SetTo(name))
1457 			return B_NO_MEMORY;
1458 		fOpenMode = openMode;
1459 
1460 		return B_OK;
1461 	}
1462 
1463 	inline const char* GetName() const		{ return fName.GetString(); }
1464 
1465 	inline int GetOpenMode() const			{ return fOpenMode; }
1466 
1467 	inline bigtime_t GetLastNotificationTime() const
1468 		{ return fLastNotificationTime; }
1469 
1470 	inline bool NotificationIntervalElapsed(bool set = false)
1471 	{
1472 		bigtime_t currentTime = system_time();
1473 		bool result = (currentTime - fLastNotificationTime
1474 			> kNotificationInterval);
1475 		if (set && result)
1476 			fLastNotificationTime = currentTime;
1477 		return result;
1478 	}
1479 
1480 private:
1481 	String		fName;
1482 	int			fOpenMode;
1483 	bigtime_t	fLastNotificationTime;
1484 };
1485 
1486 
1487 // ramfs_create_attr
1488 static status_t
1489 ramfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
1490 	uint32 type, int openMode, void** _cookie)
1491 {
1492 
1493 	Volume* volume = (Volume*)_volume->private_volume;
1494 	Node* node = (Node*)_node->private_node;
1495 
1496 	if (VolumeWriteLocker locker = volume) {
1497 		// try to find the attribute
1498 		Attribute *attribute = NULL;
1499 		node->FindAttribute(name, &attribute);
1500 
1501 		// in case the attribute exists we fail if required by the openMode
1502 		if (attribute && (openMode & O_EXCL))
1503 			RETURN_ERROR(B_FILE_EXISTS);
1504 
1505 		// creating and truncating require write permission
1506 		int accessMode = open_mode_to_access(openMode);
1507 		if (!attribute || (openMode & O_TRUNC))
1508 			accessMode |= ACCESS_W;
1509 
1510 		// check required permissions against node permissions
1511 		status_t error = node->CheckPermissions(accessMode);
1512 		if (error != B_OK)
1513 			RETURN_ERROR(error);
1514 
1515 		// create the cookie
1516 		AttributeCookie *cookie = new(nothrow) AttributeCookie();
1517 		if (!cookie)
1518 			return B_NO_MEMORY;
1519 		ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1520 
1521 		// init the cookie
1522 		error = cookie->Init(name, openMode);
1523 		if (error != B_OK)
1524 			RETURN_ERROR(error);
1525 
1526 		// if not existing yet, create the attribute and set its type
1527 		if (!attribute) {
1528 			error = node->CreateAttribute(name, &attribute);
1529 			if (error != B_OK)
1530 				RETURN_ERROR(error);
1531 
1532 			attribute->SetType(type);
1533 
1534 			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1535 				B_ATTR_CREATED);
1536 
1537 		// else truncate if requested
1538 		} else if (openMode & O_TRUNC) {
1539 			error = attribute->SetSize(0);
1540 			if (error != B_OK)
1541 				return error;
1542 
1543 			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1544 				B_ATTR_CHANGED);
1545 		}
1546 		NodeMTimeUpdater mTimeUpdater(node);
1547 
1548 		// success
1549 		cookieDeleter.Detach();
1550 		*_cookie = cookie;
1551 	} else
1552 		RETURN_ERROR(B_ERROR);
1553 
1554 	return B_OK;
1555 }
1556 
1557 
1558 // ramfs_open_attr
1559 static status_t
1560 ramfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
1561 	int openMode, void** _cookie)
1562 {
1563 //	FUNCTION_START();
1564 	Volume* volume = (Volume*)_volume->private_volume;
1565 	Node* node = (Node*)_node->private_node;
1566 
1567 	FUNCTION(("node: %lld\n", node->GetID()));
1568 	status_t error = B_OK;
1569 
1570 	if (VolumeWriteLocker locker = volume) {
1571 		// find the attribute
1572 		Attribute *attribute = NULL;
1573 		if (error == B_OK)
1574 			error = node->FindAttribute(name, &attribute);
1575 
1576 		// truncating requires write permission
1577 		int accessMode = open_mode_to_access(openMode);
1578 		if (error == B_OK && (openMode & O_TRUNC))
1579 			accessMode |= ACCESS_W;
1580 
1581 		// check open mode against permissions
1582 		if (error == B_OK)
1583 			error = node->CheckPermissions(accessMode);
1584 
1585 		// create the cookie
1586 		AttributeCookie *cookie = NULL;
1587 		if (error == B_OK) {
1588 			cookie = new(nothrow) AttributeCookie();
1589 			if (cookie) {
1590 				SET_ERROR(error, cookie->Init(name, openMode));
1591 			} else {
1592 				SET_ERROR(error, B_NO_MEMORY);
1593 			}
1594 		}
1595 
1596 		// truncate if requested
1597 		if (error == B_OK && (openMode & O_TRUNC)) {
1598 			error = attribute->SetSize(0);
1599 
1600 			if (error == B_OK) {
1601 				notify_attribute_changed(volume->GetID(), -1, node->GetID(),
1602 					name, B_ATTR_CHANGED);
1603 			}
1604 		}
1605 		NodeMTimeUpdater mTimeUpdater(node);
1606 
1607 		// set result / cleanup on failure
1608 		if (error == B_OK)
1609 			*_cookie = cookie;
1610 		else if (cookie)
1611 			delete cookie;
1612 	} else
1613 		SET_ERROR(error, B_ERROR);
1614 	RETURN_ERROR(error);
1615 }
1616 
1617 
1618 // ramfs_close_attr
1619 static status_t
1620 ramfs_close_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1621 {
1622 //	FUNCTION_START();
1623 	Volume* volume = (Volume*)_volume->private_volume;
1624 	Node* node = (Node*)_node->private_node;
1625 
1626 	FUNCTION(("node: %lld\n", node->GetID()));
1627 	status_t error = B_OK;
1628 
1629 	// notify listeners
1630 	if (VolumeReadLocker locker = volume) {
1631 		notify_if_stat_changed(volume, node);
1632 	} else
1633 		SET_ERROR(error, B_ERROR);
1634 	return B_OK;
1635 
1636 }
1637 
1638 
1639 // ramfs_free_attr_cookie
1640 static status_t
1641 ramfs_free_attr_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1642 {
1643 	FUNCTION_START();
1644 	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1645 	delete cookie;
1646 	return B_OK;
1647 }
1648 
1649 
1650 // ramfs_read_attr
1651 static status_t
1652 ramfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1653 	void *buffer, size_t *bufferSize)
1654 {
1655 //	FUNCTION_START();
1656 	Volume* volume = (Volume*)_volume->private_volume;
1657 	Node* node = (Node*)_node->private_node;
1658 
1659 	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1660 
1661 	status_t error = B_OK;
1662 	if (VolumeReadLocker locker = volume) {
1663 		// find the attribute
1664 		Attribute *attribute = NULL;
1665 		if (error == B_OK)
1666 			error = node->FindAttribute(cookie->GetName(), &attribute);
1667 
1668 		// check permissions
1669 		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1670 		if (error == B_OK && !(accessMode & ACCESS_R))
1671 			SET_ERROR(error, B_NOT_ALLOWED);
1672 
1673 		// read
1674 		if (error == B_OK)
1675 			error = attribute->ReadAt(pos, buffer, *bufferSize, bufferSize);
1676 	} else
1677 		SET_ERROR(error, B_ERROR);
1678 	RETURN_ERROR(error);
1679 }
1680 
1681 
1682 // ramfs_write_attr
1683 static status_t
1684 ramfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1685 	off_t pos, const void *buffer, size_t *bufferSize)
1686 {
1687 	//	FUNCTION_START();
1688 	Volume* volume = (Volume*)_volume->private_volume;
1689 	Node* node = (Node*)_node->private_node;
1690 	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1691 
1692 	status_t error = B_OK;
1693 	// Don't allow writing the reserved attributes.
1694 	const char *name = cookie->GetName();
1695 	if (name[0] == '\0' || !strcmp(name, "name")
1696 		|| !strcmp(name, "last_modified") || !strcmp(name, "size")) {
1697 		// FUNCTION(("failed: node: %s, attribute: %s\n",
1698 		//	node->GetName(), name));
1699 		RETURN_ERROR(B_NOT_ALLOWED);
1700 	}
1701 
1702 	if (VolumeWriteLocker locker = volume) {
1703 		NodeMTimeUpdater mTimeUpdater(node);
1704 
1705 		// find the attribute
1706 		Attribute *attribute = NULL;
1707 		if (error == B_OK)
1708 			error = node->FindAttribute(cookie->GetName(), &attribute);
1709 
1710 		// check permissions
1711 		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1712 		if (error == B_OK && !(accessMode & ACCESS_W))
1713 			SET_ERROR(error, B_NOT_ALLOWED);
1714 
1715 		// write the data
1716 		if (error == B_OK)
1717 			error = attribute->WriteAt(pos, buffer, *bufferSize, bufferSize);
1718 
1719 		// notify listeners
1720 		if (error == B_OK) {
1721 			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1722 				B_ATTR_CHANGED);
1723 		}
1724 	} else
1725 		SET_ERROR(error, B_ERROR);
1726 
1727 	RETURN_ERROR(error);
1728 }
1729 
1730 
1731 // ramfs_read_attr_stat
1732 static status_t
1733 ramfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1734 	struct stat *st)
1735 {
1736 //	FUNCTION_START();
1737 	Volume* volume = (Volume*)_volume->private_volume;
1738 	Node* node = (Node*)_node->private_node;
1739 	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1740 	status_t error = B_OK;
1741 
1742 	if (VolumeReadLocker locker = volume) {
1743 		// find the attribute
1744 		Attribute *attribute = NULL;
1745 		if (error == B_OK)
1746 			error = node->FindAttribute(cookie->GetName(), &attribute);
1747 
1748 		// check permissions
1749 		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1750 		if (error == B_OK && !(accessMode & ACCESS_R))
1751 			SET_ERROR(error, B_NOT_ALLOWED);
1752 
1753 		// read
1754 		if (error == B_OK) {
1755 			st->st_type = attribute->GetType();
1756 			st->st_size = attribute->GetSize();
1757 		}
1758 	} else
1759 		SET_ERROR(error, B_ERROR);
1760 	RETURN_ERROR(error);
1761 }
1762 
1763 
1764 // ramfs_rename_attr
1765 static status_t
1766 ramfs_rename_attr(fs_volume* /*fs*/, fs_vnode* /*_fromNode*/,
1767 	const char */*fromName*/, fs_vnode* /*_toNode*/, const char */*toName*/)
1768 {
1769 	// TODO : ramfs_rename_attr
1770 	return B_BAD_VALUE;
1771 }
1772 
1773 
1774 // ramfs_remove_attr
1775 static status_t
1776 ramfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char *name)
1777 {
1778 	FUNCTION_START();
1779 	Volume* volume = (Volume*)_volume->private_volume;
1780 	Node* node = (Node*)_node->private_node;
1781 	status_t error = B_OK;
1782 
1783 	if (VolumeWriteLocker locker = volume) {
1784 		NodeMTimeUpdater mTimeUpdater(node);
1785 
1786 		// check permissions
1787 		error = node->CheckPermissions(ACCESS_W);
1788 
1789 		// find the attribute
1790 		Attribute *attribute = NULL;
1791 		if (error == B_OK)
1792 			error = node->FindAttribute(name, &attribute);
1793 
1794 		// delete it
1795 		if (error == B_OK)
1796 			error = node->DeleteAttribute(attribute);
1797 
1798 		// notify listeners
1799 		if (error == B_OK) {
1800 			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1801 				B_ATTR_REMOVED);
1802 		}
1803 	} else
1804 		SET_ERROR(error, B_ERROR);
1805 
1806 	RETURN_ERROR(error);
1807 }
1808 
1809 
1810 // #pragma mark - Indices
1811 
1812 
1813 // IndexDirCookie
1814 class IndexDirCookie {
1815 public:
1816 	IndexDirCookie() : index_index(0) {}
1817 
1818 	int32	index_index;
1819 };
1820 
1821 
1822 // ramfs_open_index_dir
1823 static status_t
1824 ramfs_open_index_dir(fs_volume* _volume, void** _cookie)
1825 {
1826 	FUNCTION_START();
1827 	Volume* volume = (Volume*)_volume->private_volume;
1828 	status_t error = B_OK;
1829 	if (VolumeReadLocker locker = volume) {
1830 		// check whether an index directory exists
1831 		if (volume->GetIndexDirectory()) {
1832 			IndexDirCookie *cookie = new(nothrow) IndexDirCookie;
1833 			if (cookie)
1834 				*_cookie = cookie;
1835 			else
1836 				SET_ERROR(error, B_NO_MEMORY);
1837 		} else
1838 			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1839 	} else
1840 		SET_ERROR(error, B_ERROR);
1841 	RETURN_ERROR(error);
1842 }
1843 
1844 
1845 // ramfs_close_index_dir
1846 static status_t
1847 ramfs_close_index_dir(fs_volume* /*fs*/, void* /*_cookie*/)
1848 {
1849 	FUNCTION_START();
1850 	return B_OK;
1851 }
1852 
1853 
1854 // ramfs_free_index_dir_cookie
1855 static status_t
1856 ramfs_free_index_dir_cookie(fs_volume* /*fs*/, void* _cookie)
1857 {
1858 	FUNCTION_START();
1859 	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1860 	delete cookie;
1861 	return B_OK;
1862 }
1863 
1864 
1865 // ramfs_read_index_dir
1866 static status_t
1867 ramfs_read_index_dir(fs_volume* _volume, void* _cookie,
1868 	struct dirent *buffer, size_t bufferSize, uint32 *count)
1869 {
1870 	FUNCTION_START();
1871 	Volume* volume = (Volume*)_volume->private_volume;
1872 	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1873 	status_t error = B_OK;
1874 
1875 	if (VolumeReadLocker locker = volume) {
1876 		// get the next index
1877 		Index *index = volume->GetIndexDirectory()->IndexAt(
1878 			cookie->index_index++);
1879 		if (index) {
1880 			const char *name = index->GetName();
1881 			size_t nameLen = strlen(name);
1882 			// check, whether the entry fits into the buffer,
1883 			// and fill it in
1884 			size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1885 			if (length <= bufferSize) {
1886 				buffer->d_dev = volume->GetID();
1887 				buffer->d_ino = -1;	// indices don't have a node ID
1888 				memcpy(buffer->d_name, name, nameLen);
1889 				buffer->d_name[nameLen] = '\0';
1890 				buffer->d_reclen = length;
1891 				*count = 1;
1892 			} else {
1893 				SET_ERROR(error, B_BUFFER_OVERFLOW);
1894 			}
1895 		} else
1896 			*count = 0;
1897 	} else
1898 		SET_ERROR(error, B_ERROR);
1899 
1900 	RETURN_ERROR(error);
1901 }
1902 
1903 
1904 // ramfs_rewind_index_dir
1905 static status_t
1906 ramfs_rewind_index_dir(fs_volume* /*fs*/, void* _cookie)
1907 {
1908 	FUNCTION_START();
1909 	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1910 	cookie->index_index = 0;
1911 	return B_OK;
1912 }
1913 
1914 
1915 // ramfs_create_index
1916 static status_t
1917 ramfs_create_index(fs_volume* _volume, const char *name, uint32 type,
1918 	uint32 /*flags*/)
1919 {
1920 	FUNCTION_START();
1921 	Volume* volume = (Volume*)_volume->private_volume;
1922 	status_t error = B_OK;
1923 
1924 	// only root is allowed to manipulate the indices
1925 	if (geteuid() != 0) {
1926 		SET_ERROR(error, B_NOT_ALLOWED);
1927 	} else if (VolumeWriteLocker locker = volume) {
1928 		// get the index directory
1929 		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
1930 			// check whether an index with that name does already exist
1931 			if (indexDir->FindIndex(name)) {
1932 				SET_ERROR(error, B_FILE_EXISTS);
1933 			} else {
1934 				// create the index
1935 				AttributeIndex *index;
1936 				error = indexDir->CreateIndex(name, type, &index);
1937 			}
1938 		} else
1939 			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1940 	} else
1941 		SET_ERROR(error, B_ERROR);
1942 
1943 	RETURN_ERROR(error);
1944 }
1945 
1946 
1947 // ramfs_remove_index
1948 static status_t
1949 ramfs_remove_index(fs_volume* _volume, const char *name)
1950 {
1951 	FUNCTION_START();
1952 	Volume* volume = (Volume*)_volume->private_volume;
1953 	status_t error = B_OK;
1954 	// only root is allowed to manipulate the indices
1955 	if (geteuid() != 0) {
1956 		SET_ERROR(error, B_NOT_ALLOWED);
1957 	} else if (VolumeWriteLocker locker = volume) {
1958 		// get the index directory
1959 		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
1960 			// check whether an index with that name does exist
1961 			if (Index *index = indexDir->FindIndex(name)) {
1962 				// don't delete a special index
1963 				if (indexDir->IsSpecialIndex(index)) {
1964 					SET_ERROR(error, B_BAD_VALUE);
1965 				} else
1966 					indexDir->DeleteIndex(index);
1967 			} else
1968 				SET_ERROR(error, B_ENTRY_NOT_FOUND);
1969 		} else
1970 			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1971 	} else
1972 		SET_ERROR(error, B_ERROR);
1973 	RETURN_ERROR(error);
1974 }
1975 
1976 
1977 // ramfs_read_index_stat
1978 static status_t
1979 ramfs_read_index_stat(fs_volume* _volume, const char *name, struct stat *st)
1980 {
1981 	FUNCTION_START();
1982 	Volume* volume = (Volume*)_volume->private_volume;
1983 	status_t error = B_OK;
1984 	if (VolumeReadLocker locker = volume) {
1985 		// get the index directory
1986 		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
1987 			// find the index
1988 			if (Index *index = indexDir->FindIndex(name)) {
1989 				st->st_type = index->GetType();
1990 				if (index->HasFixedKeyLength())
1991 					st->st_size = index->GetKeyLength();
1992 				else
1993 					st->st_size = kMaxIndexKeyLength;
1994 				st->st_atime = 0;	// TODO: index times
1995 				st->st_mtime = 0;	// ...
1996 				st->st_ctime = 0;	// ...
1997 				st->st_crtime = 0;	// ...
1998 				st->st_uid = 0;		// root owns the indices
1999 				st->st_gid = 0;		//
2000 			} else
2001 				SET_ERROR(error, B_ENTRY_NOT_FOUND);
2002 		} else
2003 			SET_ERROR(error, B_ENTRY_NOT_FOUND);
2004 	} else
2005 		SET_ERROR(error, B_ERROR);
2006 	RETURN_ERROR(error);
2007 }
2008 
2009 
2010 // #pragma mark - Queries
2011 
2012 // Query implementation by Axel Dörfler. Slightly adjusted.
2013 
2014 // ramfs_open_query
2015 static status_t
2016 ramfs_open_query(fs_volume* _volume, const char *queryString, uint32 flags,
2017 	port_id port, uint32 token, void** _cookie)
2018 {
2019 	FUNCTION_START();
2020 	PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n",
2021 		queryString, flags, port, token));
2022 
2023 	Volume* volume = (Volume*)_volume->private_volume;
2024 
2025 	// lock the volume
2026 	VolumeReadLocker locker(volume);
2027 	if (!locker.IsLocked())
2028 		RETURN_ERROR(B_ERROR);
2029 
2030 	// parse the query expression
2031 	Expression *expression = new Expression((char *)queryString);
2032 	if (expression == NULL)
2033 		RETURN_ERROR(B_NO_MEMORY);
2034 	ObjectDeleter<Expression> expressionDeleter(expression);
2035 
2036 	if (expression->InitCheck() < B_OK) {
2037 		WARN(("Could not parse query, stopped at: \"%s\"\n",
2038 			expression->Position()));
2039 		RETURN_ERROR(B_BAD_VALUE);
2040 	}
2041 
2042 	// create the query
2043 	Query *query = new Query(volume, expression, flags);
2044 	if (query == NULL)
2045 		RETURN_ERROR(B_NO_MEMORY);
2046 	expressionDeleter.Detach();
2047 	// TODO: The Query references an Index, but nothing prevents the Index
2048 	// from being deleted, while the Query is in existence.
2049 
2050 	if (flags & B_LIVE_QUERY)
2051 		query->SetLiveMode(port, token);
2052 
2053 	*_cookie = (void *)query;
2054 
2055 	return B_OK;
2056 }
2057 
2058 
2059 // ramfs_close_query
2060 static status_t
2061 ramfs_close_query(fs_volume* /*fs*/, void* /*cookie*/)
2062 {
2063 	FUNCTION_START();
2064 	return B_OK;
2065 }
2066 
2067 
2068 // ramfs_free_query_cookie
2069 static status_t
2070 ramfs_free_query_cookie(fs_volume* _volume, void* _cookie)
2071 {
2072 	FUNCTION_START();
2073 
2074 	Volume* volume = (Volume*)_volume->private_volume;
2075 
2076 	// lock the volume
2077 	VolumeReadLocker locker(volume);
2078 	if (!locker.IsLocked())
2079 		RETURN_ERROR(B_ERROR);
2080 
2081 	Query *query = (Query *)_cookie;
2082 	Expression *expression = query->GetExpression();
2083 	delete query;
2084 	delete expression;
2085 
2086 	return B_OK;
2087 }
2088 
2089 
2090 // ramfs_read_query
2091 static status_t
2092 ramfs_read_query(fs_volume* _volume, void* _cookie, struct dirent *buffer,
2093 	size_t bufferSize, uint32 *count)
2094 {
2095 	FUNCTION_START();
2096 	Query *query = (Query *)_cookie;
2097 	Volume* volume = (Volume*)_volume->private_volume;
2098 
2099 	// lock the volume
2100 	VolumeReadLocker locker(volume);
2101 	if (!locker.IsLocked())
2102 		RETURN_ERROR(B_ERROR);
2103 
2104 	status_t status = query->GetNextEntry(buffer, bufferSize);
2105 	if (status == B_OK)
2106 		*count = 1;
2107 	else if (status == B_ENTRY_NOT_FOUND)
2108 		*count = 0;
2109 	else
2110 		return status;
2111 
2112 	return B_OK;
2113 }
2114 
2115 
2116 // TODO: status_t (*rewind_query)(fs_volume fs, void** _cookie);
2117 
2118 
2119 // #pragma mark - Module Interface
2120 
2121 
2122 static status_t
2123 ramfs_std_ops(int32 op, ...)
2124 {
2125 	switch (op) {
2126 		case B_MODULE_INIT:
2127 		{
2128 			init_debugging();
2129 			PRINT(("ramfs_std_ops(): B_MODULE_INIT\n"));
2130 			return B_OK;
2131 		}
2132 
2133 		case B_MODULE_UNINIT:
2134 			PRINT(("ramfs_std_ops(): B_MODULE_UNINIT\n"));
2135 			exit_debugging();
2136 			return B_OK;
2137 
2138 		default:
2139 			return B_ERROR;
2140 	}
2141 }
2142 
2143 
2144 fs_volume_ops gRamFSVolumeOps = {
2145 	&ramfs_unmount,
2146 	&ramfs_read_fs_info,
2147 	&ramfs_write_fs_info,
2148 	&ramfs_sync,
2149 	&ramfs_read_vnode,
2150 
2151 	/* index directory & index operations */
2152 	&ramfs_open_index_dir,
2153 	&ramfs_close_index_dir,
2154 	&ramfs_free_index_dir_cookie,
2155 	&ramfs_read_index_dir,
2156 	&ramfs_rewind_index_dir,
2157 
2158 	&ramfs_create_index,
2159 	&ramfs_remove_index,
2160 	&ramfs_read_index_stat,
2161 
2162 	/* query operations */
2163 	&ramfs_open_query,
2164 	&ramfs_close_query,
2165 	&ramfs_free_query_cookie,
2166 	&ramfs_read_query,
2167 	NULL	// rewind_query
2168 };
2169 
2170 
2171 fs_vnode_ops gRamFSVnodeOps = {
2172 	/* vnode operations */
2173 	&ramfs_lookup,			// lookup
2174 	NULL,					// get name
2175 	&ramfs_write_vnode,		// write
2176 	&ramfs_remove_vnode,	// remove
2177 
2178 	/* VM file access */
2179 	NULL,					// can_page
2180 	NULL,					// read pages
2181 	NULL,					// write pages
2182 
2183 	NULL,					// io?
2184 	NULL,					// cancel io
2185 
2186 	NULL,					// get file map
2187 
2188 	&ramfs_ioctl,
2189 	&ramfs_set_flags,
2190 	NULL,   // &ramfs_select,
2191 	NULL,   // &ramfs_deselect,
2192 	&ramfs_fsync,
2193 
2194 	&ramfs_read_symlink,
2195 	&ramfs_create_symlink,
2196 
2197 	&ramfs_link,
2198 	&ramfs_unlink,
2199 	&ramfs_rename,
2200 
2201 	&ramfs_access,
2202 	&ramfs_read_stat,
2203 	&ramfs_write_stat,
2204 	NULL,   // &ramfs_preallocate,
2205 
2206 	/* file operations */
2207 	&ramfs_create,
2208 	&ramfs_open,
2209 	&ramfs_close,
2210 	&ramfs_free_cookie,
2211 	&ramfs_read,
2212 	&ramfs_write,
2213 
2214 	/* directory operations */
2215 	&ramfs_create_dir,
2216 	&ramfs_remove_dir,
2217 	&ramfs_open_dir,
2218 	&ramfs_close_dir,
2219 	&ramfs_free_dir_cookie,
2220 	&ramfs_read_dir,
2221 	&ramfs_rewind_dir,
2222 
2223 	/* attribute directory operations */
2224 	&ramfs_open_attr_dir,
2225 	&ramfs_close_attr_dir,
2226 	&ramfs_free_attr_dir_cookie,
2227 	&ramfs_read_attr_dir,
2228 	&ramfs_rewind_attr_dir,
2229 
2230 	/* attribute operations */
2231 	&ramfs_create_attr,
2232 	&ramfs_open_attr,
2233 	&ramfs_close_attr,
2234 	&ramfs_free_attr_cookie,
2235 	&ramfs_read_attr,
2236 	&ramfs_write_attr,
2237 
2238 	&ramfs_read_attr_stat,
2239 	NULL,   // &ramfs_write_attr_stat,
2240 	&ramfs_rename_attr,
2241 	&ramfs_remove_attr,
2242 
2243 	/* special nodes */
2244 	NULL	// create_special_node
2245 };
2246 
2247 static file_system_module_info sRamFSModuleInfo = {
2248 	{
2249 		"file_systems/ramfs" B_CURRENT_FS_API_VERSION,
2250 		0,
2251 		ramfs_std_ops,
2252 	},
2253 
2254 	"ramfs",				// short_name
2255 	"RAM File System",		// pretty_name
2256 	0						// DDM flags
2257 	| B_DISK_SYSTEM_SUPPORTS_WRITING,
2258 
2259 	// scanning
2260 	NULL,	// identify_partition()
2261 	NULL,	// scan_partition()
2262 	NULL,	// free_identify_partition_cookie()
2263 	NULL,	// free_partition_content_cookie()
2264 
2265 	&ramfs_mount,
2266 
2267 	NULL,	// TODO : &ramfs_get_supported_operations
2268 
2269 	NULL,   // validate_resize
2270 	NULL,   // validate_move
2271 	NULL,   // validate_set_content_name
2272 	NULL,   // validate_set_content_parameters
2273 	NULL,   // validate_initialize,
2274 
2275 	/* shadow partition modification */
2276 	NULL,   // shadow_changed
2277 
2278 	/* writing */
2279 	NULL,   // defragment
2280 	NULL,   // repair
2281 	NULL,   // resize
2282 	NULL,   // move
2283 	NULL,   // set_content_name
2284 	NULL,   // set_content_parameters
2285 	NULL	// bfs_initialize
2286 };
2287 
2288 module_info *modules[] = {
2289 	(module_info *)&sRamFSModuleInfo,
2290 	NULL,
2291 };
2292 
2293