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_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize;
697		st->st_atime = node->GetATime();
698		st->st_mtime = node->GetMTime();
699		st->st_ctime = node->GetCTime();
700		st->st_crtime = node->GetCrTime();
701	} else
702		SET_ERROR(error, B_ERROR);
703	RETURN_ERROR(error);
704}
705
706
707// ramfs_write_stat
708static status_t
709ramfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat *st,
710	uint32 mask)
711{
712	Volume* volume = (Volume*)_volume->private_volume;
713	Node* node = (Node*)_node->private_node;
714
715	FUNCTION(("mask: %lx\n", mask));
716	status_t error = B_OK;
717	if (VolumeWriteLocker locker = volume) {
718		NodeMTimeUpdater mTimeUpdater(node);
719		// check permissions
720		error = node->CheckPermissions(ACCESS_W);
721		// size
722		if (error == B_OK && (mask & B_STAT_SIZE))
723			error = node->SetSize(st->st_size);
724		if (error == B_OK) {
725			// permissions
726			if (mask & B_STAT_MODE) {
727				node->SetMode((node->GetMode() & ~S_IUMSK)
728					| (st->st_mode & S_IUMSK));
729			}
730			// UID
731			if (mask & B_STAT_UID)
732				node->SetUID(st->st_uid);
733			// GID
734			if (mask & B_STAT_GID)
735				node->SetGID(st->st_gid);
736			// mtime
737			if (mask & B_STAT_MODIFICATION_TIME)
738				node->SetMTime(st->st_mtime);
739			// crtime
740			if (mask & B_STAT_CREATION_TIME)
741				node->SetCrTime(st->st_crtime);
742		}
743		// notify listeners
744		if (error == B_OK)
745			notify_if_stat_changed(volume, node);
746	} else
747		SET_ERROR(error, B_ERROR);
748	RETURN_ERROR(error);
749}
750
751
752// #pragma mark - Files
753
754
755// FileCookie
756class FileCookie {
757public:
758	FileCookie(int openMode) : fOpenMode(openMode), fLastNotificationTime(0) {}
759
760	inline int GetOpenMode() { return fOpenMode; }
761	inline bigtime_t GetLastNotificationTime() { return fLastNotificationTime; }
762
763	inline bool	NotificationIntervalElapsed(bool set = false)
764	{
765		bigtime_t currentTime = system_time();
766		bool result = (currentTime
767			- fLastNotificationTime > kNotificationInterval);
768
769		if (set && result)
770			fLastNotificationTime = currentTime;
771
772		return result;
773	}
774
775private:
776	int fOpenMode;
777	bigtime_t fLastNotificationTime;
778};
779
780
781// ramfs_create
782static status_t
783ramfs_create(fs_volume* _volume, fs_vnode* _dir, const char *name, int openMode,
784	int mode, void** _cookie, ino_t *vnid)
785{
786//	FUNCTION_START();
787	FUNCTION(("name: `%s', open mode: %x, mode: %x\n", name, openMode, mode));
788	Volume* volume = (Volume*)_volume->private_volume;
789	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
790
791	status_t error = B_OK;
792	// check name
793	if (!name || *name == '\0') {
794		SET_ERROR(error, B_BAD_VALUE);
795	// check directory
796	} else if (!dir) {
797		SET_ERROR(error, B_BAD_VALUE);
798	// check special names
799	} else if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
800		SET_ERROR(error, B_FILE_EXISTS);
801	} else if (VolumeWriteLocker locker = volume) {
802		NodeMTimeUpdater mTimeUpdater(dir);
803		// directory deleted?
804		bool removed;
805		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
806			!= B_OK || removed) {
807			SET_ERROR(error, B_NOT_ALLOWED);
808		}
809		// create the file cookie
810		FileCookie *cookie = NULL;
811		if (error == B_OK) {
812			cookie = new(nothrow) FileCookie(openMode);
813			if (!cookie)
814				SET_ERROR(error, B_NO_MEMORY);
815		}
816		Node *node = NULL;
817		if (error == B_OK) {
818			// check if entry does already exist
819			if (dir->FindNode(name, &node) == B_OK) {
820				// entry does already exist
821				// fail, if we shall fail, when the file exists
822				if (openMode & O_EXCL) {
823					SET_ERROR(error, B_FILE_EXISTS);
824				// don't create a file over an existing directory or symlink
825				} else if (!node->IsFile()) {
826					SET_ERROR(error, B_NOT_ALLOWED);
827				// the user must have write permission for an existing entry
828				} else if ((error = node->CheckPermissions(ACCESS_W)) == B_OK) {
829					// truncate, if requested
830					if (openMode & O_TRUNC)
831						error = node->SetSize(0);
832					// we ignore the supplied permissions in this case
833					// get vnode
834					if (error == B_OK) {
835						*vnid = node->GetID();
836						error = volume->GetVNode(node->GetID(), &node);
837					}
838				}
839			// the user must have dir write permission to create a new entry
840			} else if ((error = dir->CheckPermissions(ACCESS_W)) == B_OK) {
841				// entry doesn't exist: create a file
842				File *file = NULL;
843				error = dir->CreateFile(name, &file);
844				if (error == B_OK) {
845					node = file;
846					*vnid = node->GetID();
847					// set permissions, owner and group
848					node->SetMode(mode);
849					node->SetUID(geteuid());
850					node->SetGID(getegid());
851
852					// set cache in vnode
853					struct vnode* vnode;
854					if (vfs_lookup_vnode(_volume->id, node->GetID(), &vnode) == B_OK) {
855						vfs_set_vnode_cache(vnode, file->GetCache());
856					}
857				}
858			}
859			// set result / cleanup on failure
860			if (error == B_OK)
861				*_cookie = cookie;
862			else if (cookie)
863				delete cookie;
864		}
865		NodeMTimeUpdater mTimeUpdater2(node);
866		// notify listeners
867		if (error == B_OK)
868			notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid);
869	} else
870		SET_ERROR(error, B_ERROR);
871	RETURN_ERROR(error);
872}
873
874
875// ramfs_open
876static status_t
877ramfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
878{
879//	FUNCTION_START();
880	Volume* volume = (Volume*)_volume->private_volume;
881	Node* node = (Node*)_node->private_node;
882
883	FUNCTION(("node: %lld\n", node->GetID()));
884	status_t error = B_OK;
885	if (VolumeReadLocker locker = volume) {
886		// directory can be opened read-only
887		if (node->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
888			error = B_IS_A_DIRECTORY;
889		if (error == B_OK && (openMode & O_DIRECTORY) != 0 && !node->IsDirectory())
890			error = B_NOT_A_DIRECTORY;
891
892		int accessMode = open_mode_to_access(openMode);
893		// truncating requires write permission
894		if (error == B_OK && (openMode & O_TRUNC))
895			accessMode |= ACCESS_W;
896		// check open mode against permissions
897		if (error == B_OK)
898			error = node->CheckPermissions(accessMode);
899		// create the cookie
900		FileCookie *cookie = NULL;
901		if (error == B_OK) {
902			cookie = new(nothrow) FileCookie(openMode);
903			if (!cookie)
904				SET_ERROR(error, B_NO_MEMORY);
905		}
906		// truncate if requested
907		if (error == B_OK && (openMode & O_TRUNC))
908			error = node->SetSize(0);
909		NodeMTimeUpdater mTimeUpdater(node);
910		// set result / cleanup on failure
911		if (error == B_OK)
912			*_cookie = cookie;
913		else if (cookie)
914			delete cookie;
915	} else
916		SET_ERROR(error, B_ERROR);
917	RETURN_ERROR(error);
918}
919
920
921// ramfs_close
922static status_t
923ramfs_close(fs_volume* _volume, fs_vnode* _node, void* /*cookie*/)
924{
925//	FUNCTION_START();
926	Volume* volume = (Volume*)_volume->private_volume;
927	Node* node = (Node*)_node->private_node;
928
929	FUNCTION(("node: %lld\n", node->GetID()));
930	status_t error = B_OK;
931	// notify listeners
932	if (VolumeReadLocker locker = volume) {
933		notify_if_stat_changed(volume, node);
934	} else
935		SET_ERROR(error, B_ERROR);
936	return error;
937
938}
939
940
941// ramfs_free_cookie
942static status_t
943ramfs_free_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
944{
945	FUNCTION_START();
946	FileCookie *cookie = (FileCookie*)_cookie;
947	delete cookie;
948	return B_OK;
949}
950
951
952// ramfs_read
953static status_t
954ramfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
955	void *buffer, size_t *bufferSize)
956{
957//	FUNCTION_START();
958	Volume* volume = (Volume*)_volume->private_volume;
959	Node* node = (Node*)_node->private_node;
960	FileCookie *cookie = (FileCookie*)_cookie;
961
962//	FUNCTION(("((%lu, %lu), %lld, %p, %lu)\n", node->GetDirID(),
963//			  node->GetObjectID(), pos, buffer, *bufferSize));
964	status_t error = B_OK;
965	if (VolumeReadLocker locker = volume) {
966		// don't read anything but files
967		if (!node->IsFile())
968			SET_ERROR(error, B_BAD_VALUE);
969		// check, if reading is allowed
970		int rwMode = cookie->GetOpenMode() & O_RWMASK;
971		if (error == B_OK && rwMode != O_RDONLY && rwMode != O_RDWR)
972			SET_ERROR(error, B_FILE_ERROR);
973		// read
974		if (error == B_OK) {
975			if (File *file = dynamic_cast<File*>(node))
976				error = file->ReadAt(pos, buffer, *bufferSize, bufferSize);
977			else {
978				FATAL("Node %" B_PRIdINO " pretends to be a File, but isn't!\n",
979					node->GetID());
980				error = B_BAD_VALUE;
981			}
982		}
983	} else
984		SET_ERROR(error, B_ERROR);
985	RETURN_ERROR(error);
986}
987
988
989// ramfs_write
990static status_t
991ramfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
992	const void *buffer, size_t *bufferSize)
993{
994//	FUNCTION_START();
995	Volume* volume = (Volume*)_volume->private_volume;
996	Node* node = (Node*)_node->private_node;
997
998	FileCookie *cookie = (FileCookie*)_cookie;
999//	FUNCTION(("((%lu, %lu), %lld, %p, %lu)\n", node->GetDirID(),
1000//			  node->GetObjectID(), pos, buffer, *bufferSize));
1001	status_t error = B_OK;
1002	if (VolumeWriteLocker locker = volume) {
1003		// don't write anything but files
1004		if (!node->IsFile())
1005			SET_ERROR(error, B_BAD_VALUE);
1006		if (error == B_OK) {
1007			// check, if reading is allowed
1008			int rwMode = cookie->GetOpenMode() & O_RWMASK;
1009			if (error == B_OK && rwMode != O_WRONLY && rwMode != O_RDWR)
1010				SET_ERROR(error, B_FILE_ERROR);
1011			if (error == B_OK) {
1012				// reset the position, if opened in append mode
1013				if (cookie->GetOpenMode() & O_APPEND)
1014					pos = node->GetSize();
1015				// write
1016				if (File *file = dynamic_cast<File*>(node)) {
1017					error = file->WriteAt(pos, buffer, *bufferSize,
1018						bufferSize);
1019				} else {
1020					FATAL("Node %" B_PRIdINO " pretends to be a File, but isn't!\n",
1021						node->GetID());
1022					error = B_BAD_VALUE;
1023				}
1024			}
1025		}
1026		// notify listeners
1027		if (error == B_OK && cookie->NotificationIntervalElapsed(true))
1028			notify_if_stat_changed(volume, node);
1029		NodeMTimeUpdater mTimeUpdater(node);
1030	} else
1031		SET_ERROR(error, B_ERROR);
1032	RETURN_ERROR(error);
1033}
1034
1035
1036// #pragma mark - Directories
1037
1038
1039// DirectoryCookie
1040class DirectoryCookie {
1041public:
1042	DirectoryCookie(Directory *directory = NULL)
1043		:
1044		fIterator(directory),
1045		fDotIndex(DOT_INDEX),
1046		// debugging
1047		fIteratorID(atomic_add(&fNextIteratorID, 1)),
1048		fGetNextCounter(0)
1049	{
1050	}
1051
1052	void Unset() { fIterator.Unset(); }
1053
1054//	EntryIterator *GetIterator() const { return &fIterator; }
1055
1056	status_t GetNext(ino_t *nodeID, const char **entryName)
1057	{
1058fGetNextCounter++;
1059		status_t error = B_OK;
1060		if (fDotIndex == DOT_INDEX) {
1061			// "."
1062			Node *entry = fIterator.GetDirectory();
1063			*nodeID = entry->GetID();
1064			*entryName = ".";
1065			fDotIndex++;
1066		} else if (fDotIndex == DOT_DOT_INDEX) {
1067			// ".."
1068			Directory *dir = fIterator.GetDirectory();
1069			if (dir->GetParent())
1070				*nodeID = dir->GetParent()->GetID();
1071			else
1072				*nodeID = dir->GetID();
1073			*entryName = "..";
1074			fDotIndex++;
1075		} else {
1076			// ordinary entries
1077			Entry *entry = NULL;
1078			error = fIterator.GetNext(&entry);
1079			if (error == B_OK) {
1080				*nodeID = entry->GetNode()->GetID();
1081				*entryName = entry->GetName();
1082			}
1083		}
1084		PRINT("EntryIterator %" B_PRId32 ", GetNext() counter: %" B_PRId32 ", entry: %p (%lld)\n",
1085		fIteratorID, fGetNextCounter, fIterator.GetCurrent(),
1086			(fIterator.GetCurrent()
1087				? fIterator.GetCurrent()->GetNode()->GetID() : -1));
1088		return error;
1089	}
1090
1091	status_t Rewind()
1092	{
1093		fDotIndex = DOT_INDEX;
1094		return fIterator.Rewind();
1095	}
1096
1097	status_t Suspend() { return fIterator.Suspend(); }
1098	status_t Resume() { return fIterator.Resume(); }
1099
1100private:
1101	enum {
1102		DOT_INDEX		= 0,
1103		DOT_DOT_INDEX	= 1,
1104		ENTRY_INDEX		= 2,
1105	};
1106
1107private:
1108	EntryIterator	fIterator;
1109	uint32			fDotIndex;
1110
1111	// debugging
1112	int32			fIteratorID;
1113	int32			fGetNextCounter;
1114	static int32	fNextIteratorID;
1115};
1116
1117
1118int32 DirectoryCookie::fNextIteratorID = 0;
1119
1120
1121// ramfs_create_dir
1122static status_t
1123ramfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char *name, int mode)
1124{
1125	FUNCTION(("name: `%s', mode: %x\n", name, mode));
1126	Volume* volume = (Volume*)_volume->private_volume;
1127	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
1128
1129	status_t error = B_OK;
1130	// check name
1131	if (!name || *name == '\0') {
1132		SET_ERROR(error, B_BAD_VALUE);
1133	// check directory
1134	} else if (!dir) {
1135		SET_ERROR(error, B_BAD_VALUE);
1136	// check special names
1137	} else if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
1138		SET_ERROR(error, B_FILE_EXISTS);
1139	} else if (VolumeWriteLocker locker = volume) {
1140		NodeMTimeUpdater mTimeUpdater(dir);
1141		// directory deleted?
1142		bool removed;
1143		if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
1144			!= B_OK || removed) {
1145			SET_ERROR(error, B_NOT_ALLOWED);
1146		}
1147		// check directory write permissions
1148		error = dir->CheckPermissions(ACCESS_W);
1149		Node *node = NULL;
1150		if (error == B_OK) {
1151			// check if entry does already exist
1152			if (dir->FindNode(name, &node) == B_OK) {
1153				SET_ERROR(error, B_FILE_EXISTS);
1154			} else {
1155				// entry doesn't exist: create a directory
1156				Directory *newDir = NULL;
1157				error = dir->CreateDirectory(name, &newDir);
1158				if (error == B_OK) {
1159					node = newDir;
1160					// set permissions, owner and group
1161					node->SetMode(mode);
1162					node->SetUID(geteuid());
1163					node->SetGID(getegid());
1164					// put the node
1165					volume->PutVNode(node->GetID());
1166				}
1167			}
1168		}
1169		NodeMTimeUpdater mTimeUpdater2(node);
1170		// notify listeners
1171		if (error == B_OK) {
1172			notify_entry_created(volume->GetID(), dir->GetID(), name,
1173				node->GetID());
1174		}
1175	} else
1176		SET_ERROR(error, B_ERROR);
1177	RETURN_ERROR(error);
1178}
1179
1180
1181// ramfs_remove_dir
1182static status_t
1183ramfs_remove_dir(fs_volume* _volume, fs_vnode* _dir, const char *name)
1184{
1185	FUNCTION(("name: `%s'\n", name));
1186	Volume* volume = (Volume*)_volume->private_volume;
1187	Directory* dir = dynamic_cast<Directory*>((Node*)_dir->private_node);
1188	status_t error = B_OK;
1189
1190	// check name
1191	if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) {
1192		SET_ERROR(error, B_BAD_VALUE);
1193	// check node
1194	} else if (!dir) {
1195		SET_ERROR(error, B_BAD_VALUE);
1196	} else if (VolumeWriteLocker locker = volume) {
1197		NodeMTimeUpdater mTimeUpdater(dir);
1198		// check directory write permissions
1199		error = dir->CheckPermissions(ACCESS_W);
1200		ino_t nodeID = -1;
1201		if (error == B_OK) {
1202			// check if entry exists
1203			Node *node = NULL;
1204			Entry *entry = NULL;
1205			if (dir->FindAndGetNode(name, &node, &entry) == B_OK) {
1206				nodeID = node->GetID();
1207				if (!node->IsDirectory()) {
1208					SET_ERROR(error, B_NOT_A_DIRECTORY);
1209				} else if (!dynamic_cast<Directory*>(node)->IsEmpty()) {
1210					SET_ERROR(error, B_DIRECTORY_NOT_EMPTY);
1211				} else
1212					error = dir->DeleteEntry(entry);
1213				volume->PutVNode(node);
1214			} else
1215				SET_ERROR(error, B_ENTRY_NOT_FOUND);
1216		}
1217		// notify listeners
1218		if (error == B_OK)
1219			notify_entry_removed(volume->GetID(), dir->GetID(), name, nodeID);
1220	} else
1221		SET_ERROR(error, B_ERROR);
1222	RETURN_ERROR(error);
1223}
1224
1225
1226// ramfs_open_dir
1227static status_t
1228ramfs_open_dir(fs_volume* /*fs*/, fs_vnode* _node, void** _cookie)
1229{
1230//	FUNCTION_START();
1231//	Volume *volume = (Volume*)fs;
1232	Node* node = (Node*)_node->private_node;
1233
1234	FUNCTION(("dir: (%Lu)\n", node->GetID()));
1235	// get the Directory
1236	status_t error = (node->IsDirectory() ? B_OK : B_NOT_A_DIRECTORY);
1237	Directory *dir = NULL;
1238	if (error == B_OK) {
1239		dir = dynamic_cast<Directory*>(node);
1240		if (!dir) {
1241			FATAL("Node %" B_PRIdINO " pretends to be a Directory, but isn't!\n",
1242				node->GetID());
1243			error = B_NOT_A_DIRECTORY;
1244		}
1245	}
1246	// create a DirectoryCookie
1247	if (error == B_OK) {
1248		DirectoryCookie *cookie = new(nothrow) DirectoryCookie(dir);
1249		if (cookie) {
1250			error = cookie->Suspend();
1251			if (error == B_OK)
1252				*_cookie = cookie;
1253			else
1254				delete cookie;
1255		} else
1256			SET_ERROR(error, B_NO_MEMORY);
1257	}
1258	FUNCTION_END();
1259	RETURN_ERROR(error);
1260}
1261
1262
1263// ramfs_close_dir
1264static status_t
1265ramfs_close_dir(fs_volume* /*fs*/, fs_vnode* DARG(_node), void* _cookie)
1266{
1267	FUNCTION_START();
1268	FUNCTION(("dir: (%Lu)\n", ((Node*)_node)->GetID()));
1269	// No locking needed, since the Directory is guaranteed to live at this
1270	// time and for iterators there is a separate locking.
1271	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1272	cookie->Unset();
1273	return B_OK;
1274}
1275
1276
1277// ramfs_free_dir_cookie
1278static status_t
1279ramfs_free_dir_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1280{
1281	FUNCTION_START();
1282	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1283	delete cookie;
1284	return B_OK;
1285}
1286
1287
1288// ramfs_read_dir
1289static status_t
1290ramfs_read_dir(fs_volume* _volume, fs_vnode* DARG(_node), void* _cookie,
1291	struct dirent *buffer, size_t bufferSize, uint32 *count)
1292{
1293	FUNCTION_START();
1294	Volume* volume = (Volume*)_volume->private_volume;
1295	DARG(Node *node = (Node*)_node; )
1296
1297	FUNCTION(("dir: (%Lu)\n", node->GetID()));
1298	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1299	status_t error = B_OK;
1300	if (VolumeReadLocker locker = volume) {
1301		error = cookie->Resume();
1302		if (error == B_OK) {
1303			// read one entry
1304			ino_t nodeID = -1;
1305			const char *name = NULL;
1306			if (cookie->GetNext(&nodeID, &name) == B_OK) {
1307				PRINT("  entry: `%s'\n", name);
1308				size_t nameLen = strlen(name);
1309				// check, whether the entry fits into the buffer,
1310				// and fill it in
1311				size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1312				if (length <= bufferSize) {
1313					buffer->d_dev = volume->GetID();
1314					buffer->d_ino = nodeID;
1315					memcpy(buffer->d_name, name, nameLen);
1316					buffer->d_name[nameLen] = '\0';
1317					buffer->d_reclen = length;
1318					*count = 1;
1319				} else {
1320					SET_ERROR(error, B_BUFFER_OVERFLOW);
1321				}
1322			} else
1323				*count = 0;
1324
1325			cookie->Suspend();
1326		}
1327	} else
1328		SET_ERROR(error, B_ERROR);
1329
1330	RETURN_ERROR(error);
1331}
1332
1333
1334// ramfs_rewind_dir
1335static status_t
1336ramfs_rewind_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1337{
1338	FUNCTION_START();
1339	// No locking needed, since the Directory is guaranteed to live at this
1340	// time and for iterators there is a separate locking.
1341	DirectoryCookie *cookie = (DirectoryCookie*)_cookie;
1342	// no need to Resume(), iterator remains suspended
1343	status_t error = cookie->Rewind();
1344	RETURN_ERROR(error);
1345}
1346
1347
1348// #pragma mark - Attribute Directories
1349
1350
1351// ramfs_open_attr_dir
1352static status_t
1353ramfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1354{
1355	FUNCTION_START();
1356	Volume* volume = (Volume*)_volume->private_volume;
1357	Node* node = (Node*)_node->private_node;
1358
1359	status_t error = B_OK;
1360	if (VolumeReadLocker locker = volume) {
1361		// check permissions
1362		error = node->CheckPermissions(ACCESS_R);
1363		// create iterator
1364		AttributeIterator *iterator = NULL;
1365		if (error == B_OK) {
1366			iterator = new(nothrow) AttributeIterator(node);
1367			if (iterator)
1368				error = iterator->Suspend();
1369			else
1370				SET_ERROR(error, B_NO_MEMORY);
1371		}
1372		// set result / cleanup on failure
1373		if (error == B_OK)
1374			*_cookie = iterator;
1375		else
1376			delete iterator;
1377	} else
1378		SET_ERROR(error, B_ERROR);
1379	RETURN_ERROR(error);
1380}
1381
1382
1383// ramfs_close_attr_dir
1384static status_t
1385ramfs_close_attr_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1386{
1387	FUNCTION_START();
1388	// No locking needed, since the Node is guaranteed to live at this time
1389	// and for iterators there is a separate locking.
1390	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1391	iterator->Unset();
1392	return B_OK;
1393}
1394
1395
1396// ramfs_free_attr_dir_cookie
1397static status_t
1398ramfs_free_attr_dir_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/,
1399	void* _cookie)
1400{
1401	FUNCTION_START();
1402	// No locking needed, since the Node is guaranteed to live at this time
1403	// and for iterators there is a separate locking.
1404	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1405	delete iterator;
1406	return B_OK;
1407}
1408
1409
1410// ramfs_read_attr_dir
1411static status_t
1412ramfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1413	struct dirent *buffer, size_t bufferSize, uint32 *count)
1414{
1415	FUNCTION_START();
1416	Volume* volume = (Volume*)_volume->private_volume;
1417
1418	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1419	status_t error = B_OK;
1420	if (VolumeReadLocker locker = volume) {
1421		error = iterator->Resume();
1422		if (error == B_OK) {
1423			// get next attribute
1424			Attribute *attribute = NULL;
1425			if (iterator->GetNext(&attribute) == B_OK) {
1426				const char *name = attribute->GetName();
1427				size_t nameLen = strlen(name);
1428				// check, whether the entry fits into the buffer,
1429				// and fill it in
1430				size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1431				if (length <= bufferSize) {
1432					buffer->d_dev = volume->GetID();
1433					buffer->d_ino = -1;	// attributes don't have a node ID
1434					memcpy(buffer->d_name, name, nameLen);
1435					buffer->d_name[nameLen] = '\0';
1436					buffer->d_reclen = length;
1437					*count = 1;
1438				} else {
1439					SET_ERROR(error, B_BUFFER_OVERFLOW);
1440				}
1441			} else
1442				*count = 0;
1443
1444			iterator->Suspend();
1445		}
1446	} else
1447		SET_ERROR(error, B_ERROR);
1448
1449	RETURN_ERROR(error);
1450}
1451
1452
1453// ramfs_rewind_attr_dir
1454static status_t
1455ramfs_rewind_attr_dir(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1456{
1457	FUNCTION_START();
1458	// No locking needed, since the Node is guaranteed to live at this time
1459	// and for iterators there is a separate locking.
1460	AttributeIterator *iterator = (AttributeIterator*)_cookie;
1461	// no need to Resume(), iterator remains suspended
1462	status_t error = iterator->Rewind();
1463	RETURN_ERROR(error);
1464}
1465
1466
1467// #pragma mark - Attributes
1468
1469
1470// AttributeCookie
1471class AttributeCookie {
1472public:
1473	AttributeCookie() : fOpenMode(0), fLastNotificationTime(0) {}
1474
1475	status_t Init(const char* name, int openMode)
1476	{
1477		if (!fName.SetTo(name))
1478			return B_NO_MEMORY;
1479		fOpenMode = openMode;
1480
1481		return B_OK;
1482	}
1483
1484	inline const char* GetName() const		{ return fName.GetString(); }
1485
1486	inline int GetOpenMode() const			{ return fOpenMode; }
1487
1488	inline bigtime_t GetLastNotificationTime() const
1489		{ return fLastNotificationTime; }
1490
1491	inline bool NotificationIntervalElapsed(bool set = false)
1492	{
1493		bigtime_t currentTime = system_time();
1494		bool result = (currentTime - fLastNotificationTime
1495			> kNotificationInterval);
1496		if (set && result)
1497			fLastNotificationTime = currentTime;
1498		return result;
1499	}
1500
1501private:
1502	String		fName;
1503	int			fOpenMode;
1504	bigtime_t	fLastNotificationTime;
1505};
1506
1507
1508// ramfs_create_attr
1509static status_t
1510ramfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
1511	uint32 type, int openMode, void** _cookie)
1512{
1513
1514	Volume* volume = (Volume*)_volume->private_volume;
1515	Node* node = (Node*)_node->private_node;
1516
1517	if (VolumeWriteLocker locker = volume) {
1518		// try to find the attribute
1519		Attribute *attribute = NULL;
1520		node->FindAttribute(name, &attribute);
1521
1522		// in case the attribute exists we fail if required by the openMode
1523		if (attribute && (openMode & O_EXCL))
1524			RETURN_ERROR(B_FILE_EXISTS);
1525
1526		// creating and truncating require write permission
1527		int accessMode = open_mode_to_access(openMode);
1528		if (!attribute || (openMode & O_TRUNC))
1529			accessMode |= ACCESS_W;
1530
1531		// check required permissions against node permissions
1532		status_t error = node->CheckPermissions(accessMode);
1533		if (error != B_OK)
1534			RETURN_ERROR(error);
1535
1536		// create the cookie
1537		AttributeCookie *cookie = new(nothrow) AttributeCookie();
1538		if (!cookie)
1539			return B_NO_MEMORY;
1540		ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1541
1542		// init the cookie
1543		error = cookie->Init(name, openMode);
1544		if (error != B_OK)
1545			RETURN_ERROR(error);
1546
1547		// if not existing yet, create the attribute and set its type
1548		if (!attribute) {
1549			error = node->CreateAttribute(name, &attribute);
1550			if (error != B_OK)
1551				RETURN_ERROR(error);
1552
1553			attribute->SetType(type);
1554
1555			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1556				B_ATTR_CREATED);
1557
1558		// else truncate if requested
1559		} else if (openMode & O_TRUNC) {
1560			error = attribute->SetSize(0);
1561			if (error != B_OK)
1562				return error;
1563
1564			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1565				B_ATTR_CHANGED);
1566		}
1567		NodeMTimeUpdater mTimeUpdater(node);
1568
1569		// success
1570		cookieDeleter.Detach();
1571		*_cookie = cookie;
1572	} else
1573		RETURN_ERROR(B_ERROR);
1574
1575	return B_OK;
1576}
1577
1578
1579// ramfs_open_attr
1580static status_t
1581ramfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
1582	int openMode, void** _cookie)
1583{
1584//	FUNCTION_START();
1585	Volume* volume = (Volume*)_volume->private_volume;
1586	Node* node = (Node*)_node->private_node;
1587
1588	FUNCTION(("node: %lld\n", node->GetID()));
1589	status_t error = B_OK;
1590
1591	if (VolumeWriteLocker locker = volume) {
1592		// find the attribute
1593		Attribute *attribute = NULL;
1594		if (error == B_OK)
1595			error = node->FindAttribute(name, &attribute);
1596
1597		// truncating requires write permission
1598		int accessMode = open_mode_to_access(openMode);
1599		if (error == B_OK && (openMode & O_TRUNC))
1600			accessMode |= ACCESS_W;
1601
1602		// check open mode against permissions
1603		if (error == B_OK)
1604			error = node->CheckPermissions(accessMode);
1605
1606		// create the cookie
1607		AttributeCookie *cookie = NULL;
1608		if (error == B_OK) {
1609			cookie = new(nothrow) AttributeCookie();
1610			if (cookie) {
1611				SET_ERROR(error, cookie->Init(name, openMode));
1612			} else {
1613				SET_ERROR(error, B_NO_MEMORY);
1614			}
1615		}
1616
1617		// truncate if requested
1618		if (error == B_OK && (openMode & O_TRUNC)) {
1619			error = attribute->SetSize(0);
1620
1621			if (error == B_OK) {
1622				notify_attribute_changed(volume->GetID(), -1, node->GetID(),
1623					name, B_ATTR_CHANGED);
1624			}
1625		}
1626		NodeMTimeUpdater mTimeUpdater(node);
1627
1628		// set result / cleanup on failure
1629		if (error == B_OK)
1630			*_cookie = cookie;
1631		else if (cookie)
1632			delete cookie;
1633	} else
1634		SET_ERROR(error, B_ERROR);
1635	RETURN_ERROR(error);
1636}
1637
1638
1639// ramfs_close_attr
1640static status_t
1641ramfs_close_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1642{
1643//	FUNCTION_START();
1644	Volume* volume = (Volume*)_volume->private_volume;
1645	Node* node = (Node*)_node->private_node;
1646
1647	FUNCTION(("node: %lld\n", node->GetID()));
1648	status_t error = B_OK;
1649
1650	// notify listeners
1651	if (VolumeReadLocker locker = volume) {
1652		notify_if_stat_changed(volume, node);
1653	} else
1654		SET_ERROR(error, B_ERROR);
1655	return error;
1656}
1657
1658
1659// ramfs_free_attr_cookie
1660static status_t
1661ramfs_free_attr_cookie(fs_volume* /*fs*/, fs_vnode* /*_node*/, void* _cookie)
1662{
1663	FUNCTION_START();
1664	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1665	delete cookie;
1666	return B_OK;
1667}
1668
1669
1670// ramfs_read_attr
1671static status_t
1672ramfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1673	void *buffer, size_t *bufferSize)
1674{
1675//	FUNCTION_START();
1676	Volume* volume = (Volume*)_volume->private_volume;
1677	Node* node = (Node*)_node->private_node;
1678
1679	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1680
1681	status_t error = B_OK;
1682	if (VolumeReadLocker locker = volume) {
1683		// find the attribute
1684		Attribute *attribute = NULL;
1685		if (error == B_OK)
1686			error = node->FindAttribute(cookie->GetName(), &attribute);
1687
1688		// check permissions
1689		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1690		if (error == B_OK && !(accessMode & ACCESS_R))
1691			SET_ERROR(error, B_NOT_ALLOWED);
1692
1693		// read
1694		if (error == B_OK)
1695			error = attribute->ReadAt(pos, buffer, *bufferSize, bufferSize);
1696	} else
1697		SET_ERROR(error, B_ERROR);
1698	RETURN_ERROR(error);
1699}
1700
1701
1702// ramfs_write_attr
1703static status_t
1704ramfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1705	off_t pos, const void *buffer, size_t *bufferSize)
1706{
1707	//	FUNCTION_START();
1708	Volume* volume = (Volume*)_volume->private_volume;
1709	Node* node = (Node*)_node->private_node;
1710	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1711
1712	status_t error = B_OK;
1713	// Don't allow writing the reserved attributes.
1714	const char *name = cookie->GetName();
1715	if (name[0] == '\0' || !strcmp(name, "name")
1716		|| !strcmp(name, "last_modified") || !strcmp(name, "size")) {
1717		// FUNCTION(("failed: node: %s, attribute: %s\n",
1718		//	node->GetName(), name));
1719		RETURN_ERROR(B_NOT_ALLOWED);
1720	}
1721
1722	if (VolumeWriteLocker locker = volume) {
1723		NodeMTimeUpdater mTimeUpdater(node);
1724
1725		// find the attribute
1726		Attribute *attribute = NULL;
1727		if (error == B_OK)
1728			error = node->FindAttribute(cookie->GetName(), &attribute);
1729
1730		// check permissions
1731		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1732		if (error == B_OK && !(accessMode & ACCESS_W))
1733			SET_ERROR(error, B_NOT_ALLOWED);
1734
1735		// write the data
1736		if (error == B_OK)
1737			error = attribute->WriteAt(pos, buffer, *bufferSize, bufferSize);
1738
1739		// notify listeners
1740		if (error == B_OK) {
1741			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1742				B_ATTR_CHANGED);
1743		}
1744	} else
1745		SET_ERROR(error, B_ERROR);
1746
1747	RETURN_ERROR(error);
1748}
1749
1750
1751// ramfs_read_attr_stat
1752static status_t
1753ramfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1754	struct stat *st)
1755{
1756//	FUNCTION_START();
1757	Volume* volume = (Volume*)_volume->private_volume;
1758	Node* node = (Node*)_node->private_node;
1759	AttributeCookie *cookie = (AttributeCookie*)_cookie;
1760	status_t error = B_OK;
1761
1762	if (VolumeReadLocker locker = volume) {
1763		// find the attribute
1764		Attribute *attribute = NULL;
1765		if (error == B_OK)
1766			error = node->FindAttribute(cookie->GetName(), &attribute);
1767
1768		// check permissions
1769		int accessMode = open_mode_to_access(cookie->GetOpenMode());
1770		if (error == B_OK && !(accessMode & ACCESS_R))
1771			SET_ERROR(error, B_NOT_ALLOWED);
1772
1773		// read
1774		if (error == B_OK) {
1775			st->st_type = attribute->GetType();
1776			st->st_size = attribute->GetSize();
1777		}
1778	} else
1779		SET_ERROR(error, B_ERROR);
1780	RETURN_ERROR(error);
1781}
1782
1783
1784// ramfs_rename_attr
1785static status_t
1786ramfs_rename_attr(fs_volume* /*fs*/, fs_vnode* /*_fromNode*/,
1787	const char */*fromName*/, fs_vnode* /*_toNode*/, const char */*toName*/)
1788{
1789	// TODO : ramfs_rename_attr
1790	return B_BAD_VALUE;
1791}
1792
1793
1794// ramfs_remove_attr
1795static status_t
1796ramfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char *name)
1797{
1798	FUNCTION_START();
1799	Volume* volume = (Volume*)_volume->private_volume;
1800	Node* node = (Node*)_node->private_node;
1801	status_t error = B_OK;
1802
1803	if (VolumeWriteLocker locker = volume) {
1804		NodeMTimeUpdater mTimeUpdater(node);
1805
1806		// check permissions
1807		error = node->CheckPermissions(ACCESS_W);
1808
1809		// find the attribute
1810		Attribute *attribute = NULL;
1811		if (error == B_OK)
1812			error = node->FindAttribute(name, &attribute);
1813
1814		// delete it
1815		if (error == B_OK)
1816			error = node->DeleteAttribute(attribute);
1817
1818		// notify listeners
1819		if (error == B_OK) {
1820			notify_attribute_changed(volume->GetID(), -1, node->GetID(), name,
1821				B_ATTR_REMOVED);
1822		}
1823	} else
1824		SET_ERROR(error, B_ERROR);
1825
1826	RETURN_ERROR(error);
1827}
1828
1829
1830// #pragma mark - Indices
1831
1832
1833// IndexDirCookie
1834class IndexDirCookie {
1835public:
1836	IndexDirCookie() : index_index(0) {}
1837
1838	int32	index_index;
1839};
1840
1841
1842// ramfs_open_index_dir
1843static status_t
1844ramfs_open_index_dir(fs_volume* _volume, void** _cookie)
1845{
1846	FUNCTION_START();
1847	Volume* volume = (Volume*)_volume->private_volume;
1848	status_t error = B_OK;
1849	if (VolumeReadLocker locker = volume) {
1850		// check whether an index directory exists
1851		if (volume->GetIndexDirectory()) {
1852			IndexDirCookie *cookie = new(nothrow) IndexDirCookie;
1853			if (cookie)
1854				*_cookie = cookie;
1855			else
1856				SET_ERROR(error, B_NO_MEMORY);
1857		} else
1858			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1859	} else
1860		SET_ERROR(error, B_ERROR);
1861	RETURN_ERROR(error);
1862}
1863
1864
1865// ramfs_close_index_dir
1866static status_t
1867ramfs_close_index_dir(fs_volume* /*fs*/, void* /*_cookie*/)
1868{
1869	FUNCTION_START();
1870	return B_OK;
1871}
1872
1873
1874// ramfs_free_index_dir_cookie
1875static status_t
1876ramfs_free_index_dir_cookie(fs_volume* /*fs*/, void* _cookie)
1877{
1878	FUNCTION_START();
1879	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1880	delete cookie;
1881	return B_OK;
1882}
1883
1884
1885// ramfs_read_index_dir
1886static status_t
1887ramfs_read_index_dir(fs_volume* _volume, void* _cookie,
1888	struct dirent *buffer, size_t bufferSize, uint32 *count)
1889{
1890	FUNCTION_START();
1891	Volume* volume = (Volume*)_volume->private_volume;
1892	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1893	status_t error = B_OK;
1894
1895	if (VolumeReadLocker locker = volume) {
1896		// get the next index
1897		Index *index = volume->GetIndexDirectory()->IndexAt(
1898			cookie->index_index++);
1899		if (index) {
1900			const char *name = index->GetName();
1901			size_t nameLen = strlen(name);
1902			// check, whether the entry fits into the buffer,
1903			// and fill it in
1904			size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
1905			if (length <= bufferSize) {
1906				buffer->d_dev = volume->GetID();
1907				buffer->d_ino = -1;	// indices don't have a node ID
1908				memcpy(buffer->d_name, name, nameLen);
1909				buffer->d_name[nameLen] = '\0';
1910				buffer->d_reclen = length;
1911				*count = 1;
1912			} else {
1913				SET_ERROR(error, B_BUFFER_OVERFLOW);
1914			}
1915		} else
1916			*count = 0;
1917	} else
1918		SET_ERROR(error, B_ERROR);
1919
1920	RETURN_ERROR(error);
1921}
1922
1923
1924// ramfs_rewind_index_dir
1925static status_t
1926ramfs_rewind_index_dir(fs_volume* /*fs*/, void* _cookie)
1927{
1928	FUNCTION_START();
1929	IndexDirCookie *cookie = (IndexDirCookie*)_cookie;
1930	cookie->index_index = 0;
1931	return B_OK;
1932}
1933
1934
1935// ramfs_create_index
1936static status_t
1937ramfs_create_index(fs_volume* _volume, const char *name, uint32 type,
1938	uint32 /*flags*/)
1939{
1940	FUNCTION_START();
1941	Volume* volume = (Volume*)_volume->private_volume;
1942	status_t error = B_OK;
1943
1944	// only root is allowed to manipulate the indices
1945	if (geteuid() != 0) {
1946		SET_ERROR(error, B_NOT_ALLOWED);
1947	} else if (VolumeWriteLocker locker = volume) {
1948		// get the index directory
1949		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
1950			// check whether an index with that name does already exist
1951			if (indexDir->FindIndex(name)) {
1952				SET_ERROR(error, B_FILE_EXISTS);
1953			} else {
1954				// create the index
1955				AttributeIndex *index;
1956				error = indexDir->CreateIndex(name, type, &index);
1957			}
1958		} else
1959			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1960	} else
1961		SET_ERROR(error, B_ERROR);
1962
1963	RETURN_ERROR(error);
1964}
1965
1966
1967// ramfs_remove_index
1968static status_t
1969ramfs_remove_index(fs_volume* _volume, const char *name)
1970{
1971	FUNCTION_START();
1972	Volume* volume = (Volume*)_volume->private_volume;
1973	status_t error = B_OK;
1974	// only root is allowed to manipulate the indices
1975	if (geteuid() != 0) {
1976		SET_ERROR(error, B_NOT_ALLOWED);
1977	} else if (VolumeWriteLocker locker = volume) {
1978		// get the index directory
1979		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
1980			// check whether an index with that name does exist
1981			if (Index *index = indexDir->FindIndex(name)) {
1982				// don't delete a special index
1983				if (indexDir->IsSpecialIndex(index)) {
1984					SET_ERROR(error, B_BAD_VALUE);
1985				} else
1986					indexDir->DeleteIndex(index);
1987			} else
1988				SET_ERROR(error, B_ENTRY_NOT_FOUND);
1989		} else
1990			SET_ERROR(error, B_ENTRY_NOT_FOUND);
1991	} else
1992		SET_ERROR(error, B_ERROR);
1993	RETURN_ERROR(error);
1994}
1995
1996
1997// ramfs_read_index_stat
1998static status_t
1999ramfs_read_index_stat(fs_volume* _volume, const char *name, struct stat *st)
2000{
2001	FUNCTION_START();
2002	Volume* volume = (Volume*)_volume->private_volume;
2003	status_t error = B_OK;
2004	if (VolumeReadLocker locker = volume) {
2005		// get the index directory
2006		if (IndexDirectory *indexDir = volume->GetIndexDirectory()) {
2007			// find the index
2008			if (Index *index = indexDir->FindIndex(name)) {
2009				st->st_type = index->GetType();
2010				if (index->HasFixedKeyLength())
2011					st->st_size = index->GetKeyLength();
2012				else
2013					st->st_size = kMaxIndexKeyLength;
2014				st->st_atime = 0;	// TODO: index times
2015				st->st_mtime = 0;	// ...
2016				st->st_ctime = 0;	// ...
2017				st->st_crtime = 0;	// ...
2018				st->st_uid = 0;		// root owns the indices
2019				st->st_gid = 0;		//
2020			} else
2021				SET_ERROR(error, B_ENTRY_NOT_FOUND);
2022		} else
2023			SET_ERROR(error, B_ENTRY_NOT_FOUND);
2024	} else
2025		SET_ERROR(error, B_ERROR);
2026	RETURN_ERROR(error);
2027}
2028
2029
2030// #pragma mark - Queries
2031
2032// Query implementation by Axel D��rfler. Slightly adjusted.
2033
2034// ramfs_open_query
2035static status_t
2036ramfs_open_query(fs_volume* _volume, const char *queryString, uint32 flags,
2037	port_id port, uint32 token, void** _cookie)
2038{
2039	FUNCTION_START();
2040	PRINT("query = \"%s\", flags = %lu, port_id = %" B_PRId32 ", token = %" B_PRId32 "\n",
2041		queryString, flags, port, token);
2042
2043	Volume* volume = (Volume*)_volume->private_volume;
2044
2045	// lock the volume
2046	VolumeWriteLocker locker(volume);
2047	if (!locker.IsLocked())
2048		RETURN_ERROR(B_ERROR);
2049
2050	Query* query;
2051	status_t error = Query::Create(volume, queryString, flags, port, token, query);
2052	if (error != B_OK)
2053		return error;
2054	// TODO: The Query references an Index, but nothing prevents the Index
2055	// from being deleted, while the Query is in existence.
2056
2057	*_cookie = (void *)query;
2058
2059	return B_OK;
2060}
2061
2062
2063// ramfs_close_query
2064static status_t
2065ramfs_close_query(fs_volume* /*fs*/, void* /*cookie*/)
2066{
2067	FUNCTION_START();
2068	return B_OK;
2069}
2070
2071
2072// ramfs_free_query_cookie
2073static status_t
2074ramfs_free_query_cookie(fs_volume* _volume, void* _cookie)
2075{
2076	FUNCTION_START();
2077
2078	Volume* volume = (Volume*)_volume->private_volume;
2079
2080	// lock the volume
2081	VolumeWriteLocker locker(volume);
2082	if (!locker.IsLocked())
2083		RETURN_ERROR(B_ERROR);
2084
2085	Query *query = (Query *)_cookie;
2086	delete query;
2087
2088	return B_OK;
2089}
2090
2091
2092// ramfs_read_query
2093static status_t
2094ramfs_read_query(fs_volume* _volume, void* _cookie, struct dirent *buffer,
2095	size_t bufferSize, uint32 *count)
2096{
2097	FUNCTION_START();
2098	Query *query = (Query *)_cookie;
2099	Volume* volume = (Volume*)_volume->private_volume;
2100
2101	// lock the volume
2102	VolumeReadLocker locker(volume);
2103	if (!locker.IsLocked())
2104		RETURN_ERROR(B_ERROR);
2105
2106	status_t status = query->GetNextEntry(buffer, bufferSize);
2107	if (status == B_OK)
2108		*count = 1;
2109	else if (status == B_ENTRY_NOT_FOUND)
2110		*count = 0;
2111	else
2112		return status;
2113
2114	return B_OK;
2115}
2116
2117
2118// TODO: status_t (*rewind_query)(fs_volume fs, void** _cookie);
2119
2120
2121// #pragma mark - Module Interface
2122
2123
2124static status_t
2125ramfs_std_ops(int32 op, ...)
2126{
2127	switch (op) {
2128		case B_MODULE_INIT:
2129		{
2130			init_debugging();
2131			PRINT("ramfs_std_ops(): B_MODULE_INIT\n");
2132			return B_OK;
2133		}
2134
2135		case B_MODULE_UNINIT:
2136			PRINT("ramfs_std_ops(): B_MODULE_UNINIT\n");
2137			exit_debugging();
2138			return B_OK;
2139
2140		default:
2141			return B_ERROR;
2142	}
2143}
2144
2145
2146fs_volume_ops gRamFSVolumeOps = {
2147	&ramfs_unmount,
2148	&ramfs_read_fs_info,
2149	&ramfs_write_fs_info,
2150	&ramfs_sync,
2151	&ramfs_get_vnode,
2152
2153	/* index directory & index operations */
2154	&ramfs_open_index_dir,
2155	&ramfs_close_index_dir,
2156	&ramfs_free_index_dir_cookie,
2157	&ramfs_read_index_dir,
2158	&ramfs_rewind_index_dir,
2159
2160	&ramfs_create_index,
2161	&ramfs_remove_index,
2162	&ramfs_read_index_stat,
2163
2164	/* query operations */
2165	&ramfs_open_query,
2166	&ramfs_close_query,
2167	&ramfs_free_query_cookie,
2168	&ramfs_read_query,
2169	NULL	// rewind_query
2170};
2171
2172
2173fs_vnode_ops gRamFSVnodeOps = {
2174	/* vnode operations */
2175	&ramfs_lookup,			// lookup
2176	NULL,					// get name
2177	&ramfs_write_vnode,		// write
2178	&ramfs_remove_vnode,	// remove
2179
2180	/* VM file access */
2181	NULL,					// can_page
2182	NULL,					// read pages
2183	NULL,					// write pages
2184
2185	NULL,					// io?
2186	NULL,					// cancel io
2187
2188	NULL,					// get file map
2189
2190	&ramfs_ioctl,
2191	&ramfs_set_flags,
2192	NULL,   // &ramfs_select,
2193	NULL,   // &ramfs_deselect,
2194	&ramfs_fsync,
2195
2196	&ramfs_read_symlink,
2197	&ramfs_create_symlink,
2198
2199	&ramfs_link,
2200	&ramfs_unlink,
2201	&ramfs_rename,
2202
2203	&ramfs_access,
2204	&ramfs_read_stat,
2205	&ramfs_write_stat,
2206	NULL,   // &ramfs_preallocate,
2207
2208	/* file operations */
2209	&ramfs_create,
2210	&ramfs_open,
2211	&ramfs_close,
2212	&ramfs_free_cookie,
2213	&ramfs_read,
2214	&ramfs_write,
2215
2216	/* directory operations */
2217	&ramfs_create_dir,
2218	&ramfs_remove_dir,
2219	&ramfs_open_dir,
2220	&ramfs_close_dir,
2221	&ramfs_free_dir_cookie,
2222	&ramfs_read_dir,
2223	&ramfs_rewind_dir,
2224
2225	/* attribute directory operations */
2226	&ramfs_open_attr_dir,
2227	&ramfs_close_attr_dir,
2228	&ramfs_free_attr_dir_cookie,
2229	&ramfs_read_attr_dir,
2230	&ramfs_rewind_attr_dir,
2231
2232	/* attribute operations */
2233	&ramfs_create_attr,
2234	&ramfs_open_attr,
2235	&ramfs_close_attr,
2236	&ramfs_free_attr_cookie,
2237	&ramfs_read_attr,
2238	&ramfs_write_attr,
2239
2240	&ramfs_read_attr_stat,
2241	NULL,   // &ramfs_write_attr_stat,
2242	&ramfs_rename_attr,
2243	&ramfs_remove_attr,
2244
2245	/* special nodes */
2246	NULL	// create_special_node
2247};
2248
2249static file_system_module_info sRamFSModuleInfo = {
2250	{
2251		"file_systems/ramfs" B_CURRENT_FS_API_VERSION,
2252		0,
2253		ramfs_std_ops,
2254	},
2255
2256	"ramfs",				// short_name
2257	"RAM File System",		// pretty_name
2258	0						// DDM flags
2259	| B_DISK_SYSTEM_SUPPORTS_WRITING,
2260
2261	// scanning
2262	NULL,	// identify_partition()
2263	NULL,	// scan_partition()
2264	NULL,	// free_identify_partition_cookie()
2265	NULL,	// free_partition_content_cookie()
2266
2267	&ramfs_mount,
2268
2269	NULL,	// TODO : &ramfs_get_supported_operations
2270
2271	NULL,   // validate_resize
2272	NULL,   // validate_move
2273	NULL,   // validate_set_content_name
2274	NULL,   // validate_set_content_parameters
2275	NULL,   // validate_initialize,
2276
2277	/* shadow partition modification */
2278	NULL,   // shadow_changed
2279
2280	/* writing */
2281	NULL,   // defragment
2282	NULL,   // repair
2283	NULL,   // resize
2284	NULL,   // move
2285	NULL,   // set_content_name
2286	NULL,   // set_content_parameters
2287	NULL	// bfs_initialize
2288};
2289
2290module_info *modules[] = {
2291	(module_info *)&sRamFSModuleInfo,
2292	NULL,
2293};
2294
2295