1/* kernel_interface - file system interface to BeOS' vnode layer
2 *
3 * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de
4 * This file may be used under the terms of the MIT License.
5 */
6
7
8#ifndef COMPILE_FOR_R5
9#	define COMPILE_FOR_R5
10#endif
11
12#include "Debug.h"
13#include "Volume.h"
14#include "Inode.h"
15#include "Index.h"
16#include "BPlusTree.h"
17#include "Query.h"
18#include "bfs_control.h"
19
20#include <util/kernel_cpp.h>
21
22#include <string.h>
23#include <stdio.h>
24
25// BeOS vnode layer stuff
26#include <KernelExport.h>
27#ifndef _IMPEXP_KERNEL
28#	define _IMPEXP_KERNEL
29#endif
30
31#include "fsproto.h"
32#include "cache.h"
33
34#include <fs_index.h>
35#include <fs_query.h>
36
37// needed for bfs_initialize() in the fs_shell only
38#ifdef USER
39#	include "boot_block.h"
40#endif
41
42
43#ifdef USER
44#	define dprintf printf
45#endif
46
47// file system API
48static int bfs_mount(nspace_id nsid, const char *device, ulong flags,
49				void *parms, size_t len, void **data, vnode_id *vnid);
50static int bfs_unmount(void *_ns);
51static int bfs_read_fs_stat(void *_ns, struct fs_info *);
52static int bfs_write_fs_stat(void *ns, struct fs_info *, long mode);
53static int bfs_initialize(const char *devname, void *parms, size_t len);
54
55static int bfs_sync(void *ns);
56
57static int bfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node);
58static int bfs_release_vnode(void *_ns, void *_node, char r);
59static int bfs_remove_vnode(void *ns, void *node, char r);
60
61static int bfs_walk(void *_ns, void *_base, const char *file,
62				char **newpath, vnode_id *vnid);
63
64static int bfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, size_t len);
65static int bfs_setflags(void *ns, void *node, void *cookie, int flags);
66
67static int bfs_select(void *ns, void *node, void *cookie, uint8 event,
68				uint32 ref, selectsync *sync);
69static int bfs_deselect(void *ns, void *node, void *cookie, uint8 event,
70				selectsync *sync);
71static int bfs_fsync(void *ns, void *node);
72
73static int bfs_create(void *ns, void *dir, const char *name,
74				int perms, int omode, vnode_id *vnid, void **cookie);
75static int bfs_symlink(void *ns, void *dir, const char *name,
76				const char *path);
77static int bfs_link(void *ns, void *dir, const char *name, void *node);
78static int bfs_unlink(void *ns, void *dir, const char *name);
79static int bfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName);
80
81static int bfs_read_stat(void *_ns, void *_node, struct stat *st);
82static int bfs_write_stat(void *ns, void *node, struct stat *st, long mask);
83
84static int bfs_open(void *_ns, void *_node, int omode, void **cookie);
85static int bfs_read(void *_ns, void *_node, void *cookie, off_t pos,
86				void *buf, size_t *len);
87static int bfs_write(void *ns, void *node, void *cookie, off_t pos,
88				const void *buf, size_t *len);
89static int bfs_free_cookie(void *ns, void *node, void *cookie);
90static int bfs_close(void *ns, void *node, void *cookie);
91
92static int bfs_access(void *_ns, void *_node, int mode);
93static int bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize);
94
95// directory functions
96static int bfs_mkdir(void *ns, void *dir, const char *name, int perms);
97static int bfs_rmdir(void *ns, void *dir, const char *name);
98static int bfs_open_dir(void *_ns, void *_node, void **cookie);
99static int bfs_read_dir(void *_ns, void *_node, void *cookie,
100				long *num, struct dirent *dirent, size_t bufferSize);
101static int bfs_rewind_dir(void *_ns, void *_node, void *cookie);
102static int bfs_close_dir(void *_ns, void *_node, void *cookie);
103static int bfs_free_dir_cookie(void *_ns, void *_node, void *cookie);
104
105// attribute support
106static int bfs_open_attrdir(void *ns, void *node, void **cookie);
107static int bfs_close_attrdir(void *ns, void *node, void *cookie);
108static int bfs_free_attrdir_cookie(void *ns, void *node, void *cookie);
109static int bfs_rewind_attrdir(void *ns, void *node, void *cookie);
110static int bfs_read_attrdir(void *ns, void *node, void *cookie, long *num,
111				struct dirent *buf, size_t bufferSize);
112static int bfs_remove_attr(void *ns, void *node, const char *name);
113static int bfs_rename_attr(void *ns, void *node, const char *oldname,
114				const char *newname);
115static int bfs_stat_attr(void *ns, void *node, const char *name,
116				struct attr_info *buf);
117static int bfs_write_attr(void *ns, void *node, const char *name, int type,
118				const void *buf, size_t *len, off_t pos);
119static int bfs_read_attr(void *ns, void *node, const char *name, int type,
120				void *buf, size_t *len, off_t pos);
121
122// index support
123static int bfs_open_indexdir(void *ns, void **cookie);
124static int bfs_close_indexdir(void *ns, void *cookie);
125static int bfs_free_indexdir_cookie(void *ns, void *node, void *cookie);
126static int bfs_rewind_indexdir(void *ns, void *cookie);
127static int bfs_read_indexdir(void *ns, void *cookie, long *num, struct dirent *dirent,
128				size_t bufferSize);
129static int bfs_create_index(void *ns, const char *name, int type, int flags);
130static int bfs_remove_index(void *ns, const char *name);
131static int bfs_rename_index(void *ns, const char *oldname, const char *newname);
132static int bfs_stat_index(void *ns, const char *name, struct index_info *indexInfo);
133
134// query support
135static int bfs_open_query(void *ns, const char *query, ulong flags,
136				port_id port, long token, void **cookie);
137static int bfs_close_query(void *ns, void *cookie);
138static int bfs_free_query_cookie(void *ns, void *node, void *cookie);
139static int bfs_read_query(void *ns, void *cookie, long *num,
140				struct dirent *buf, size_t bufsize);
141
142// Dano compatibility (required for booting)
143static int bfs_wake_vnode(void *ns, void *node);
144static int bfs_suspend_vnode(void *ns, void *node);
145
146
147/* vnode_ops struct. Fill this in to tell the kernel how to call
148	functions in your driver.
149*/
150
151vnode_ops fs_entry =  {
152	&bfs_read_vnode,			// read_vnode
153	&bfs_release_vnode,			// write_vnode
154	&bfs_remove_vnode,			// remove_vnode
155	NULL,						// secure_vnode (not needed)
156	&bfs_walk,					// walk
157	&bfs_access,				// access
158	&bfs_create,				// create
159	&bfs_mkdir,					// mkdir
160	&bfs_symlink,				// symlink
161	&bfs_link,					// link
162	&bfs_rename,				// rename
163	&bfs_unlink,				// unlink
164	&bfs_rmdir,					// rmdir
165	&bfs_read_link,				// readlink
166	&bfs_open_dir,				// opendir
167	&bfs_close_dir,				// closedir
168	&bfs_free_dir_cookie,		// free_dircookie
169	&bfs_rewind_dir,			// rewinddir
170	&bfs_read_dir,				// readdir
171	&bfs_open,					// open file
172	&bfs_close,					// close file
173	&bfs_free_cookie,			// free cookie
174	&bfs_read,					// read file
175	&bfs_write,					// write file
176	NULL,						// readv
177	NULL,						// writev
178	&bfs_ioctl,					// ioctl
179	&bfs_setflags,				// setflags file
180	&bfs_read_stat,				// read stat
181	&bfs_write_stat,			// write stat
182	&bfs_fsync,					// fsync
183	&bfs_initialize,			// initialize
184	&bfs_mount,					// mount
185	&bfs_unmount,				// unmount
186	&bfs_sync,					// sync
187	&bfs_read_fs_stat,			// read fs stat
188	&bfs_write_fs_stat,			// write fs stat
189	&bfs_select,				// select
190	&bfs_deselect,				// deselect
191
192	&bfs_open_indexdir,			// open index dir
193	&bfs_close_indexdir,		// close index dir
194	&bfs_free_indexdir_cookie,	// free index dir cookie
195	&bfs_rewind_indexdir,		// rewind index dir
196	&bfs_read_indexdir,			// read index dir
197	&bfs_create_index,			// create index
198	&bfs_remove_index,			// remove index
199	&bfs_rename_index,			// rename index
200	&bfs_stat_index,			// stat index
201
202	&bfs_open_attrdir,			// open attr dir
203	&bfs_close_attrdir,			// close attr dir
204	&bfs_free_attrdir_cookie,	// free attr dir cookie
205	&bfs_rewind_attrdir,		// rewind attr dir
206	&bfs_read_attrdir,			// read attr dir
207	&bfs_write_attr,			// write attr
208	&bfs_read_attr,				// read attr
209	&bfs_remove_attr,			// remove attr
210	&bfs_rename_attr,			// rename attr
211	&bfs_stat_attr,				// stat attr
212
213	&bfs_open_query,			// open query
214	&bfs_close_query,			// close query
215	&bfs_free_query_cookie,		// free query cookie
216	&bfs_read_query,			// read query
217
218	&bfs_wake_vnode,			// these two are added for Dano compatibility;
219	&bfs_suspend_vnode			// they do nothing.
220};
221
222#define BFS_IO_SIZE	65536
223
224// ToDo: has to change to "bfs" later
225#ifdef BFS_REPLACEMENT
226#	define BFS_NAME	"bfs"
227#else
228#	define BFS_NAME "obfs"
229#endif
230
231int32	api_version = B_CUR_FS_API_VERSION;
232
233
234static int
235bfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms,
236		size_t len, void **data, vnode_id *rootID)
237{
238	FUNCTION();
239
240	Volume *volume = new Volume(nsid);
241	if (volume == NULL)
242		return B_NO_MEMORY;
243
244	status_t status;
245	if ((status = volume->Mount(device, flags)) == B_OK) {
246		*data = volume;
247		*rootID = volume->ToVnode(volume->Root());
248		INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n",
249			volume->Name(), *rootID, device));
250
251#ifdef DEBUG
252		add_debugger_commands();
253#endif
254	}
255	else
256		delete volume;
257
258	RETURN_ERROR(status);
259}
260
261
262static int
263bfs_unmount(void *ns)
264{
265	FUNCTION();
266	Volume* volume = (Volume *)ns;
267
268	status_t status = volume->Unmount();
269	delete volume;
270
271#ifdef DEBUG
272	remove_debugger_commands();
273#endif
274	RETURN_ERROR(status);
275}
276
277
278/**	Fill in bfs_info struct for device.
279 */
280
281static int
282bfs_read_fs_stat(void *_ns, struct fs_info *info)
283{
284	FUNCTION();
285	if (_ns == NULL || info == NULL)
286		return B_BAD_VALUE;
287
288	Volume *volume = (Volume *)_ns;
289
290	RecursiveLocker locker(volume->Lock());
291
292	// File system flags.
293	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY |
294			(volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
295
296	info->io_size = BFS_IO_SIZE;
297		// whatever is appropriate here? Just use the same value as BFS (and iso9660) for now
298
299	info->block_size = volume->BlockSize();
300	info->total_blocks = volume->NumBlocks();
301	info->free_blocks = volume->FreeBlocks();
302
303	// Volume name
304	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
305
306	// File system name
307	strlcpy(info->fsh_name, BFS_NAME, sizeof(info->fsh_name));
308
309	return B_OK;
310}
311
312
313static int
314bfs_write_fs_stat(void *_ns, struct fs_info *info, long mask)
315{
316	FUNCTION_START(("mask = %ld\n", mask));
317
318	Volume *volume = (Volume *)_ns;
319	disk_super_block &superBlock = volume->SuperBlock();
320
321	RecursiveLocker locker(volume->Lock());
322
323	status_t status = B_BAD_VALUE;
324
325	if (mask & WFSSTAT_NAME) {
326		strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1);
327		superBlock.name[sizeof(superBlock.name) - 1] = '\0';
328
329		status = volume->WriteSuperBlock();
330	}
331	return status;
332}
333
334
335static int
336bfs_initialize(const char *deviceName, void *parms, size_t len)
337{
338	FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len));
339
340	// This function is not available from the outside in BeOS
341	// It will be similarly implemented in OpenBeOS, though - the
342	// backend (to create the file system) is already done; just
343	// call Volume::Initialize().
344
345	// When compiling the userland bfs_shell, we provide a useful
346	// implementation for this function anyway, so that the bfs_shell can
347	// initialize images.
348
349#ifndef USER
350
351	return B_ERROR;
352
353#else // USER
354
355	if (deviceName == NULL)
356		return B_BAD_VALUE;
357
358	char **argv = (char**)parms;
359
360	uint32 blockSize = 1024;
361	uint32 flags = 0;
362	bool verbose = false;
363
364	while (argv && *++argv) {
365		char *arg = *argv;
366		if (*arg == '-') {
367			if (arg[1] == '-') {
368				if (!strcmp(arg, "--noindex"))
369					flags |= VOLUME_NO_INDICES;
370				else
371					return B_BAD_VALUE;
372			}
373
374			while (*++arg && *arg >= 'a' && *arg <= 'z') {
375				switch (*arg) {
376					case 'v':
377						verbose = true;
378						break;
379					case 'b':
380						if (*++argv == NULL || !(**argv >= '0' && **argv <= '9'))
381							return B_BAD_VALUE;
382
383						blockSize = atol(*argv);
384						break;
385					case 'n':
386						flags |= VOLUME_NO_INDICES;
387						break;
388				}
389			}
390		}
391		else
392			break;
393	}
394
395	char *volumeName = "Unnamed";
396	if (argv[0] != NULL)
397		volumeName = argv[0];
398
399	if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) {
400		FATAL(("valid block sizes are: 1024, 2048, 4096, and 8192\n"));
401		return -1;
402	}
403
404	Volume volume(2);
405		// we just happen to know it's 2...
406	status_t status = volume.Initialize(deviceName, volumeName, blockSize, flags);
407	if (status < B_OK) {
408		FATAL(("Initializing volume failed: %s\n", strerror(status)));
409		return -1;
410	}
411
412	if (verbose) {
413		disk_super_block super = volume.SuperBlock();
414
415		INFORM(("Disk was initialized successfully.\n"));
416		INFORM(("\tname: \"%s\"\n", super.name));
417		INFORM(("\tnum blocks: %Ld\n", super.NumBlocks()));
418		INFORM(("\tused blocks: %Ld\n", super.UsedBlocks()));
419		INFORM(("\tblock size: %lu bytes\n", super.BlockSize()));
420		INFORM(("\tnum allocation groups: %ld\n", super.AllocationGroups()));
421		INFORM(("\tallocation group size: %ld blocks\n", 1L << super.AllocationGroupShift()));
422		INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length()));
423	}
424
425	// make the disk image bootable
426
427	int device = open(deviceName, O_RDWR);
428	if (device < 0) {
429		FATAL(("Could not make image bootable: Failed to open \"%s\"\n",
430			deviceName));
431		return B_FILE_ERROR;
432	}
433
434	// change BIOS drive and partition offset
435	// ToDo: for now, this will only work for images only
436
437	sBootBlockData1[kBIOSDriveOffset] = 0x80;
438		// for now, this should be replaced by the real thing
439	uint32 *offset = (uint32 *)&sBootBlockData1[kPartitionDataOffset];
440	*offset = 0;
441
442	write_pos(device, 0, sBootBlockData1, kBootBlockData1Size);
443	write_pos(device, kBootBlockData2Offset, sBootBlockData2, kBootBlockData2Size);
444
445	close(device);
446
447	return B_OK;
448
449#endif	// USER
450}
451
452
453static int
454bfs_sync(void *_ns)
455{
456	FUNCTION();
457	if (_ns == NULL)
458		return B_BAD_VALUE;
459
460	Volume *volume = (Volume *)_ns;
461
462	return volume->Sync();
463}
464
465
466//	#pragma mark -
467
468
469/**	Reads in the node from disk and creates an inode object from it.
470 *	Has to be cautious if the node in question is currently under
471 *	construction, in which case it waits for that action to be completed,
472 *	and uses the inode object from the construction instead of creating
473 *	a new one.
474 *	ToDo: Must not be called without the volume lock being held. Actually,
475 *	this might even happen with the BDirectory(node_ref *) constructor
476 *	(at least I think so, I haven't tested it yet), so we should better
477 *	test this. Fortunately, we can easily solve the issue with our kernel.
478 */
479
480static int
481bfs_read_vnode(void *_ns, vnode_id id, char reenter, void **_node)
482{
483	FUNCTION_START(("vnode_id = %Ld\n", id));
484	Volume *volume = (Volume *)_ns;
485
486	if (id < 0 || id > volume->NumBlocks()) {
487		FATAL(("inode at %Ld requested!\n", id));
488		return B_ERROR;
489	}
490
491	CachedBlock cached(volume, id);
492
493	bfs_inode *node = (bfs_inode *)cached.Block();
494	Inode *inode = NULL;
495	int32 tries = 0;
496
497restartIfBusy:
498	status_t status = node->InitCheck(volume);
499
500	if (status == B_BUSY) {
501		inode = (Inode *)node->etc;
502			// We have to use the "etc" field to get the inode object
503			// (the inode is currently being constructed)
504			// We would need to call something like get_vnode() again here
505			// to get rid of the "etc" field - which would be nice, especially
506			// for other file systems which don't have this messy field.
507
508		// let us wait a bit and try again later
509		if (tries++ < 200) {
510			// wait for one second at maximum
511			snooze(5000);
512			goto restartIfBusy;
513		}
514		FATAL(("inode is not becoming unbusy (id = %Ld)\n", id));
515		return status;
516	} else if (status < B_OK) {
517		FATAL(("inode at %Ld is corrupt!\n", id));
518		return status;
519	}
520
521	// If the inode is currently being constructed, we already have an inode
522	// pointer (taken from the node's etc field).
523	// If not, we create a new one here
524
525	if (inode == NULL) {
526		inode = new Inode(&cached);
527		if (inode == NULL)
528			return B_NO_MEMORY;
529
530		status = inode->InitCheck(false);
531		if (status < B_OK)
532			delete inode;
533	} else
534		status = inode->InitCheck(false);
535
536	if (status == B_OK)
537		*_node = inode;
538
539	return status;
540}
541
542
543static int
544bfs_release_vnode(void *ns, void *_node, char reenter)
545{
546	//FUNCTION_START(("node = %p\n", _node));
547	Inode *inode = (Inode *)_node;
548
549	delete inode;
550
551	return B_NO_ERROR;
552}
553
554
555static int
556bfs_remove_vnode(void *_ns, void *_node, char reenter)
557{
558	FUNCTION();
559
560	if (_ns == NULL || _node == NULL)
561		return B_BAD_VALUE;
562
563	Volume *volume = (Volume *)_ns;
564	Inode *inode = (Inode *)_node;
565
566	// The "chkbfs" functionality uses this flag to prevent the space used
567	// up by the inode from being freed - this flag is set only in situations
568	// where this is a good idea... (the block bitmap will get fixed anyway
569	// in this case).
570	if (inode->Flags() & INODE_DONT_FREE_SPACE) {
571		delete inode;
572		return B_OK;
573	}
574
575	// If the inode isn't in use anymore, we were called before
576	// bfs_unlink() returns - in this case, we can just use the
577	// transaction which has already deleted the inode.
578	Transaction localTransaction, *transaction = NULL;
579
580	Journal *journal = volume->GetJournal(volume->ToBlock(inode->Parent()));
581	if (journal != NULL)
582		transaction = journal->CurrentTransaction();
583
584	if (transaction == NULL) {
585		transaction = &localTransaction;
586		localTransaction.Start(volume, inode->BlockNumber());
587	}
588
589	status_t status = inode->Free(transaction);
590	if (status == B_OK) {
591		if (transaction == &localTransaction)
592			localTransaction.Done();
593
594		delete inode;
595	}
596
597	return status;
598}
599
600
601static int
602bfs_wake_vnode(void *_ns, void *_node)
603{
604	return B_OK;
605}
606
607
608static int
609bfs_suspend_vnode(void *_ns, void *_node)
610{
611	return B_OK;
612}
613
614
615//	#pragma mark -
616
617
618/**	the walk function just "walks" through a directory looking for the
619 *	specified file. It calls get_vnode() on its vnode-id to init it
620 *	for the kernel.
621 */
622
623static int
624bfs_walk(void *_ns, void *_directory, const char *file, char **_resolvedPath, vnode_id *_vnodeID)
625{
626	//FUNCTION_START(("file = %s\n", file));
627	if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL)
628		return B_BAD_VALUE;
629
630	Volume *volume = (Volume *)_ns;
631	Inode *directory = (Inode *)_directory;
632
633	// check access permissions
634	status_t status = directory->CheckPermissions(R_OK);
635	if (status < B_OK)
636		RETURN_ERROR(status);
637
638	BPlusTree *tree;
639	if (directory->GetTree(&tree) != B_OK)
640		RETURN_ERROR(B_BAD_VALUE);
641
642	if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) {
643		PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
644		return status;
645	}
646
647	RecursiveLocker locker(volume->Lock());
648		// we have to hold the volume lock in order to not
649		// interfere with new_vnode() here
650
651	Inode *inode;
652	if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) {
653		REPORT_ERROR(status);
654		return B_ENTRY_NOT_FOUND;
655	}
656
657	// Is inode a symlink? Then resolve it, if we should
658
659	if (inode->IsSymLink() && _resolvedPath != NULL) {
660		status_t status = B_OK;
661		char *newPath = NULL;
662
663		// Symbolic links can store their target in the data stream (for links
664		// that take more than 144 bytes of storage [the size of the data_stream
665		// structure]), or directly instead of the data_stream class
666		// So we have to deal with both cases here.
667
668		// Note: we would naturally call bfs_read_link() here, but the API of the
669		// vnode layer would require us to always reserve a large chunk of memory
670		// for the path, so we're not going to do that
671
672		if (inode->Flags() & INODE_LONG_SYMLINK) {
673			size_t readBytes = inode->Size();
674			char *data = (char *)malloc(readBytes);
675			if (data != NULL) {
676				status = inode->ReadAt(0, (uint8 *)data, &readBytes);
677				if (status == B_OK && readBytes == inode->Size())
678					status = new_path(data, &newPath);
679
680				free(data);
681			} else
682				status = B_NO_MEMORY;
683		} else
684			status = new_path((char *)&inode->Node()->short_symlink, &newPath);
685
686		put_vnode(volume->ID(), inode->ID());
687		if (status == B_OK)
688			*_resolvedPath = newPath;
689
690		RETURN_ERROR(status);
691	}
692
693	return B_OK;
694}
695
696
697static int
698bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t bufferLength)
699{
700	FUNCTION_START(("node = %p, cmd = %d, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength));
701
702	if (_ns == NULL)
703		return B_BAD_VALUE;
704
705	Volume *volume = (Volume *)_ns;
706	Inode *inode = (Inode *)_node;
707
708	switch (cmd) {
709		case IOCTL_FILE_UNCACHED_IO:
710		{
711			if (inode == NULL)
712				return B_BAD_VALUE;
713
714			// if the inode is already set up for uncached access, bail out
715			if (inode->Flags() & INODE_NO_CACHE) {
716				FATAL(("File %Ld is already uncached\n", inode->ID()));
717				return B_ERROR;
718			}
719
720			PRINT(("uncached access to inode %Ld\n", inode->ID()));
721
722			// ToDo: sync the cache for this file!
723			// Unfortunately, we can't remove any blocks from the cache in BeOS,
724			// that means we can't guarantee consistency for the file contents
725			// spanning over both access modes.
726
727			// request buffers for being able to access the file without
728			// using the cache or allocating memory
729			status_t status = volume->Pool().RequestBuffers(volume->BlockSize());
730			if (status == B_OK)
731				inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_NO_CACHE);
732			return status;
733		}
734		case IOCTL_CREATE_TIME:
735		{
736			if (inode == NULL || buffer == NULL)
737				return B_BAD_VALUE;
738
739			off_t *creationTime = (off_t *)buffer;
740			*creationTime = inode->Node()->CreateTime();
741			return B_OK;
742		}
743		case IOCTL_MODIFIED_TIME:
744		{
745			if (inode == NULL || buffer == NULL)
746				return B_BAD_VALUE;
747
748			off_t *modifiedTime = (off_t *)buffer;
749			*modifiedTime = inode->LastModified();
750			return B_OK;
751		}
752		case BFS_IOCTL_VERSION:
753		{
754			uint32 *version = (uint32 *)buffer;
755
756			*version = 0x10000;
757			return B_OK;
758		}
759		case BFS_IOCTL_START_CHECKING:
760		{
761			// start checking
762			BlockAllocator &allocator = volume->Allocator();
763			check_control *control = (check_control *)buffer;
764
765			status_t status = allocator.StartChecking(control);
766			if (status == B_OK && inode != NULL)
767				inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING);
768
769			return status;
770		}
771		case BFS_IOCTL_STOP_CHECKING:
772		{
773			// stop checking
774			BlockAllocator &allocator = volume->Allocator();
775			check_control *control = (check_control *)buffer;
776
777			status_t status = allocator.StopChecking(control);
778			if (status == B_OK && inode != NULL)
779				inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING);
780
781			return status;
782		}
783		case BFS_IOCTL_CHECK_NEXT_NODE:
784		{
785			// check next
786			BlockAllocator &allocator = volume->Allocator();
787			check_control *control = (check_control *)buffer;
788
789			return allocator.CheckNextNode(control);
790		}
791#ifdef DEBUG
792		case 56742:
793		{
794			// allocate all free blocks and zero them out (a test for the BlockAllocator)!
795			BlockAllocator &allocator = volume->Allocator();
796			Transaction transaction(volume, 0);
797			CachedBlock cached(volume);
798			block_run run;
799			while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) {
800				PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length));
801				for (int32 i = 0;i < run.length;i++) {
802					uint8 *block = cached.SetTo(run);
803					if (block != NULL) {
804						memset(block, 0, volume->BlockSize());
805						cached.WriteBack(&transaction);
806					}
807				}
808			}
809			return B_OK;
810		}
811		case 56743:
812			dump_super_block(&volume->SuperBlock());
813			return B_OK;
814		case 56744:
815			if (inode != NULL)
816				dump_inode(inode->Node());
817			return B_OK;
818		case 56745:
819			if (inode != NULL)
820				dump_block((const char *)inode->Node(), volume->BlockSize());
821			return B_OK;
822#endif
823	}
824	return B_BAD_VALUE;
825}
826
827
828/** Sets the open-mode flags for the open file cookie - only
829 *	supports O_APPEND currently, but that should be sufficient
830 *	for a file system.
831 */
832
833static int
834bfs_setflags(void *_ns, void *_node, void *_cookie, int flags)
835{
836	FUNCTION_START(("node = %p, flags = %d", _node, flags));
837
838	file_cookie *cookie = (file_cookie *)_cookie;
839	cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
840
841	return B_OK;
842}
843
844
845int
846bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync)
847{
848	FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync));
849	notify_select_event(sync, ref);
850
851	return B_OK;
852}
853
854
855static int
856bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync)
857{
858	FUNCTION();
859	return B_OK;
860}
861
862
863static int
864bfs_fsync(void *_ns, void *_node)
865{
866	FUNCTION();
867	if (_node == NULL)
868		return B_BAD_VALUE;
869
870	Inode *inode = (Inode *)_node;
871	return inode->Sync();
872}
873
874
875/**	Fills in the stat struct for a node
876 */
877
878static int
879bfs_read_stat(void *_ns, void *_node, struct stat *st)
880{
881	FUNCTION();
882
883	Volume *volume = (Volume *)_ns;
884	Inode *inode = (Inode *)_node;
885	bfs_inode *node = inode->Node();
886
887	st->st_dev = volume->ID();
888	st->st_ino = inode->ID();
889	st->st_nlink = 1;
890	st->st_blksize = BFS_IO_SIZE;
891
892	st->st_uid = node->UserID();
893	st->st_gid = node->GroupID();
894	st->st_mode = node->Mode();
895	st->st_size = node->data.Size();
896
897	st->st_atime = time(NULL);
898	st->st_mtime = st->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT);
899	st->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT);
900
901	return B_NO_ERROR;
902}
903
904
905static int
906bfs_write_stat(void *_ns, void *_node, struct stat *stat, long mask)
907{
908	FUNCTION();
909
910	if (_ns == NULL || _node == NULL || stat == NULL)
911		RETURN_ERROR(B_BAD_VALUE);
912
913	Volume *volume = (Volume *)_ns;
914	Inode *inode = (Inode *)_node;
915
916	// that may be incorrect here - I don't think we need write access to
917	// change most of the stat...
918	// we should definitely check a bit more if the new stats are correct and valid...
919
920	status_t status = inode->CheckPermissions(W_OK);
921	if (status < B_OK)
922		RETURN_ERROR(status);
923
924#ifdef UNSAFE_GET_VNODE
925	RecursiveLocker locker(volume->Lock());
926#endif
927
928	WriteLocked locked(inode->Lock());
929	if (locked.IsLocked() < B_OK)
930		RETURN_ERROR(B_ERROR);
931
932	Transaction transaction(volume, inode->BlockNumber());
933
934	bfs_inode *node = inode->Node();
935
936	if (mask & WSTAT_SIZE) {
937		// Since WSTAT_SIZE is the only thing that can fail directly, we
938		// do it first, so that the inode state will still be consistent
939		// with the on-disk version
940		if (inode->IsDirectory())
941			return B_IS_A_DIRECTORY;
942
943		if (inode->Size() != stat->st_size) {
944			status = inode->SetFileSize(&transaction, stat->st_size);
945			if (status < B_OK)
946				return status;
947
948			// fill the new blocks (if any) with zeros
949			inode->FillGapWithZeros(inode->OldSize(), inode->Size());
950
951			Index index(volume);
952			index.UpdateSize(&transaction, inode);
953
954			if ((mask & WSTAT_MTIME) == 0)
955				index.UpdateLastModified(&transaction, inode);
956		}
957	}
958
959	if (mask & WSTAT_MODE) {
960		PRINT(("original mode = %ld, stat->st_mode = %d\n", node->mode, stat->st_mode));
961		node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK;
962	}
963
964	if (mask & WSTAT_UID)
965		node->uid = stat->st_uid;
966	if (mask & WSTAT_GID)
967		node->gid = stat->st_gid;
968
969	if (mask & WSTAT_MTIME) {
970		if (inode->IsDirectory()) {
971			// directory modification times are not part of the index
972			node->last_modified_time = HOST_ENDIAN_TO_BFS_INT64(
973				(bigtime_t)stat->st_mtime << INODE_TIME_SHIFT);
974		} else if (!inode->IsDeleted()) {
975			// Index::UpdateLastModified() will set the new time in the inode
976			Index index(volume);
977			index.UpdateLastModified(&transaction, inode,
978				(bigtime_t)stat->st_mtime << INODE_TIME_SHIFT);
979		}
980	}
981	if (mask & WSTAT_CRTIME) {
982		node->create_time = HOST_ENDIAN_TO_BFS_INT64(
983			(bigtime_t)stat->st_crtime << INODE_TIME_SHIFT);
984	}
985
986	if ((status = inode->WriteBack(&transaction)) == B_OK)
987		transaction.Done();
988
989	notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL);
990
991	return status;
992}
993
994
995int
996bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode,
997	vnode_id *vnodeID, void **_cookie)
998{
999	FUNCTION_START(("name = \"%s\", perms = %d, omode = %d\n", name, mode, omode));
1000
1001	if (_ns == NULL || _directory == NULL || _cookie == NULL
1002		|| name == NULL || *name == '\0')
1003		RETURN_ERROR(B_BAD_VALUE);
1004
1005	Volume *volume = (Volume *)_ns;
1006	Inode *directory = (Inode *)_directory;
1007
1008	if (!directory->IsDirectory())
1009		RETURN_ERROR(B_BAD_TYPE);
1010
1011	status_t status = directory->CheckPermissions(W_OK);
1012	if (status < B_OK)
1013		RETURN_ERROR(status);
1014
1015	// We are creating the cookie at this point, so that we don't have
1016	// to remove the inode if we don't have enough free memory later...
1017	file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
1018	if (cookie == NULL)
1019		RETURN_ERROR(B_NO_MEMORY);
1020
1021	// initialize the cookie
1022	cookie->open_mode = omode;
1023	cookie->last_size = 0;
1024	cookie->last_notification = system_time();
1025
1026#ifdef UNSAFE_GET_VNODE
1027	RecursiveLocker locker(volume->Lock());
1028#endif
1029	Transaction transaction(volume, directory->BlockNumber());
1030
1031	status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK),
1032		omode, 0, vnodeID);
1033
1034	if (status >= B_OK) {
1035		transaction.Done();
1036
1037		// register the cookie
1038		*_cookie = cookie;
1039
1040		notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, *vnodeID, name);
1041	} else
1042		free(cookie);
1043
1044	return status;
1045}
1046
1047
1048int
1049bfs_symlink(void *_ns, void *_directory, const char *name, const char *path)
1050{
1051	FUNCTION();
1052
1053	if (_ns == NULL || _directory == NULL || path == NULL
1054		|| name == NULL || *name == '\0')
1055		RETURN_ERROR(B_BAD_VALUE);
1056
1057	Volume *volume = (Volume *)_ns;
1058	Inode *directory = (Inode *)_directory;
1059
1060	if (!directory->IsDirectory())
1061		RETURN_ERROR(B_BAD_TYPE);
1062
1063	status_t status = directory->CheckPermissions(W_OK);
1064	if (status < B_OK)
1065		RETURN_ERROR(status);
1066
1067#ifdef UNSAFE_GET_VNODE
1068	RecursiveLocker locker(volume->Lock());
1069#endif
1070	Transaction transaction(volume, directory->BlockNumber());
1071
1072	Inode *link;
1073	off_t id;
1074	status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link);
1075	if (status < B_OK)
1076		RETURN_ERROR(status);
1077
1078	size_t length = strlen(path);
1079	if (length < SHORT_SYMLINK_NAME_LENGTH) {
1080		strcpy(link->Node()->short_symlink, path);
1081		status = link->WriteBack(&transaction);
1082	} else {
1083		link->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED);
1084		// The following call will have to write the inode back, so
1085		// we don't have to do that here...
1086		status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length);
1087	}
1088	// ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY
1089	//	flag set until here, so that it can be accessed directly
1090
1091	// Inode::Create() left the inode locked in memory
1092	put_vnode(volume->ID(), id);
1093
1094	if (status == B_OK) {
1095		transaction.Done();
1096
1097		notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name);
1098	}
1099
1100	return status;
1101}
1102
1103
1104int
1105bfs_link(void *ns, void *dir, const char *name, void *node)
1106{
1107	FUNCTION_START(("name = \"%s\"\n", name));
1108
1109	// This one won't be implemented in a binary compatible BFS
1110
1111	return B_ERROR;
1112}
1113
1114
1115int
1116bfs_unlink(void *_ns, void *_directory, const char *name)
1117{
1118	FUNCTION_START(("name = \"%s\"\n", name));
1119
1120	if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0')
1121		return B_BAD_VALUE;
1122	if (!strcmp(name, "..") || !strcmp(name, "."))
1123		return B_NOT_ALLOWED;
1124
1125	Volume *volume = (Volume *)_ns;
1126	Inode *directory = (Inode *)_directory;
1127
1128	status_t status = directory->CheckPermissions(W_OK);
1129	if (status < B_OK)
1130		return status;
1131
1132#ifdef UNSAFE_GET_VNODE
1133	RecursiveLocker locker(volume->Lock());
1134#endif
1135	Transaction transaction(volume, directory->BlockNumber());
1136
1137	off_t id;
1138	if ((status = directory->Remove(&transaction, name, &id)) == B_OK) {
1139		transaction.Done();
1140
1141		notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL);
1142	}
1143	return status;
1144}
1145
1146
1147int
1148bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName)
1149{
1150	FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName));
1151
1152	// there might be some more tests needed?!
1153	if (_ns == NULL || _oldDir == NULL || _newDir == NULL
1154		|| oldName == NULL || *oldName == '\0'
1155		|| newName == NULL || *newName == '\0'
1156		|| !strcmp(oldName, ".") || !strcmp(oldName, "..")
1157		|| !strcmp(newName, ".") || !strcmp(newName, "..")
1158		|| strchr(newName, '/') != NULL)
1159		RETURN_ERROR(B_BAD_VALUE);
1160
1161	Volume *volume = (Volume *)_ns;
1162	Inode *oldDirectory = (Inode *)_oldDir;
1163	Inode *newDirectory = (Inode *)_newDir;
1164
1165	// are we already done?
1166	if (oldDirectory == newDirectory && !strcmp(oldName, newName))
1167		return B_OK;
1168
1169	// are we allowed to do what we've been told?
1170	status_t status = oldDirectory->CheckPermissions(W_OK);
1171	if (status == B_OK)
1172		status = newDirectory->CheckPermissions(W_OK);
1173	if (status < B_OK)
1174		return status;
1175
1176	RecursiveLocker locker(volume->Lock());
1177
1178	// get the directory's tree, and a pointer to the inode which should be changed
1179	BPlusTree *tree;
1180	status = oldDirectory->GetTree(&tree);
1181	if (status < B_OK)
1182		RETURN_ERROR(status);
1183
1184	off_t id;
1185	status = tree->Find((const uint8 *)oldName, strlen(oldName), &id);
1186	if (status < B_OK)
1187		RETURN_ERROR(status);
1188
1189	Vnode vnode(volume, id);
1190	Inode *inode;
1191	if (vnode.Get(&inode) < B_OK)
1192		return B_IO_ERROR;
1193
1194	// Don't move a directory into one of its children - we soar up
1195	// from the newDirectory to either the root node or the old
1196	// directory, whichever comes first.
1197	// If we meet our inode on that way, we have to bail out.
1198
1199	if (oldDirectory != newDirectory) {
1200		vnode_id parent = volume->ToVnode(newDirectory->Parent());
1201		vnode_id root = volume->RootNode()->ID();
1202
1203		while (true) {
1204			if (parent == id)
1205				return B_BAD_VALUE;
1206			else if (parent == root || parent == oldDirectory->ID())
1207				break;
1208
1209			Vnode vnode(volume, parent);
1210			Inode *parentNode;
1211			if (vnode.Get(&parentNode) < B_OK)
1212				return B_ERROR;
1213
1214			parent = volume->ToVnode(parentNode->Parent());
1215		}
1216	}
1217
1218	// Everything okay? Then lets get to work...
1219
1220	Transaction transaction(volume, oldDirectory->BlockNumber());
1221
1222	// First, try to make sure there is nothing that will stop us in
1223	// the target directory - since this is the only non-critical
1224	// failure, we will test this case first
1225	BPlusTree *newTree = tree;
1226	if (newDirectory != oldDirectory) {
1227		status = newDirectory->GetTree(&newTree);
1228		if (status < B_OK)
1229			RETURN_ERROR(status);
1230	}
1231
1232	status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id);
1233	if (status == B_NAME_IN_USE) {
1234		// If there is already a file with that name, we have to remove
1235		// it, as long it's not a directory with files in it
1236		off_t clobber;
1237		if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK)
1238			return B_NAME_IN_USE;
1239		if (clobber == id)
1240			return B_BAD_VALUE;
1241
1242		Vnode vnode(volume, clobber);
1243		Inode *other;
1244		if (vnode.Get(&other) < B_OK)
1245			return B_NAME_IN_USE;
1246
1247		status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory());
1248		if (status < B_OK)
1249			return status;
1250
1251		notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL);
1252
1253		status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id);
1254	}
1255	if (status < B_OK)
1256		return status;
1257
1258	// If anything fails now, we have to remove the inode from the
1259	// new directory in any case to restore the previous state
1260	status_t bailStatus = B_OK;
1261
1262	// update the name only when they differ
1263	bool nameUpdated = false;
1264	if (strcmp(oldName, newName)) {
1265		status = inode->SetName(&transaction, newName);
1266		if (status == B_OK) {
1267			Index index(volume);
1268			index.UpdateName(&transaction, oldName, newName, inode);
1269			nameUpdated = true;
1270		}
1271	}
1272
1273	if (status == B_OK) {
1274		status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id);
1275		if (status == B_OK) {
1276			inode->Node()->parent = newDirectory->BlockRun();
1277
1278			// if it's a directory, update the parent directory pointer
1279			// in its tree if necessary
1280			BPlusTree *movedTree = NULL;
1281			if (oldDirectory != newDirectory
1282				&& inode->IsDirectory()
1283				&& (status = inode->GetTree(&movedTree)) == B_OK)
1284				status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID());
1285
1286			if (status == B_OK) {
1287				status = inode->WriteBack(&transaction);
1288				if (status == B_OK)	{
1289					transaction.Done();
1290
1291					notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(),
1292						newDirectory->ID(), id, newName);
1293					return B_OK;
1294				}
1295			}
1296			// If we get here, something has gone wrong already!
1297
1298			// Those better don't fail, or we switch to a read-only
1299			// device for safety reasons (Volume::Panic() does this
1300			// for us)
1301			// Anyway, if we overwrote a file in the target directory
1302			// this is lost now (only in-memory, not on-disk)...
1303			bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id);
1304			if (movedTree != NULL)
1305				movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID());
1306		}
1307	}
1308
1309	if (bailStatus == B_OK && nameUpdated) {
1310		bailStatus = inode->SetName(&transaction, oldName);
1311		if (status == B_OK) {
1312			// update inode and index
1313			inode->WriteBack(&transaction);
1314
1315			Index index(volume);
1316			index.UpdateName(&transaction, newName, oldName, inode);
1317		}
1318	}
1319
1320	if (bailStatus == B_OK)
1321		bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id);
1322
1323	if (bailStatus < B_OK)
1324		volume->Panic();
1325
1326	return status;
1327}
1328
1329
1330/**	Opens the file with the specified mode.
1331 */
1332
1333static int
1334bfs_open(void *_ns, void *_node, int omode, void **_cookie)
1335{
1336	FUNCTION();
1337	if (_ns == NULL || _node == NULL || _cookie == NULL)
1338		RETURN_ERROR(B_BAD_VALUE);
1339
1340	Volume *volume = (Volume *)_ns;
1341	Inode *inode = (Inode *)_node;
1342
1343	// we can't open a file which uses uncached access twice
1344	if (inode->Flags() & INODE_NO_CACHE)
1345		return B_BUSY;
1346
1347	// opening a directory read-only is allowed, although you can't read
1348	// any data from it.
1349	if (inode->IsDirectory() && omode & O_RWMASK) {
1350		omode = omode & ~O_RWMASK;
1351		// ToDo: for compatibility reasons, we don't return an error here...
1352		// e.g. "copyattr" tries to do that
1353		//return B_IS_A_DIRECTORY;
1354	}
1355
1356	status_t status = inode->CheckPermissions(oModeToAccess(omode));
1357	if (status < B_OK)
1358		RETURN_ERROR(status);
1359
1360	// we could actually use the cookie to keep track of:
1361	//	- the last block_run
1362	//	- the location in the data_stream (indirect, double indirect,
1363	//	  position in block_run array)
1364	//
1365	// This could greatly speed up continuous reads of big files, especially
1366	// in the indirect block section.
1367
1368	file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
1369	if (cookie == NULL)
1370		RETURN_ERROR(B_NO_MEMORY);
1371
1372	// initialize the cookie
1373	cookie->open_mode = omode;
1374		// needed by e.g. bfs_write() for O_APPEND
1375	cookie->last_size = inode->Size();
1376	cookie->last_notification = system_time();
1377
1378	// Should we truncate the file?
1379	if (omode & O_TRUNC) {
1380		WriteLocked locked(inode->Lock());
1381		Transaction transaction(volume, inode->BlockNumber());
1382
1383		status_t status = inode->SetFileSize(&transaction, 0);
1384		if (status < B_OK) {
1385			// bfs_free_cookie() is only called if this function is successful
1386			free(cookie);
1387			return status;
1388		}
1389
1390		transaction.Done();
1391	}
1392
1393	*_cookie = cookie;
1394	return B_OK;
1395}
1396
1397
1398/**	Read a file specified by node, using information in cookie
1399 *	and at offset specified by pos. read len bytes into buffer buf.
1400 */
1401
1402static int
1403bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length)
1404{
1405	//FUNCTION();
1406	Inode *inode = (Inode *)_node;
1407
1408	if (!inode->HasUserAccessableStream()) {
1409		*_length = 0;
1410		RETURN_ERROR(B_BAD_VALUE);
1411	}
1412
1413	ReadLocked locked(inode->Lock());
1414	return inode->ReadAt(pos, (uint8 *)buffer, _length);
1415}
1416
1417
1418int
1419bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length)
1420{
1421	//FUNCTION();
1422	// uncomment to be more robust against a buggy vnode layer ;-)
1423	//if (_ns == NULL || _node == NULL || _cookie == NULL)
1424	//	return B_BAD_VALUE;
1425
1426	Volume *volume = (Volume *)_ns;
1427	Inode *inode = (Inode *)_node;
1428
1429	if (!inode->HasUserAccessableStream()) {
1430		*_length = 0;
1431		RETURN_ERROR(B_BAD_VALUE);
1432	}
1433
1434	file_cookie *cookie = (file_cookie *)_cookie;
1435
1436	if (cookie->open_mode & O_APPEND)
1437		pos = inode->Size();
1438
1439	WriteLocked locked(inode->Lock());
1440	if (locked.IsLocked() < B_OK)
1441		RETURN_ERROR(B_ERROR);
1442
1443	Transaction transaction;
1444		// We are not starting the transaction here, since
1445		// it might not be needed at all (the contents of
1446		// regular files aren't logged)
1447
1448	status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length);
1449
1450	if (status == B_OK)
1451		transaction.Done();
1452
1453	if (status == B_OK && (inode->Flags() & INODE_NO_CACHE) == 0) {
1454		// uncached files don't cause notifications during access, and
1455		// never want to write back any cached blocks
1456
1457		// periodically notify if the file size has changed
1458		// ToDo: should we better test for a change in the last_modified time only?
1459		if (cookie->last_size != inode->Size()
1460			&& system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) {
1461			notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL);
1462			cookie->last_size = inode->Size();
1463			cookie->last_notification = system_time();
1464		}
1465
1466		// This will flush the dirty blocks to disk from time to time.
1467		// It's done here and not in Inode::WriteAt() so that it won't
1468		// add to the duration of a transaction - it might even be a
1469		// good idea to offload those calls to another thread
1470		volume->WriteCachedBlocksIfNecessary();
1471	}
1472
1473	return status;
1474}
1475
1476
1477/**	Do whatever is necessary to close a file, EXCEPT for freeing
1478 *	the cookie!
1479 */
1480
1481static int
1482bfs_close(void *_ns, void *_node, void *_cookie)
1483{
1484	FUNCTION();
1485	if (_ns == NULL || _node == NULL || _cookie == NULL)
1486		return B_BAD_VALUE;
1487
1488	return B_OK;
1489}
1490
1491
1492static int
1493bfs_free_cookie(void *_ns, void *_node, void *_cookie)
1494{
1495	FUNCTION();
1496
1497	if (_ns == NULL || _node == NULL || _cookie == NULL)
1498		return B_BAD_VALUE;
1499
1500	file_cookie *cookie = (file_cookie *)_cookie;
1501
1502	Volume *volume = (Volume *)_ns;
1503	Inode *inode = (Inode *)_node;
1504
1505	if (cookie->open_mode & O_RWMASK) {
1506#ifdef UNSAFE_GET_VNODE
1507		RecursiveLocker locker(volume->Lock());
1508#endif
1509		ReadLocked locked(inode->Lock());
1510
1511		// trim the preallocated blocks and update the size,
1512		// and last_modified indices if needed
1513
1514		Transaction transaction(volume, inode->BlockNumber());
1515		status_t status = B_OK;
1516		bool changed = false;
1517		Index index(volume);
1518
1519		if (inode->OldSize() != inode->Size()) {
1520			status = inode->Trim(&transaction);
1521			if (status < B_OK)
1522				FATAL(("Could not trim preallocated blocks!"));
1523
1524			index.UpdateSize(&transaction, inode);
1525			changed = true;
1526		}
1527		if (inode->OldLastModified() != inode->LastModified()) {
1528			index.UpdateLastModified(&transaction, inode, inode->LastModified());
1529			changed = true;
1530
1531			// updating the index doesn't write back the inode
1532			inode->WriteBack(&transaction);
1533		}
1534
1535		if (status == B_OK)
1536			transaction.Done();
1537
1538		if (changed)
1539			notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL);
1540	}
1541
1542	if (inode->Flags() & INODE_NO_CACHE) {
1543		volume->Pool().ReleaseBuffers();
1544		inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE);
1545			// We don't need to save the inode, because INODE_NO_CACHE is a
1546			// non-permanent flag which will be removed when the inode is loaded
1547			// into memory.
1548	}
1549
1550	if (inode->Flags() & INODE_CHKBFS_RUNNING) {
1551		// "chkbfs" exited abnormally, so we have to stop it here...
1552		FATAL(("check process was aborted!\n"));
1553		volume->Allocator().StopChecking(NULL);
1554	}
1555
1556	return B_OK;
1557}
1558
1559
1560/**	Checks access permissions, return B_NOT_ALLOWED if the action
1561 *	is not allowed.
1562 */
1563
1564static int
1565bfs_access(void *_ns, void *_node, int accessMode)
1566{
1567	FUNCTION();
1568
1569	if (_ns == NULL || _node == NULL)
1570		return B_BAD_VALUE;
1571
1572	Inode *inode = (Inode *)_node;
1573	status_t status = inode->CheckPermissions(accessMode);
1574	if (status < B_OK)
1575		RETURN_ERROR(status);
1576
1577	return B_OK;
1578}
1579
1580
1581static int
1582bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize)
1583{
1584	FUNCTION();
1585
1586	Inode *inode = (Inode *)_node;
1587
1588	if (!inode->IsSymLink())
1589		RETURN_ERROR(B_BAD_VALUE);
1590
1591	if (inode->Flags() & INODE_LONG_SYMLINK) {
1592		status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize);
1593		if (status < B_OK)
1594			RETURN_ERROR(status);
1595
1596		*bufferSize = inode->Size();
1597		return B_OK;
1598	}
1599
1600	size_t numBytes = strlen((char *)&inode->Node()->short_symlink);
1601	uint32 bytes = numBytes;
1602	if (bytes > *bufferSize)
1603		bytes = *bufferSize;
1604
1605	memcpy(buffer, inode->Node()->short_symlink, bytes);
1606	*bufferSize = numBytes;
1607
1608	return B_OK;
1609}
1610
1611
1612//	#pragma mark -
1613//	Directory functions
1614
1615
1616static int
1617bfs_mkdir(void *_ns, void *_directory, const char *name, int mode)
1618{
1619	FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode));
1620
1621	if (_ns == NULL || _directory == NULL
1622		|| name == NULL || *name == '\0')
1623		RETURN_ERROR(B_BAD_VALUE);
1624
1625	Volume *volume = (Volume *)_ns;
1626	Inode *directory = (Inode *)_directory;
1627
1628	if (!directory->IsDirectory())
1629		RETURN_ERROR(B_BAD_TYPE);
1630
1631	status_t status = directory->CheckPermissions(W_OK);
1632	if (status < B_OK)
1633		RETURN_ERROR(status);
1634
1635#ifdef UNSAFE_GET_VNODE
1636	RecursiveLocker locker(volume->Lock());
1637#endif
1638	Transaction transaction(volume, directory->BlockNumber());
1639
1640	// Inode::Create() locks the inode if we pass the "id" parameter, but we
1641	// need it anyway
1642	off_t id;
1643	status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id);
1644	if (status == B_OK) {
1645		put_vnode(volume->ID(), id);
1646		transaction.Done();
1647
1648		notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name);
1649	}
1650
1651	return status;
1652}
1653
1654
1655static int
1656bfs_rmdir(void *_ns, void *_directory, const char *name)
1657{
1658	FUNCTION_START(("name = \"%s\"\n", name));
1659
1660	if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0')
1661		return B_BAD_VALUE;
1662
1663	Volume *volume = (Volume *)_ns;
1664	Inode *directory = (Inode *)_directory;
1665
1666#ifdef UNSAFE_GET_VNODE
1667	RecursiveLocker locker(volume->Lock());
1668#endif
1669	Transaction transaction(volume, directory->BlockNumber());
1670
1671	off_t id;
1672	status_t status = directory->Remove(&transaction, name, &id, true);
1673	if (status == B_OK) {
1674		transaction.Done();
1675
1676		notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL);
1677	}
1678
1679	return status;
1680}
1681
1682
1683/**	creates fs-specific "cookie" struct that keeps track of where
1684 *	you are at in reading through directory entries in bfs_readdir.
1685 */
1686
1687static int
1688bfs_open_dir(void *_ns, void *_node, void **_cookie)
1689{
1690	FUNCTION();
1691
1692	if (_ns == NULL || _node == NULL || _cookie == NULL)
1693		RETURN_ERROR(B_BAD_VALUE);
1694
1695	Inode *inode = (Inode *)_node;
1696
1697	// we don't ask here for directories only, because the bfs_open_index_dir()
1698	// function utilizes us (so we must be able to open indices as well)
1699	if (!inode->IsContainer())
1700		RETURN_ERROR(B_BAD_VALUE);
1701
1702	BPlusTree *tree;
1703	if (inode->GetTree(&tree) != B_OK)
1704		RETURN_ERROR(B_BAD_VALUE);
1705
1706	TreeIterator *iterator = new TreeIterator(tree);
1707	if (iterator == NULL)
1708		RETURN_ERROR(B_NO_MEMORY);
1709
1710	*_cookie = iterator;
1711	return B_OK;
1712}
1713
1714
1715static int
1716bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num,
1717	struct dirent *dirent, size_t bufferSize)
1718{
1719	FUNCTION();
1720
1721	TreeIterator *iterator = (TreeIterator *)_cookie;
1722	if (iterator == NULL)
1723		RETURN_ERROR(B_BAD_VALUE);
1724
1725	uint16 length;
1726	vnode_id id;
1727	status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id);
1728	if (status == B_ENTRY_NOT_FOUND) {
1729		*num = 0;
1730		return B_OK;
1731	} else if (status != B_OK)
1732		RETURN_ERROR(status);
1733
1734	Volume *volume = (Volume *)_ns;
1735
1736	dirent->d_dev = volume->ID();
1737	dirent->d_ino = id;
1738
1739#ifdef KEEP_WRONG_DIRENT_RECLEN
1740	dirent->d_reclen = length;
1741#else
1742	dirent->d_reclen = sizeof(struct dirent) + length;
1743#endif
1744
1745	*num = 1;
1746	return B_OK;
1747}
1748
1749
1750/** Sets the TreeIterator back to the beginning of the directory
1751 */
1752
1753static int
1754bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie)
1755{
1756	FUNCTION();
1757	TreeIterator *iterator = (TreeIterator *)_cookie;
1758
1759	if (iterator == NULL)
1760		RETURN_ERROR(B_BAD_VALUE);
1761
1762	return iterator->Rewind();
1763}
1764
1765
1766static int
1767bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/)
1768{
1769	FUNCTION();
1770	// Do whatever you need to to close a directory, but DON'T free the cookie!
1771	return B_OK;
1772}
1773
1774
1775static int
1776bfs_free_dir_cookie(void *ns, void *node, void *_cookie)
1777{
1778	TreeIterator *iterator = (TreeIterator *)_cookie;
1779
1780	if (iterator == NULL)
1781		RETURN_ERROR(B_BAD_VALUE);
1782
1783	delete iterator;
1784	return B_OK;
1785}
1786
1787
1788//	#pragma mark -
1789//	Attribute functions
1790
1791
1792static int
1793bfs_open_attrdir(void *_ns, void *_node, void **cookie)
1794{
1795	FUNCTION();
1796
1797	Inode *inode = (Inode *)_node;
1798	if (inode == NULL || inode->Node() == NULL)
1799		RETURN_ERROR(B_ERROR);
1800
1801	AttributeIterator *iterator = new AttributeIterator(inode);
1802	if (iterator == NULL)
1803		RETURN_ERROR(B_NO_MEMORY);
1804
1805	*cookie = iterator;
1806	return B_OK;
1807}
1808
1809
1810static int
1811bfs_close_attrdir(void *ns, void *node, void *cookie)
1812{
1813	FUNCTION();
1814	return B_OK;
1815}
1816
1817
1818static int
1819bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie)
1820{
1821	FUNCTION();
1822	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1823
1824	if (iterator == NULL)
1825		RETURN_ERROR(B_BAD_VALUE);
1826
1827	delete iterator;
1828	return B_OK;
1829}
1830
1831
1832static int
1833bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie)
1834{
1835	FUNCTION();
1836
1837	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1838	if (iterator == NULL)
1839		RETURN_ERROR(B_BAD_VALUE);
1840
1841	RETURN_ERROR(iterator->Rewind());
1842}
1843
1844
1845static int
1846bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize)
1847{
1848	FUNCTION();
1849	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1850
1851	if (iterator == NULL)
1852		RETURN_ERROR(B_BAD_VALUE);
1853
1854	uint32 type;
1855	size_t length;
1856	status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino);
1857	if (status == B_ENTRY_NOT_FOUND) {
1858		*num = 0;
1859		return B_OK;
1860	} else if (status != B_OK)
1861		RETURN_ERROR(status);
1862
1863	Volume *volume = (Volume *)_ns;
1864
1865	dirent->d_dev = volume->ID();
1866#ifdef KEEP_WRONG_DIRENT_RECLEN
1867	dirent->d_reclen = length;
1868#else
1869	dirent->d_reclen = sizeof(struct dirent) + length;
1870#endif
1871
1872	*num = 1;
1873	return B_OK;
1874}
1875
1876
1877static int
1878bfs_remove_attr(void *_ns, void *_node, const char *name)
1879{
1880	FUNCTION_START(("name = \"%s\"\n", name));
1881
1882	if (_ns == NULL || _node == NULL || name == NULL)
1883		return B_BAD_VALUE;
1884
1885	Volume *volume = (Volume *)_ns;
1886	Inode *inode = (Inode *)_node;
1887
1888	status_t status = inode->CheckPermissions(W_OK);
1889	if (status < B_OK)
1890		return status;
1891
1892#ifdef UNSAFE_GET_VNODE
1893	RecursiveLocker locker(volume->Lock());
1894#endif
1895	Transaction transaction(volume, inode->BlockNumber());
1896
1897	status = inode->RemoveAttribute(&transaction, name);
1898	if (status == B_OK) {
1899		transaction.Done();
1900
1901		notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name);
1902	}
1903
1904	RETURN_ERROR(status);
1905}
1906
1907
1908static int
1909bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname)
1910{
1911	FUNCTION_START(("name = \"%s\", to = \"%s\"\n", oldname, newname));
1912
1913	// ToDo: implement bfs_rename_attr()!
1914	// I'll skip the implementation here, and will do it for OpenBeOS - at least
1915	// there will be an API to move one attribute to another file, making that
1916	// function much more complicated - oh joy ;-)
1917
1918	RETURN_ERROR(B_ENTRY_NOT_FOUND);
1919}
1920
1921
1922static int
1923bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo)
1924{
1925	FUNCTION_START(("name = \"%s\"\n", name));
1926
1927	Inode *inode = (Inode *)_node;
1928	if (inode == NULL || inode->Node() == NULL)
1929		RETURN_ERROR(B_ERROR);
1930
1931	// first, try to find it in the small data region
1932	small_data *smallData = NULL;
1933	if (inode->SmallDataLock().Lock() == B_OK) {
1934		if ((smallData = inode->FindSmallData((const char *)name)) != NULL) {
1935			attrInfo->type = smallData->Type();
1936			attrInfo->size = smallData->DataSize();
1937		}
1938		inode->SmallDataLock().Unlock();
1939	}
1940	if (smallData != NULL)
1941		return B_OK;
1942
1943	// then, search in the attribute directory
1944	Inode *attribute;
1945	status_t status = inode->GetAttribute(name, &attribute);
1946	if (status == B_OK) {
1947		attrInfo->type = attribute->Type();
1948		attrInfo->size = attribute->Size();
1949
1950		inode->ReleaseAttribute(attribute);
1951		return B_OK;
1952	}
1953
1954	RETURN_ERROR(status);
1955}
1956
1957
1958static int
1959bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer,
1960	size_t *_length, off_t pos)
1961{
1962	FUNCTION_START(("name = \"%s\"\n", name));
1963	if (_ns == NULL || _node == NULL || name == NULL || *name == '\0')
1964		RETURN_ERROR(B_BAD_VALUE);
1965
1966	// Writing the name attribute using this function is not allowed,
1967	// also using the reserved indices name, last_modified, and size
1968	// shouldn't be allowed.
1969	// ToDo: we might think about allowing to update those values, but
1970	//	really change their corresponding values in the bfs_inode structure
1971	if (name[0] == FILE_NAME_NAME && name[1] == '\0'
1972		|| !strcmp(name, "name")
1973		|| !strcmp(name, "last_modified")
1974		|| !strcmp(name, "size"))
1975		RETURN_ERROR(B_NOT_ALLOWED);
1976
1977	Volume *volume = (Volume *)_ns;
1978	Inode *inode = (Inode *)_node;
1979
1980	status_t status = inode->CheckPermissions(W_OK);
1981	if (status < B_OK)
1982		return status;
1983
1984#ifdef UNSAFE_GET_VNODE
1985	RecursiveLocker locker(volume->Lock());
1986#endif
1987	Transaction transaction(volume, inode->BlockNumber());
1988
1989	status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length);
1990	if (status == B_OK) {
1991		transaction.Done();
1992
1993		notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name);
1994	}
1995
1996	return status;
1997}
1998
1999
2000static int
2001bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer,
2002	size_t *_length, off_t pos)
2003{
2004	FUNCTION();
2005	Inode *inode = (Inode *)_node;
2006
2007	if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL)
2008		RETURN_ERROR(B_BAD_VALUE);
2009
2010	status_t status = inode->CheckPermissions(R_OK);
2011	if (status < B_OK)
2012		return status;
2013
2014	return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length);
2015}
2016
2017
2018//	#pragma mark -
2019//	Index functions
2020
2021
2022static int
2023bfs_open_indexdir(void *_ns, void **_cookie)
2024{
2025	FUNCTION();
2026	if (_ns == NULL || _cookie == NULL)
2027		RETURN_ERROR(B_BAD_VALUE);
2028
2029	Volume *volume = (Volume *)_ns;
2030
2031	if (volume->IndicesNode() == NULL)
2032		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2033
2034	// Since the indices root node is just a directory, and we are storing
2035	// a pointer to it in our Volume object, we can just use the directory
2036	// traversal functions.
2037	// In fact we're storing it in the Volume object for that reason.
2038
2039	RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie));
2040}
2041
2042
2043static int
2044bfs_close_indexdir(void *_ns, void *_cookie)
2045{
2046	FUNCTION();
2047	if (_ns == NULL || _cookie == NULL)
2048		RETURN_ERROR(B_BAD_VALUE);
2049
2050	Volume *volume = (Volume *)_ns;
2051	RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie));
2052}
2053
2054
2055static int
2056bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie)
2057{
2058	FUNCTION();
2059	if (_ns == NULL || _cookie == NULL)
2060		RETURN_ERROR(B_BAD_VALUE);
2061
2062	Volume *volume = (Volume *)_ns;
2063	RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie));
2064}
2065
2066
2067static int
2068bfs_rewind_indexdir(void *_ns, void *_cookie)
2069{
2070	FUNCTION();
2071	if (_ns == NULL || _cookie == NULL)
2072		RETURN_ERROR(B_BAD_VALUE);
2073
2074	Volume *volume = (Volume *)_ns;
2075	RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie));
2076}
2077
2078
2079static int
2080bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize)
2081{
2082	FUNCTION();
2083	if (_ns == NULL || _cookie == NULL)
2084		RETURN_ERROR(B_BAD_VALUE);
2085
2086	Volume *volume = (Volume *)_ns;
2087	RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize));
2088}
2089
2090
2091static int
2092bfs_create_index(void *_ns, const char *name, int type, int flags)
2093{
2094	FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags));
2095	if (_ns == NULL || name == NULL || *name == '\0')
2096		return B_BAD_VALUE;
2097
2098	Volume *volume = (Volume *)_ns;
2099
2100	if (volume->IsReadOnly())
2101		return B_READ_ONLY_DEVICE;
2102
2103	// only root users are allowed to create indices
2104	if (geteuid() != 0)
2105		return B_NOT_ALLOWED;
2106
2107#ifdef UNSAFE_GET_VNODE
2108	RecursiveLocker locker(volume->Lock());
2109#endif
2110	Transaction transaction(volume, volume->Indices());
2111
2112	Index index(volume);
2113	status_t status = index.Create(&transaction, name, type);
2114
2115	if (status == B_OK)
2116		transaction.Done();
2117
2118	RETURN_ERROR(status);
2119}
2120
2121
2122static int
2123bfs_remove_index(void *_ns, const char *name)
2124{
2125	FUNCTION();
2126	if (_ns == NULL || name == NULL || *name == '\0')
2127		return B_BAD_VALUE;
2128
2129	Volume *volume = (Volume *)_ns;
2130
2131	if (volume->IsReadOnly())
2132		return B_READ_ONLY_DEVICE;
2133
2134	// only root users are allowed to remove indices
2135	if (geteuid() != 0)
2136		return B_NOT_ALLOWED;
2137
2138	Inode *indices;
2139	if ((indices = volume->IndicesNode()) == NULL)
2140		return B_ENTRY_NOT_FOUND;
2141
2142#ifdef UNSAFE_GET_VNODE
2143	RecursiveLocker locker(volume->Lock());
2144#endif
2145	Transaction transaction(volume, volume->Indices());
2146
2147	status_t status = indices->Remove(&transaction, name);
2148	if (status == B_OK)
2149		transaction.Done();
2150
2151	RETURN_ERROR(status);
2152}
2153
2154
2155static int
2156bfs_rename_index(void *ns, const char *oldname, const char *newname)
2157{
2158	FUNCTION_START(("from = %s, to = %s\n", oldname, newname));
2159
2160	// Well, renaming an index doesn't make that much sense, as you
2161	// would also need to remove every file in it (or the index
2162	// would contain wrong data)
2163	// But in that case, you can better remove the old one, and
2164	// create a new one...
2165	// There is also no way to call this function from a userland
2166	// application.
2167
2168	RETURN_ERROR(B_ENTRY_NOT_FOUND);
2169}
2170
2171
2172static int
2173bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo)
2174{
2175	FUNCTION_START(("name = %s\n", name));
2176	if (_ns == NULL || name == NULL || indexInfo == NULL)
2177		RETURN_ERROR(B_BAD_VALUE);
2178
2179	Volume *volume = (Volume *)_ns;
2180	Index index(volume);
2181	status_t status = index.SetTo(name);
2182	if (status < B_OK)
2183		RETURN_ERROR(status);
2184
2185	bfs_inode *node = index.Node()->Node();
2186
2187	indexInfo->type = index.Type();
2188	indexInfo->size = node->data.Size();
2189	indexInfo->modification_time = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT);
2190	indexInfo->creation_time = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT);
2191	indexInfo->uid = node->UserID();
2192	indexInfo->gid = node->GroupID();
2193
2194	return B_OK;
2195}
2196
2197
2198//	#pragma mark -
2199//	Query functions
2200
2201
2202static int
2203bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port,
2204	long token, void **cookie)
2205{
2206	FUNCTION();
2207	if (_ns == NULL || queryString == NULL || cookie == NULL)
2208		RETURN_ERROR(B_BAD_VALUE);
2209
2210	PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token));
2211
2212	Volume *volume = (Volume *)_ns;
2213
2214	Expression *expression = new Expression((char *)queryString);
2215	if (expression == NULL)
2216		RETURN_ERROR(B_NO_MEMORY);
2217
2218	if (expression->InitCheck() < B_OK) {
2219		FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position()));
2220		delete expression;
2221		RETURN_ERROR(B_BAD_VALUE);
2222	}
2223
2224	Query *query = new Query(volume, expression, flags);
2225	if (query == NULL) {
2226		delete expression;
2227		RETURN_ERROR(B_NO_MEMORY);
2228	}
2229
2230	if (flags & B_LIVE_QUERY)
2231		query->SetLiveMode(port, token);
2232
2233	*cookie = (void *)query;
2234
2235	return B_OK;
2236}
2237
2238
2239static int
2240bfs_close_query(void *ns, void *cookie)
2241{
2242	FUNCTION();
2243	return B_OK;
2244}
2245
2246
2247static int
2248bfs_free_query_cookie(void *ns, void *node, void *cookie)
2249{
2250	FUNCTION();
2251	if (cookie == NULL)
2252		RETURN_ERROR(B_BAD_VALUE);
2253
2254	Query *query = (Query *)cookie;
2255	Expression *expression = query->GetExpression();
2256	delete query;
2257	delete expression;
2258
2259	return B_OK;
2260}
2261
2262
2263static int
2264bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize)
2265{
2266	FUNCTION();
2267	Query *query = (Query *)cookie;
2268	if (query == NULL)
2269		RETURN_ERROR(B_BAD_VALUE);
2270
2271	status_t status = query->GetNextEntry(dirent, bufferSize);
2272	if (status == B_OK)
2273		*num = 1;
2274	else if (status == B_ENTRY_NOT_FOUND)
2275		*num = 0;
2276	else
2277		return status;
2278
2279	return B_OK;
2280}
2281
2282