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