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