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