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