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 "Debug.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
64static const size_t kOptimalIOSize = 65536;
65static const bigtime_t kNotificationInterval = 1000000LL;
66
67// notify_if_stat_changed
68void
69notify_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(), node->GetID(), statFields);
74	}
75}
76
77
78// #pragma mark - FS
79
80
81// ramfs_mount
82static status_t
83ramfs_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
114static status_t
115ramfs_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
130static status_t
131ramfs_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
154static status_t
155ramfs_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
171static status_t
172ramfs_sync(fs_volume* /*fs*/)
173{
174	FUNCTION_START();
175	return B_OK;
176}
177
178
179// #pragma mark - VNodes
180
181
182// ramfs_lookup
183static status_t
184ramfs_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);
218SET_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
230static status_t
231ramfs_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
250static status_t
251ramfs_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
266static status_t
267ramfs_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
287static status_t
288ramfs_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;
313PRINT(("  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
337static status_t
338ramfs_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
348static status_t
349ramfs_fsync(fs_volume* /*fs*/, fs_vnode* /*node*/)
350{
351	FUNCTION_START();
352	return B_OK;
353}
354
355
356// ramfs_read_symlink
357static status_t
358ramfs_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
391static status_t
392ramfs_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
449static status_t
450ramfs_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
494static status_t
495ramfs_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
539static status_t
540ramfs_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) {
550FUNCTION(("old dir: %Ld, old name: `%s', new dir: %Ld, new name: `%s'\n",
551oldDir->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
656static status_t
657ramfs_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
673static status_t
674ramfs_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
702static status_t
703ramfs_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
750class FileCookie {
751public:
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
769private:
770	int fOpenMode;
771	bigtime_t fLastNotificationTime;
772};
773
774
775// ramfs_create
776static status_t
777ramfs_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
861static status_t
862ramfs_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
904static status_t
905ramfs_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
924static status_t
925ramfs_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
935static status_t
936ramfs_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
972static status_t
973ramfs_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
1022class DirectoryCookie {
1023public:
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	{
1040fGetNextCounter++;
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
1082private:
1083	enum {
1084		DOT_INDEX		= 0,
1085		DOT_DOT_INDEX	= 1,
1086		ENTRY_INDEX		= 2,
1087	};
1088
1089private:
1090	EntryIterator	fIterator;
1091	uint32			fDotIndex;
1092
1093	// debugging
1094	int32			fIteratorID;
1095	int32			fGetNextCounter;
1096	static vint32	fNextIteratorID;
1097};
1098
1099
1100vint32 DirectoryCookie::fNextIteratorID = 0;
1101
1102
1103// ramfs_create_dir
1104static status_t
1105ramfs_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
1161static status_t
1162ramfs_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
1206static status_t
1207ramfs_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
1243static status_t
1244ramfs_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
1257static status_t
1258ramfs_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
1268static status_t
1269ramfs_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
1314static status_t
1315ramfs_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
1331static status_t
1332ramfs_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
1363static status_t
1364ramfs_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
1376static status_t
1377ramfs_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
1390static status_t
1391ramfs_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
1433static status_t
1434ramfs_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
1450class AttributeCookie {
1451public:
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
1480private:
1481	String		fName;
1482	int			fOpenMode;
1483	bigtime_t	fLastNotificationTime;
1484};
1485
1486
1487// ramfs_create_attr
1488static status_t
1489ramfs_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(), 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(), 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
1559static status_t
1560ramfs_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(), node->GetID(), name,
1602					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
1619static status_t
1620ramfs_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
1640static status_t
1641ramfs_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
1651static status_t
1652ramfs_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
1683static status_t
1684ramfs_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(), 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
1732static status_t
1733ramfs_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
1765static status_t
1766ramfs_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
1775static status_t
1776ramfs_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(), 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
1814class IndexDirCookie {
1815public:
1816	IndexDirCookie() : index_index(0) {}
1817
1818	int32	index_index;
1819};
1820
1821
1822// ramfs_open_index_dir
1823static status_t
1824ramfs_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
1846static status_t
1847ramfs_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
1855static status_t
1856ramfs_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
1866static status_t
1867ramfs_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
1905static status_t
1906ramfs_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
1916static status_t
1917ramfs_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
1948static status_t
1949ramfs_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
1978static status_t
1979ramfs_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
2015static status_t
2016ramfs_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
2060static status_t
2061ramfs_close_query(fs_volume* /*fs*/, void* /*cookie*/)
2062{
2063	FUNCTION_START();
2064	return B_OK;
2065}
2066
2067
2068// ramfs_free_query_cookie
2069static status_t
2070ramfs_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
2091static status_t
2092ramfs_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
2122static status_t
2123ramfs_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
2144fs_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
2171fs_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
2247static 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
2288module_info *modules[] = {
2289	(module_info *)&sRamFSModuleInfo,
2290	NULL,
2291};
2292
2293