xref: /haiku/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp (revision 551a9b9a01ded2752e40a36576650713e4447c05)
1 /*
2  * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 //!	file system interface to Haiku's vnode layer
8 
9 
10 #include "Attribute.h"
11 #include "CheckVisitor.h"
12 #include "Debug.h"
13 #include "Volume.h"
14 #include "Inode.h"
15 #include "Index.h"
16 #include "BPlusTree.h"
17 #include "Query.h"
18 #include "ResizeVisitor.h"
19 #include "bfs_control.h"
20 #include "bfs_disk_system.h"
21 
22 // TODO: temporary solution as long as there is no public I/O requests API
23 #ifndef FS_SHELL
24 #	include <io_requests.h>
25 #	include <util/fs_trim_support.h>
26 #endif
27 
28 
29 #define BFS_IO_SIZE	65536
30 
31 #if defined(BFS_LITTLE_ENDIAN_ONLY)
32 #define BFS_ENDIAN_SUFFIX ""
33 #define BFS_ENDIAN_PRETTY_SUFFIX ""
34 #else
35 #define BFS_ENDIAN_SUFFIX "_big"
36 #define BFS_ENDIAN_PRETTY_SUFFIX " (Big Endian)"
37 #endif
38 
39 
40 struct identify_cookie {
41 	disk_super_block super_block;
42 };
43 
44 extern void fill_stat_buffer(Inode* inode, struct stat& stat);
45 
46 
47 static void
fill_stat_time(const bfs_inode & node,struct stat & stat)48 fill_stat_time(const bfs_inode& node, struct stat& stat)
49 {
50 	bigtime_t now = real_time_clock_usecs();
51 	stat.st_atim.tv_sec = now / 1000000LL;
52 	stat.st_atim.tv_nsec = (now % 1000000LL) * 1000;
53 
54 	stat.st_mtim.tv_sec = bfs_inode::ToSecs(node.LastModifiedTime());
55 	stat.st_mtim.tv_nsec = bfs_inode::ToNsecs(node.LastModifiedTime());
56 	stat.st_crtim.tv_sec = bfs_inode::ToSecs(node.CreateTime());
57 	stat.st_crtim.tv_nsec = bfs_inode::ToNsecs(node.CreateTime());
58 
59 	// For BeOS compatibility, if on-disk ctime is invalid, fall back to mtime:
60 	bigtime_t changeTime = node.StatusChangeTime();
61 	if (changeTime < node.LastModifiedTime())
62 		stat.st_ctim = stat.st_mtim;
63 	else {
64 		stat.st_ctim.tv_sec = bfs_inode::ToSecs(changeTime);
65 		stat.st_ctim.tv_nsec = bfs_inode::ToNsecs(changeTime);
66 	}
67 }
68 
69 
70 void
fill_stat_buffer(Inode * inode,struct stat & stat)71 fill_stat_buffer(Inode* inode, struct stat& stat)
72 {
73 	const bfs_inode& node = inode->Node();
74 
75 	stat.st_dev = inode->GetVolume()->ID();
76 	stat.st_ino = inode->ID();
77 	stat.st_nlink = 1;
78 	stat.st_blksize = BFS_IO_SIZE;
79 
80 	stat.st_uid = node.UserID();
81 	stat.st_gid = node.GroupID();
82 	stat.st_mode = node.Mode();
83 	stat.st_type = node.Type();
84 
85 	fill_stat_time(node, stat);
86 
87 	if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
88 		// symlinks report the size of the link here
89 		stat.st_size = strlen(node.short_symlink);
90 	} else
91 		stat.st_size = inode->Size();
92 
93 	stat.st_blocks = inode->AllocatedSize() / 512;
94 }
95 
96 
97 //!	bfs_io() callback hook
98 static status_t
iterative_io_get_vecs_hook(void * cookie,io_request * request,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)99 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
100 	size_t size, struct file_io_vec* vecs, size_t* _count)
101 {
102 	Inode* inode = (Inode*)cookie;
103 
104 	return file_map_translate(inode->Map(), offset, size, vecs, _count,
105 		inode->GetVolume()->BlockSize());
106 }
107 
108 
109 //!	bfs_io() callback hook
110 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)111 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
112 	bool partialTransfer, size_t bytesTransferred)
113 {
114 	Inode* inode = (Inode*)cookie;
115 	rw_lock_read_unlock(&inode->Lock());
116 	put_vnode(inode->GetVolume()->FSVolume(), inode->ID());
117 	return B_OK;
118 }
119 
120 
121 //	#pragma mark - Scanning
122 
123 
124 static float
bfs_identify_partition(int fd,partition_data * partition,void ** _cookie)125 bfs_identify_partition(int fd, partition_data* partition, void** _cookie)
126 {
127 	disk_super_block superBlock;
128 	status_t status = Volume::Identify(fd, &superBlock);
129 	if (status != B_OK)
130 		return -1;
131 
132 	identify_cookie* cookie = new(std::nothrow) identify_cookie;
133 	if (cookie == NULL)
134 		return -1;
135 
136 	memcpy(&cookie->super_block, &superBlock, sizeof(disk_super_block));
137 
138 	*_cookie = cookie;
139 	return 0.85f;
140 }
141 
142 
143 static status_t
bfs_scan_partition(int fd,partition_data * partition,void * _cookie)144 bfs_scan_partition(int fd, partition_data* partition, void* _cookie)
145 {
146 	identify_cookie* cookie = (identify_cookie*)_cookie;
147 
148 	partition->status = B_PARTITION_VALID;
149 	partition->flags |= B_PARTITION_FILE_SYSTEM;
150 	partition->content_size = cookie->super_block.NumBlocks()
151 		* cookie->super_block.BlockSize();
152 	partition->block_size = cookie->super_block.BlockSize();
153 	partition->content_name = strdup(cookie->super_block.name);
154 	if (partition->content_name == NULL)
155 		return B_NO_MEMORY;
156 
157 	return B_OK;
158 }
159 
160 
161 static void
bfs_free_identify_partition_cookie(partition_data * partition,void * _cookie)162 bfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
163 {
164 	identify_cookie* cookie = (identify_cookie*)_cookie;
165 	delete cookie;
166 }
167 
168 
169 //	#pragma mark -
170 
171 
172 static status_t
bfs_mount(fs_volume * _volume,const char * device,uint32 flags,const char * args,ino_t * _rootID)173 bfs_mount(fs_volume* _volume, const char* device, uint32 flags,
174 	const char* args, ino_t* _rootID)
175 {
176 	FUNCTION();
177 
178 	Volume* volume = new(std::nothrow) Volume(_volume);
179 	if (volume == NULL)
180 		return B_NO_MEMORY;
181 
182 	status_t status = volume->Mount(device, flags);
183 	if (status != B_OK) {
184 		delete volume;
185 		RETURN_ERROR(status);
186 	}
187 
188 	_volume->private_volume = volume;
189 	_volume->ops = &gBFSVolumeOps;
190 	*_rootID = volume->ToVnode(volume->Root());
191 
192 	INFORM(("mounted \"%s\" (root node at %" B_PRIdINO ", device = %s)\n",
193 		volume->Name(), *_rootID, device));
194 	return B_OK;
195 }
196 
197 
198 static status_t
bfs_unmount(fs_volume * _volume)199 bfs_unmount(fs_volume* _volume)
200 {
201 	FUNCTION();
202 	Volume* volume = (Volume*)_volume->private_volume;
203 
204 	status_t status = volume->Unmount();
205 	delete volume;
206 
207 	RETURN_ERROR(status);
208 }
209 
210 
211 static status_t
bfs_read_fs_stat(fs_volume * _volume,struct fs_info * info)212 bfs_read_fs_stat(fs_volume* _volume, struct fs_info* info)
213 {
214 	FUNCTION();
215 
216 	Volume* volume = (Volume*)_volume->private_volume;
217 	MutexLocker locker(volume->Lock());
218 
219 	// File system flags.
220 	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
221 		| (volume->IndicesNode() != NULL ? B_FS_HAS_QUERY : 0)
222 		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0)
223 		| B_FS_SUPPORTS_MONITOR_CHILDREN;
224 
225 	info->io_size = BFS_IO_SIZE;
226 		// whatever is appropriate here?
227 
228 	info->block_size = volume->BlockSize();
229 	info->total_blocks = volume->NumBlocks();
230 	info->free_blocks = volume->FreeBlocks();
231 
232 	// Volume name
233 	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
234 
235 	// File system name
236 	strlcpy(info->fsh_name, "bfs", sizeof(info->fsh_name));
237 
238 	return B_OK;
239 }
240 
241 
242 static status_t
bfs_write_fs_stat(fs_volume * _volume,const struct fs_info * info,uint32 mask)243 bfs_write_fs_stat(fs_volume* _volume, const struct fs_info* info, uint32 mask)
244 {
245 	FUNCTION_START(("mask = %" B_PRId32 "\n", mask));
246 
247 	Volume* volume = (Volume*)_volume->private_volume;
248 	if (volume->IsReadOnly())
249 		return B_READ_ONLY_DEVICE;
250 
251 	MutexLocker locker(volume->Lock());
252 
253 	status_t status = B_BAD_VALUE;
254 
255 	if (mask & FS_WRITE_FSINFO_NAME) {
256 		disk_super_block& superBlock = volume->SuperBlock();
257 
258 		strncpy(superBlock.name, info->volume_name,
259 			sizeof(superBlock.name) - 1);
260 		superBlock.name[sizeof(superBlock.name) - 1] = '\0';
261 
262 		status = volume->WriteSuperBlock();
263 	}
264 	return status;
265 }
266 
267 
268 static status_t
bfs_sync(fs_volume * _volume)269 bfs_sync(fs_volume* _volume)
270 {
271 	FUNCTION();
272 
273 	Volume* volume = (Volume*)_volume->private_volume;
274 	return volume->Sync();
275 }
276 
277 
278 //	#pragma mark -
279 
280 
281 /*!	Reads in the node from disk and creates an inode object from it.
282 */
283 static status_t
bfs_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)284 bfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
285 	uint32* _flags, bool reenter)
286 {
287 	//FUNCTION_START(("ino_t = %lld\n", id));
288 	Volume* volume = (Volume*)_volume->private_volume;
289 
290 	// first inode may be after the log area, we don't go through
291 	// the hassle and try to load an earlier block from disk
292 	if (id < volume->ToBlock(volume->Log()) + volume->Log().Length()
293 		|| id > volume->NumBlocks()) {
294 		INFORM(("inode at %" B_PRIdINO " requested!\n", id));
295 		return B_ERROR;
296 	}
297 
298 	CachedBlock cached(volume);
299 	status_t status = cached.SetTo(id);
300 	if (status != B_OK) {
301 		FATAL(("could not read inode: %" B_PRIdINO ": %s\n", id,
302 			strerror(status)));
303 		return status;
304 	}
305 	bfs_inode* node = (bfs_inode*)cached.Block();
306 
307 	status = node->InitCheck(volume);
308 	if (status != B_OK) {
309 		if ((node->Flags() & INODE_DELETED) != 0) {
310 			INFORM(("inode at %" B_PRIdINO " is already deleted!\n", id));
311 		} else {
312 			FATAL(("inode at %" B_PRIdINO " could not be read: %s!\n", id,
313 				strerror(status)));
314 		}
315 		return status;
316 	}
317 
318 	Inode* inode = new(std::nothrow) Inode(volume, id);
319 	if (inode == NULL)
320 		return B_NO_MEMORY;
321 
322 	status = inode->InitCheck(false);
323 	if (status != B_OK)
324 		delete inode;
325 
326 	if (status == B_OK) {
327 		_node->private_node = inode;
328 		_node->ops = &gBFSVnodeOps;
329 		*_type = inode->Mode();
330 		*_flags = 0;
331 	}
332 
333 	return status;
334 }
335 
336 
337 static status_t
bfs_put_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)338 bfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
339 {
340 	Volume* volume = (Volume*)_volume->private_volume;
341 	Inode* inode = (Inode*)_node->private_node;
342 
343 	// since a directory's size can be changed without having it opened,
344 	// we need to take care about their preallocated blocks here
345 	if (!volume->IsReadOnly() && !volume->IsCheckingThread()
346 		&& inode->NeedsTrimming()) {
347 		Transaction transaction(volume, inode->BlockNumber());
348 
349 		if (inode->TrimPreallocation(transaction) == B_OK)
350 			transaction.Done();
351 		else if (transaction.HasParent()) {
352 			// TODO: for now, we don't let sub-transactions fail
353 			transaction.Done();
354 		}
355 	}
356 
357 	delete inode;
358 	return B_OK;
359 }
360 
361 
362 static status_t
bfs_remove_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)363 bfs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
364 {
365 	FUNCTION();
366 
367 	Volume* volume = (Volume*)_volume->private_volume;
368 	Inode* inode = (Inode*)_node->private_node;
369 
370 	// If the inode isn't in use anymore, we were called before
371 	// bfs_unlink() returns - in this case, we can just use the
372 	// transaction which has already deleted the inode.
373 	Transaction transaction(volume, volume->ToBlock(inode->Parent()));
374 
375 	// The file system check functionality uses this flag to prevent the space
376 	// used up by the inode from being freed - this flag is set only in
377 	// situations where this does not cause any harm as the block bitmap will
378 	// get fixed anyway in this case).
379 	if ((inode->Flags() & INODE_DONT_FREE_SPACE) != 0) {
380 		delete inode;
381 		return B_OK;
382 	}
383 
384 	ASSERT((inode->Flags() & INODE_DELETED) != 0);
385 
386 	status_t status = inode->Free(transaction);
387 	if (status == B_OK) {
388 		status = transaction.Done();
389 	} else if (transaction.HasParent()) {
390 		// TODO: for now, we don't let sub-transactions fail
391 		status = transaction.Done();
392 	}
393 
394 	volume->RemovedInodes().Remove(inode);
395 
396 	// TODO: the VFS currently does not allow this to fail
397 	delete inode;
398 
399 	return status;
400 }
401 
402 
403 static bool
bfs_can_page(fs_volume * _volume,fs_vnode * _v,void * _cookie)404 bfs_can_page(fs_volume* _volume, fs_vnode* _v, void* _cookie)
405 {
406 	// TODO: we're obviously not even asked...
407 	return false;
408 }
409 
410 
411 static status_t
bfs_read_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)412 bfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
413 	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
414 {
415 	Volume* volume = (Volume*)_volume->private_volume;
416 	Inode* inode = (Inode*)_node->private_node;
417 
418 	if (inode->FileCache() == NULL)
419 		RETURN_ERROR(B_BAD_VALUE);
420 
421 	InodeReadLocker _(inode);
422 
423 	uint32 vecIndex = 0;
424 	size_t vecOffset = 0;
425 	size_t bytesLeft = *_numBytes;
426 	status_t status;
427 
428 	while (true) {
429 		file_io_vec fileVecs[8];
430 		size_t fileVecCount = 8;
431 
432 		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
433 			&fileVecCount, 0);
434 		if (status != B_OK && status != B_BUFFER_OVERFLOW)
435 			break;
436 
437 		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
438 
439 		size_t bytes = bytesLeft;
440 		status = read_file_io_vec_pages(volume->Device(), fileVecs,
441 			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
442 		if (status != B_OK || !bufferOverflow)
443 			break;
444 
445 		pos += bytes;
446 		bytesLeft -= bytes;
447 	}
448 
449 	return status;
450 }
451 
452 
453 static status_t
bfs_write_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)454 bfs_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
455 	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
456 {
457 	Volume* volume = (Volume*)_volume->private_volume;
458 	Inode* inode = (Inode*)_node->private_node;
459 
460 	if (volume->IsReadOnly())
461 		return B_READ_ONLY_DEVICE;
462 
463 	if (inode->FileCache() == NULL)
464 		RETURN_ERROR(B_BAD_VALUE);
465 
466 	InodeReadLocker _(inode);
467 
468 	uint32 vecIndex = 0;
469 	size_t vecOffset = 0;
470 	size_t bytesLeft = *_numBytes;
471 	status_t status;
472 
473 	while (true) {
474 		file_io_vec fileVecs[8];
475 		size_t fileVecCount = 8;
476 
477 		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
478 			&fileVecCount, 0);
479 		if (status != B_OK && status != B_BUFFER_OVERFLOW)
480 			break;
481 
482 		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
483 
484 		size_t bytes = bytesLeft;
485 		status = write_file_io_vec_pages(volume->Device(), fileVecs,
486 			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
487 		if (status != B_OK || !bufferOverflow)
488 			break;
489 
490 		pos += bytes;
491 		bytesLeft -= bytes;
492 	}
493 
494 	return status;
495 }
496 
497 
498 static status_t
bfs_io(fs_volume * _volume,fs_vnode * _node,void * _cookie,io_request * request)499 bfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
500 {
501 #if KDEBUG_RW_LOCK_DEBUG
502 	// bfs_io depends on read-locks being implicitly transferrable across threads.
503 	return B_UNSUPPORTED;
504 #endif
505 
506 	Volume* volume = (Volume*)_volume->private_volume;
507 	Inode* inode = (Inode*)_node->private_node;
508 
509 #ifndef FS_SHELL
510 	if (io_request_is_write(request) && volume->IsReadOnly()) {
511 		notify_io_request(request, B_READ_ONLY_DEVICE);
512 		return B_READ_ONLY_DEVICE;
513 	}
514 #endif
515 
516 	if (inode->FileCache() == NULL) {
517 #ifndef FS_SHELL
518 		notify_io_request(request, B_BAD_VALUE);
519 #endif
520 		RETURN_ERROR(B_BAD_VALUE);
521 	}
522 
523 	// We lock the node here and will unlock it in the "finished" hook.
524 	rw_lock_read_lock(&inode->Lock());
525 
526 	// Due to how I/O request notifications work, it is possible that
527 	// some other thread could be notified that the request completed
528 	// before we have a chance to release the read lock. We thus need
529 	// our own reference to the vnode.
530 	acquire_vnode(_volume, inode->ID());
531 
532 	return do_iterative_fd_io(volume->Device(), request,
533 		iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
534 }
535 
536 
537 static status_t
bfs_get_file_map(fs_volume * _volume,fs_vnode * _node,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)538 bfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, size_t size,
539 	struct file_io_vec* vecs, size_t* _count)
540 {
541 	Volume* volume = (Volume*)_volume->private_volume;
542 	Inode* inode = (Inode*)_node->private_node;
543 
544 	int32 blockShift = volume->BlockShift();
545 	uint32 index = 0, max = *_count;
546 	block_run run;
547 	off_t fileOffset;
548 
549 	//FUNCTION_START(("offset = %lld, size = %lu\n", offset, size));
550 
551 	while (true) {
552 		status_t status = inode->FindBlockRun(offset, run, fileOffset);
553 		if (status != B_OK)
554 			return status;
555 
556 		vecs[index].offset = volume->ToOffset(run) + offset - fileOffset;
557 		vecs[index].length = ((uint32)run.Length() << blockShift)
558 			- offset + fileOffset;
559 
560 		// are we already done?
561 		if ((uint64)size <= (uint64)vecs[index].length
562 			|| (uint64)offset + (uint64)vecs[index].length
563 				>= (uint64)inode->Size()) {
564 			if ((uint64)offset + (uint64)vecs[index].length
565 					> (uint64)inode->Size()) {
566 				// make sure the extent ends with the last official file
567 				// block (without taking any preallocations into account)
568 				vecs[index].length = round_up(inode->Size() - offset,
569 					volume->BlockSize());
570 			}
571 			*_count = index + 1;
572 			return B_OK;
573 		}
574 
575 		offset += vecs[index].length;
576 		size -= vecs[index].length;
577 		index++;
578 
579 		if (index >= max) {
580 			// we're out of file_io_vecs; let's bail out
581 			*_count = index;
582 			return B_BUFFER_OVERFLOW;
583 		}
584 	}
585 
586 	// can never get here
587 	return B_ERROR;
588 }
589 
590 
591 //	#pragma mark -
592 
593 
594 static status_t
bfs_lookup(fs_volume * _volume,fs_vnode * _directory,const char * file,ino_t * _vnodeID)595 bfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* file,
596 	ino_t* _vnodeID)
597 {
598 	Volume* volume = (Volume*)_volume->private_volume;
599 	Inode* directory = (Inode*)_directory->private_node;
600 
601 	InodeReadLocker locker(directory);
602 
603 	// check access permissions
604 	status_t status = directory->CheckPermissions(X_OK);
605 	if (status != B_OK)
606 		RETURN_ERROR(status);
607 
608 	BPlusTree* tree = directory->Tree();
609 	if (tree == NULL)
610 		RETURN_ERROR(B_BAD_VALUE);
611 
612 	status = tree->Find((uint8*)file, (uint16)strlen(file), _vnodeID);
613 	if (status != B_OK) {
614 		//PRINT(("bfs_walk() could not find %lld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
615 		if (status == B_ENTRY_NOT_FOUND)
616 			entry_cache_add_missing(volume->ID(), directory->ID(), file);
617 
618 		return status;
619 	}
620 
621 	entry_cache_add(volume->ID(), directory->ID(), file, *_vnodeID);
622 
623 	locker.Unlock();
624 
625 	Inode* inode;
626 	status = get_vnode(volume->FSVolume(), *_vnodeID, (void**)&inode);
627 	if (status != B_OK) {
628 		REPORT_ERROR(status);
629 		return B_ENTRY_NOT_FOUND;
630 	}
631 
632 	return B_OK;
633 }
634 
635 
636 static status_t
bfs_get_vnode_name(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t bufferSize)637 bfs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
638 	size_t bufferSize)
639 {
640 	Inode* inode = (Inode*)_node->private_node;
641 
642 	return inode->GetName(buffer, bufferSize);
643 }
644 
645 
646 static status_t
bfs_ioctl(fs_volume * _volume,fs_vnode * _node,void * _cookie,uint32 cmd,void * buffer,size_t bufferLength)647 bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
648 	void* buffer, size_t bufferLength)
649 {
650 	FUNCTION_START(("node = %p, cmd = %" B_PRIu32 ", buf = %p"
651 		", len = %" B_PRIuSIZE "\n", _node, cmd, buffer, bufferLength));
652 
653 	Volume* volume = (Volume*)_volume->private_volume;
654 
655 	switch (cmd) {
656 #ifndef FS_SHELL
657 		case B_TRIM_DEVICE:
658 		{
659 			fs_trim_data* trimData;
660 			MemoryDeleter deleter;
661 			status_t status = get_trim_data_from_user(buffer, bufferLength,
662 				deleter, trimData);
663 			if (status != B_OK)
664 				return status;
665 
666 			trimData->trimmed_size = 0;
667 
668 			for (uint32 i = 0; i < trimData->range_count; i++) {
669 				uint64 trimmedSize = 0;
670 				status_t status = volume->Allocator().Trim(
671 					trimData->ranges[i].offset, trimData->ranges[i].size,
672 					trimmedSize);
673 				if (status != B_OK)
674 					return status;
675 
676 				trimData->trimmed_size += trimmedSize;
677 			}
678 
679 			return copy_trim_data_to_user(buffer, trimData);
680 		}
681 #endif
682 
683 		case BFS_IOCTL_VERSION:
684 		{
685 			uint32 version = 0x10000;
686 			return user_memcpy(buffer, &version, sizeof(uint32));
687 		}
688 		case BFS_IOCTL_START_CHECKING:
689 		{
690 			// start checking
691 			status_t status = volume->CreateCheckVisitor();
692 			if (status != B_OK)
693 				return status;
694 
695 			CheckVisitor* checker = volume->CheckVisitor();
696 
697 			if (user_memcpy(&checker->Control(), buffer,
698 					sizeof(check_control)) != B_OK) {
699 				return B_BAD_ADDRESS;
700 			}
701 
702 			status = checker->StartBitmapPass();
703 			if (status == B_OK) {
704 				file_cookie* cookie = (file_cookie*)_cookie;
705 				cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
706 			}
707 
708 			return status;
709 		}
710 		case BFS_IOCTL_STOP_CHECKING:
711 		{
712 			// stop checking
713 			CheckVisitor* checker = volume->CheckVisitor();
714 			if (checker == NULL)
715 				return B_NO_INIT;
716 
717 			status_t status = checker->StopChecking();
718 
719 			if (status == B_OK) {
720 				file_cookie* cookie = (file_cookie*)_cookie;
721 				cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
722 
723 				status = user_memcpy(buffer, &checker->Control(),
724 					sizeof(check_control));
725 			}
726 
727 			volume->DeleteCheckVisitor();
728 			volume->SetCheckingThread(-1);
729 
730 			return status;
731 		}
732 		case BFS_IOCTL_CHECK_NEXT_NODE:
733 		{
734 			// check next
735 			CheckVisitor* checker = volume->CheckVisitor();
736 			if (checker == NULL)
737 				return B_NO_INIT;
738 
739 			volume->SetCheckingThread(find_thread(NULL));
740 
741 			checker->Control().errors = 0;
742 
743 			status_t status = checker->Next();
744 			if (status == B_ENTRY_NOT_FOUND) {
745 				checker->Control().status = B_ENTRY_NOT_FOUND;
746 					// tells StopChecking() that we finished the pass
747 
748 				if (checker->Pass() == BFS_CHECK_PASS_BITMAP) {
749 					if (checker->WriteBackCheckBitmap() == B_OK)
750 						status = checker->StartIndexPass();
751 				}
752 			}
753 
754 			if (status == B_OK) {
755 				status = user_memcpy(buffer, &checker->Control(),
756 					sizeof(check_control));
757 			}
758 
759 			return status;
760 		}
761 		case BFS_IOCTL_UPDATE_BOOT_BLOCK:
762 		{
763 			// let's makebootable (or anyone else) update the boot block
764 			// while BFS is mounted
765 			update_boot_block update;
766 			if (bufferLength != sizeof(update_boot_block))
767 				return B_BAD_VALUE;
768 			if (user_memcpy(&update, buffer, sizeof(update_boot_block)) != B_OK)
769 				return B_BAD_ADDRESS;
770 
771 			uint32 minOffset = offsetof(disk_super_block, pad_to_block);
772 			if (update.offset < minOffset
773 				|| update.offset >= 512 || update.length > 512 - minOffset
774 				|| update.length + update.offset > 512) {
775 				return B_BAD_VALUE;
776 			}
777 			if (user_memcpy((uint8*)&volume->SuperBlock() + update.offset,
778 					update.data, update.length) != B_OK) {
779 				return B_BAD_ADDRESS;
780 			}
781 
782 			return volume->WriteSuperBlock();
783 		}
784 		case BFS_IOCTL_RESIZE:
785 		{
786 			if (bufferLength != sizeof(uint64))
787 				return B_BAD_VALUE;
788 
789 			uint64 size;
790 			if (user_memcpy((uint8*)&size, buffer, sizeof(uint64)) != B_OK)
791 				return B_BAD_ADDRESS;
792 
793 			ResizeVisitor resizer(volume);
794 			return resizer.Resize(size, -1);
795 		}
796 
797 #ifdef DEBUG_FRAGMENTER
798 		case 56741:
799 		{
800 			BlockAllocator& allocator = volume->Allocator();
801 			allocator.Fragment();
802 			return B_OK;
803 		}
804 #endif
805 
806 #ifdef DEBUG
807 		case 56742:
808 		{
809 			// allocate all free blocks and zero them out
810 			// (a test for the BlockAllocator)!
811 			BlockAllocator& allocator = volume->Allocator();
812 			Transaction transaction(volume, 0);
813 			CachedBlock cached(volume);
814 			block_run run;
815 			while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run)
816 					== B_OK) {
817 				PRINT(("write block_run(%" B_PRId32 ", %" B_PRIu16
818 					", %" B_PRIu16 ")\n", run.allocation_group, run.start,
819 					run.length));
820 
821 				for (int32 i = 0;i < run.length;i++) {
822 					status_t status = cached.SetToWritable(transaction, run);
823 					if (status == B_OK)
824 						memset(cached.WritableBlock(), 0, volume->BlockSize());
825 				}
826 			}
827 			return B_OK;
828 		}
829 #endif
830 	}
831 	return B_DEV_INVALID_IOCTL;
832 }
833 
834 
835 /*!	Sets the open-mode flags for the open file cookie - only
836 	supports O_APPEND currently, but that should be sufficient
837 	for a file system.
838 */
839 static status_t
bfs_set_flags(fs_volume * _volume,fs_vnode * _node,void * _cookie,int flags)840 bfs_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags)
841 {
842 	FUNCTION_START(("node = %p, flags = %d", _node, flags));
843 
844 	file_cookie* cookie = (file_cookie*)_cookie;
845 	cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
846 
847 	return B_OK;
848 }
849 
850 
851 static status_t
bfs_fsync(fs_volume * _volume,fs_vnode * _node)852 bfs_fsync(fs_volume* _volume, fs_vnode* _node)
853 {
854 	FUNCTION();
855 
856 	Inode* inode = (Inode*)_node->private_node;
857 	return inode->Sync();
858 }
859 
860 
861 static status_t
bfs_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * stat)862 bfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
863 {
864 	FUNCTION();
865 
866 	Inode* inode = (Inode*)_node->private_node;
867 	fill_stat_buffer(inode, *stat);
868 	return B_OK;
869 }
870 
871 
872 static status_t
bfs_write_stat(fs_volume * _volume,fs_vnode * _node,const struct stat * stat,uint32 mask)873 bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
874 	uint32 mask)
875 {
876 	FUNCTION();
877 
878 	Volume* volume = (Volume*)_volume->private_volume;
879 	Inode* inode = (Inode*)_node->private_node;
880 
881 	if (volume->IsReadOnly())
882 		return B_READ_ONLY_DEVICE;
883 
884 	// TODO: we should definitely check a bit more if the new stats are
885 	//	valid - or even better, the VFS should check this before calling us
886 
887 	bfs_inode& node = inode->Node();
888 	bool updateTime = false;
889 	uid_t uid = geteuid();
890 
891 	bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
892 	bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
893 
894 	Transaction transaction(volume, inode->BlockNumber());
895 	inode->WriteLockInTransaction(transaction);
896 
897 	if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
898 		// Since B_STAT_SIZE is the only thing that can fail directly, we
899 		// do it first, so that the inode state will still be consistent
900 		// with the on-disk version
901 		if (inode->IsDirectory())
902 			return B_IS_A_DIRECTORY;
903 		if (!inode->IsFile())
904 			return B_BAD_VALUE;
905 		if (!hasWriteAccess)
906 			RETURN_ERROR(B_NOT_ALLOWED);
907 
908 		off_t oldSize = inode->Size();
909 
910 		status_t status = inode->SetFileSize(transaction, stat->st_size);
911 		if (status != B_OK)
912 			return status;
913 
914 		// fill the new blocks (if any) with zeros
915 		if ((mask & B_STAT_SIZE_INSECURE) == 0) {
916 			// We must not keep the inode locked during a write operation,
917 			// or else we might deadlock.
918 			rw_lock_write_unlock(&inode->Lock());
919 			inode->FillGapWithZeros(oldSize, inode->Size());
920 			rw_lock_write_lock(&inode->Lock());
921 		}
922 
923 		if (!inode->IsDeleted()) {
924 			Index index(volume);
925 			index.UpdateSize(transaction, inode);
926 
927 			updateTime = true;
928 		}
929 	}
930 
931 	if ((mask & B_STAT_UID) != 0) {
932 		// only root should be allowed
933 		if (uid != 0)
934 			RETURN_ERROR(B_NOT_ALLOWED);
935 		node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
936 		updateTime = true;
937 	}
938 
939 	if ((mask & B_STAT_GID) != 0) {
940 		// only the user or root can do that
941 		if (!isOwnerOrRoot)
942 			RETURN_ERROR(B_NOT_ALLOWED);
943 		node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
944 		updateTime = true;
945 	}
946 
947 	if ((mask & B_STAT_MODE) != 0) {
948 		// only the user or root can do that
949 		if (!isOwnerOrRoot)
950 			RETURN_ERROR(B_NOT_ALLOWED);
951 		PRINT(("original mode = %u, stat->st_mode = %u\n",
952 			(unsigned int)node.Mode(), (unsigned int)stat->st_mode));
953 		node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK)
954 			| (stat->st_mode & S_IUMSK));
955 		updateTime = true;
956 	}
957 
958 	if ((mask & B_STAT_CREATION_TIME) != 0) {
959 		// the user or root can do that or any user with write access
960 		if (!isOwnerOrRoot && !hasWriteAccess)
961 			RETURN_ERROR(B_NOT_ALLOWED);
962 		node.create_time
963 			= HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
964 	}
965 
966 	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
967 		// the user or root can do that or any user with write access
968 		if (!isOwnerOrRoot && !hasWriteAccess)
969 			RETURN_ERROR(B_NOT_ALLOWED);
970 		if (!inode->InLastModifiedIndex()) {
971 			// directory modification times are not part of the index
972 			node.last_modified_time
973 				= HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_mtim));
974 		} else if (!inode->IsDeleted()) {
975 			// Index::UpdateLastModified() will set the new time in the inode
976 			Index index(volume);
977 			index.UpdateLastModified(transaction, inode,
978 				bfs_inode::ToInode(stat->st_mtim));
979 		}
980 	}
981 
982 	if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
983 		// the user or root can do that or any user with write access
984 		if (!isOwnerOrRoot && !hasWriteAccess)
985 			RETURN_ERROR(B_NOT_ALLOWED);
986 		bigtime_t newTime;
987 		if ((mask & B_STAT_CHANGE_TIME) == 0)
988 			newTime = bfs_inode::ToInode(real_time_clock_usecs());
989 		else
990 			newTime = bfs_inode::ToInode(stat->st_ctim);
991 
992 		node.status_change_time = HOST_ENDIAN_TO_BFS_INT64(newTime);
993 	}
994 
995 	status_t status = inode->WriteBack(transaction);
996 	if (status == B_OK)
997 		status = transaction.Done();
998 	if (status == B_OK)
999 		notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(), mask);
1000 
1001 	return status;
1002 }
1003 
1004 
1005 status_t
bfs_create(fs_volume * _volume,fs_vnode * _directory,const char * name,int openMode,int mode,void ** _cookie,ino_t * _vnodeID)1006 bfs_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
1007 	int openMode, int mode, void** _cookie, ino_t* _vnodeID)
1008 {
1009 	FUNCTION_START(("name = \"%s\", perms = %d, openMode = %d\n", name, mode,
1010 		openMode));
1011 
1012 	Volume* volume = (Volume*)_volume->private_volume;
1013 	Inode* directory = (Inode*)_directory->private_node;
1014 
1015 	if (volume->IsReadOnly())
1016 		return B_READ_ONLY_DEVICE;
1017 
1018 	if (!directory->IsDirectory())
1019 		RETURN_ERROR(B_BAD_TYPE);
1020 
1021 	// We are creating the cookie at this point, so that we don't have
1022 	// to remove the inode if we don't have enough free memory later...
1023 	file_cookie* cookie = new(std::nothrow) file_cookie;
1024 	if (cookie == NULL)
1025 		RETURN_ERROR(B_NO_MEMORY);
1026 
1027 	// initialize the cookie
1028 	cookie->open_mode = openMode;
1029 	cookie->last_size = 0;
1030 	cookie->last_notification = system_time();
1031 
1032 	Transaction transaction(volume, directory->BlockNumber());
1033 
1034 	Inode* inode;
1035 	bool created;
1036 	status_t status = Inode::Create(transaction, directory, name,
1037 		S_FILE | (mode & S_IUMSK), openMode, 0, &created, _vnodeID, &inode);
1038 
1039 	// Disable the file cache, if requested?
1040 	if (status == B_OK && (openMode & O_NOCACHE) != 0
1041 		&& inode->FileCache() != NULL) {
1042 		status = file_cache_disable(inode->FileCache());
1043 	}
1044 
1045 	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
1046 
1047 	if (status == B_OK)
1048 		status = transaction.Done();
1049 
1050 	if (status == B_OK) {
1051 		// register the cookie
1052 		*_cookie = cookie;
1053 
1054 		if (created) {
1055 			notify_entry_created(volume->ID(), directory->ID(), name,
1056 				*_vnodeID);
1057 		}
1058 	} else {
1059 		entry_cache_remove(volume->ID(), directory->ID(), name);
1060 		delete cookie;
1061 	}
1062 
1063 	return status;
1064 }
1065 
1066 
1067 static status_t
bfs_create_symlink(fs_volume * _volume,fs_vnode * _directory,const char * name,const char * path,int mode)1068 bfs_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
1069 	const char* path, int mode)
1070 {
1071 	FUNCTION_START(("name = \"%s\", path = \"%s\"\n", name, path));
1072 
1073 	Volume* volume = (Volume*)_volume->private_volume;
1074 	Inode* directory = (Inode*)_directory->private_node;
1075 
1076 	if (volume->IsReadOnly())
1077 		return B_READ_ONLY_DEVICE;
1078 
1079 	if (!directory->IsDirectory())
1080 		RETURN_ERROR(B_BAD_TYPE);
1081 
1082 	status_t status = directory->CheckPermissions(W_OK);
1083 	if (status < B_OK)
1084 		RETURN_ERROR(status);
1085 
1086 	Transaction transaction(volume, directory->BlockNumber());
1087 
1088 	Inode* link;
1089 	off_t id;
1090 	status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
1091 		0, 0, NULL, &id, &link);
1092 	if (status < B_OK)
1093 		RETURN_ERROR(status);
1094 
1095 	size_t length = strlen(path);
1096 	if (length < SHORT_SYMLINK_NAME_LENGTH) {
1097 		strcpy(link->Node().short_symlink, path);
1098 	} else {
1099 		link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK
1100 			| INODE_LOGGED);
1101 
1102 		// links usually don't have a file cache attached - but we now need one
1103 		link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0));
1104 		link->SetMap(file_map_create(volume->ID(), link->ID(), 0));
1105 
1106 		// The following call will have to write the inode back, so
1107 		// we don't have to do that here...
1108 		status = link->WriteAt(transaction, 0, (const uint8*)path, &length);
1109 	}
1110 
1111 	if (status == B_OK)
1112 		status = link->WriteBack(transaction);
1113 
1114 	// Inode::Create() left the inode locked in memory, and also doesn't
1115 	// publish links
1116 	publish_vnode(volume->FSVolume(), id, link, &gBFSVnodeOps, link->Mode(), 0);
1117 	put_vnode(volume->FSVolume(), id);
1118 
1119 	if (status == B_OK) {
1120 		entry_cache_add(volume->ID(), directory->ID(), name, id);
1121 
1122 		status = transaction.Done();
1123 		if (status == B_OK)
1124 			notify_entry_created(volume->ID(), directory->ID(), name, id);
1125 		else
1126 			entry_cache_remove(volume->ID(), directory->ID(), name);
1127 	}
1128 
1129 	return status;
1130 }
1131 
1132 
1133 status_t
bfs_link(fs_volume * _volume,fs_vnode * dir,const char * name,fs_vnode * node)1134 bfs_link(fs_volume* _volume, fs_vnode* dir, const char* name, fs_vnode* node)
1135 {
1136 	FUNCTION_START(("name = \"%s\"\n", name));
1137 
1138 	// This one won't be implemented in a binary compatible BFS
1139 	return B_UNSUPPORTED;
1140 }
1141 
1142 
1143 status_t
bfs_unlink(fs_volume * _volume,fs_vnode * _directory,const char * name)1144 bfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
1145 {
1146 	FUNCTION_START(("name = \"%s\"\n", name));
1147 
1148 	if (!strcmp(name, "..") || !strcmp(name, "."))
1149 		return B_NOT_ALLOWED;
1150 
1151 	Volume* volume = (Volume*)_volume->private_volume;
1152 	Inode* directory = (Inode*)_directory->private_node;
1153 
1154 	status_t status = directory->CheckPermissions(W_OK);
1155 	if (status < B_OK)
1156 		return status;
1157 
1158 	Transaction transaction(volume, directory->BlockNumber());
1159 
1160 	off_t id;
1161 	status = directory->Remove(transaction, name, &id);
1162 	if (status == B_OK) {
1163 		entry_cache_remove(volume->ID(), directory->ID(), name);
1164 
1165 		status = transaction.Done();
1166 		if (status == B_OK)
1167 			notify_entry_removed(volume->ID(), directory->ID(), name, id);
1168 		else
1169 			entry_cache_add(volume->ID(), directory->ID(), name, id);
1170 	}
1171 	return status;
1172 }
1173 
1174 
1175 status_t
bfs_rename(fs_volume * _volume,fs_vnode * _oldDir,const char * oldName,fs_vnode * _newDir,const char * newName)1176 bfs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
1177 	fs_vnode* _newDir, const char* newName)
1178 {
1179 	FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = "
1180 		"\"%s\"\n", _oldDir, oldName, _newDir, newName));
1181 
1182 	Volume* volume = (Volume*)_volume->private_volume;
1183 	Inode* oldDirectory = (Inode*)_oldDir->private_node;
1184 	Inode* newDirectory = (Inode*)_newDir->private_node;
1185 
1186 	// are we already done?
1187 	if (oldDirectory == newDirectory && !strcmp(oldName, newName))
1188 		return B_OK;
1189 
1190 	Transaction transaction(volume, oldDirectory->BlockNumber());
1191 
1192 	oldDirectory->WriteLockInTransaction(transaction);
1193 	if (oldDirectory != newDirectory)
1194 		newDirectory->WriteLockInTransaction(transaction);
1195 
1196 	// are we allowed to do what we've been told?
1197 	status_t status = oldDirectory->CheckPermissions(W_OK);
1198 	if (status == B_OK)
1199 		status = newDirectory->CheckPermissions(W_OK);
1200 	if (status != B_OK)
1201 		return status;
1202 
1203 	// Get the directory's tree, and a pointer to the inode which should be
1204 	// changed
1205 	BPlusTree* tree = oldDirectory->Tree();
1206 	if (tree == NULL)
1207 		RETURN_ERROR(B_BAD_VALUE);
1208 
1209 	off_t id;
1210 	status = tree->Find((const uint8*)oldName, strlen(oldName), &id);
1211 	if (status != B_OK)
1212 		RETURN_ERROR(status);
1213 
1214 	Vnode vnode(volume, id);
1215 	Inode* inode;
1216 	if (vnode.Get(&inode) != B_OK)
1217 		return B_IO_ERROR;
1218 
1219 	// Don't move a directory into one of its children - we soar up
1220 	// from the newDirectory to either the root node or the old
1221 	// directory, whichever comes first.
1222 	// If we meet our inode on that way, we have to bail out.
1223 
1224 	if (oldDirectory != newDirectory) {
1225 		ino_t parent = newDirectory->ID();
1226 		ino_t root = volume->RootNode()->ID();
1227 
1228 		while (true) {
1229 			if (parent == id)
1230 				return B_BAD_VALUE;
1231 			else if (parent == root || parent == oldDirectory->ID())
1232 				break;
1233 
1234 			Vnode vnode(volume, parent);
1235 			Inode* parentNode;
1236 			if (vnode.Get(&parentNode) != B_OK)
1237 				return B_ERROR;
1238 
1239 			parent = volume->ToVnode(parentNode->Parent());
1240 		}
1241 	}
1242 
1243 	// Everything okay? Then lets get to work...
1244 
1245 	// First, try to make sure there is nothing that will stop us in
1246 	// the target directory - since this is the only non-critical
1247 	// failure, we will test this case first
1248 	BPlusTree* newTree = tree;
1249 	if (newDirectory != oldDirectory) {
1250 		newTree = newDirectory->Tree();
1251 		if (newTree == NULL)
1252 			RETURN_ERROR(B_BAD_VALUE);
1253 	}
1254 
1255 	status = newTree->Insert(transaction, (const uint8*)newName,
1256 		strlen(newName), id);
1257 	if (status == B_NAME_IN_USE) {
1258 		// If there is already a file with that name, we have to remove
1259 		// it, as long it's not a directory with files in it
1260 		off_t clobber;
1261 		if (newTree->Find((const uint8*)newName, strlen(newName), &clobber)
1262 				< B_OK)
1263 			return B_NAME_IN_USE;
1264 		if (clobber == id)
1265 			return B_BAD_VALUE;
1266 
1267 		Vnode vnode(volume, clobber);
1268 		Inode* other;
1269 		if (vnode.Get(&other) < B_OK)
1270 			return B_NAME_IN_USE;
1271 
1272 		// only allowed, if either both nodes are directories or neither is
1273 		if (inode->IsDirectory() != other->IsDirectory())
1274 			return other->IsDirectory() ? B_IS_A_DIRECTORY : B_NOT_A_DIRECTORY;
1275 
1276 		status = newDirectory->Remove(transaction, newName, NULL,
1277 			other->IsDirectory());
1278 		if (status < B_OK)
1279 			return status;
1280 
1281 		entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1282 
1283 		notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1284 			clobber);
1285 
1286 		status = newTree->Insert(transaction, (const uint8*)newName,
1287 			strlen(newName), id);
1288 	}
1289 	if (status != B_OK)
1290 		return status;
1291 
1292 	inode->WriteLockInTransaction(transaction);
1293 
1294 	volume->UpdateLiveQueriesRenameMove(inode, oldDirectory->ID(), oldName,
1295 		newDirectory->ID(), newName);
1296 
1297 	// update the name only when they differ
1298 	if (strcmp(oldName, newName)) {
1299 		status = inode->SetName(transaction, newName);
1300 		if (status == B_OK) {
1301 			Index index(volume);
1302 			index.UpdateName(transaction, oldName, newName, inode);
1303 		}
1304 	}
1305 
1306 	if (status == B_OK) {
1307 		status = tree->Remove(transaction, (const uint8*)oldName,
1308 			strlen(oldName), id);
1309 		if (status == B_OK) {
1310 			inode->Parent() = newDirectory->BlockRun();
1311 
1312 			// if it's a directory, update the parent directory pointer
1313 			// in its tree if necessary
1314 			BPlusTree* movedTree = inode->Tree();
1315 			if (oldDirectory != newDirectory
1316 				&& inode->IsDirectory()
1317 				&& movedTree != NULL) {
1318 				status = movedTree->Replace(transaction, (const uint8*)"..",
1319 					2, newDirectory->ID());
1320 
1321 				if (status == B_OK) {
1322 					// update/add the cache entry for the parent
1323 					entry_cache_add(volume->ID(), id, "..", newDirectory->ID());
1324 				}
1325 			}
1326 
1327 			if (status == B_OK && newDirectory != oldDirectory)
1328 				status = oldDirectory->ContainerContentsChanged(transaction);
1329 			if (status == B_OK)
1330 				status = newDirectory->ContainerContentsChanged(transaction);
1331 
1332 			if (status == B_OK)
1333 				status = inode->WriteBack(transaction);
1334 
1335 			if (status == B_OK) {
1336 				entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1337 				entry_cache_add(volume->ID(), newDirectory->ID(), newName, id);
1338 
1339 				status = transaction.Done();
1340 				if (status == B_OK) {
1341 					notify_entry_moved(volume->ID(), oldDirectory->ID(),
1342 						oldName, newDirectory->ID(), newName, id);
1343 					return B_OK;
1344 				}
1345 
1346 				entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1347 				entry_cache_add(volume->ID(), oldDirectory->ID(), oldName, id);
1348 			}
1349 		}
1350 	}
1351 
1352 	return status;
1353 }
1354 
1355 
1356 static status_t
bfs_open(fs_volume * _volume,fs_vnode * _node,int openMode,void ** _cookie)1357 bfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1358 {
1359 	FUNCTION();
1360 
1361 	Volume* volume = (Volume*)_volume->private_volume;
1362 	Inode* inode = (Inode*)_node->private_node;
1363 
1364 	// Opening a directory read-only is allowed, although you can't read
1365 	// any data from it.
1366 	if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
1367 		return B_IS_A_DIRECTORY;
1368 	if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
1369 		return B_NOT_A_DIRECTORY;
1370 
1371 	status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
1372 		| ((openMode & O_TRUNC) != 0 ? W_OK : 0));
1373 	if (status != B_OK)
1374 		RETURN_ERROR(status);
1375 
1376 	file_cookie* cookie = new(std::nothrow) file_cookie;
1377 	if (cookie == NULL)
1378 		RETURN_ERROR(B_NO_MEMORY);
1379 	ObjectDeleter<file_cookie> cookieDeleter(cookie);
1380 
1381 	// initialize the cookie
1382 	cookie->open_mode = openMode & BFS_OPEN_MODE_USER_MASK;
1383 	cookie->last_size = inode->Size();
1384 	cookie->last_notification = system_time();
1385 
1386 	// Disable the file cache, if requested?
1387 	CObjectDeleter<void, void, file_cache_enable> fileCacheEnabler;
1388 	if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
1389 		status = file_cache_disable(inode->FileCache());
1390 		if (status != B_OK)
1391 			return status;
1392 		fileCacheEnabler.SetTo(inode->FileCache());
1393 	}
1394 
1395 	// Should we truncate the file?
1396 	if ((openMode & O_TRUNC) != 0) {
1397 		if ((openMode & O_RWMASK) == O_RDONLY)
1398 			return B_NOT_ALLOWED;
1399 
1400 		Transaction transaction(volume, inode->BlockNumber());
1401 		inode->WriteLockInTransaction(transaction);
1402 
1403 		status_t status = inode->SetFileSize(transaction, 0);
1404 		if (status == B_OK)
1405 			status = inode->WriteBack(transaction);
1406 		if (status == B_OK)
1407 			status = transaction.Done();
1408 		if (status != B_OK)
1409 			return status;
1410 	}
1411 
1412 	fileCacheEnabler.Detach();
1413 	cookieDeleter.Detach();
1414 	*_cookie = cookie;
1415 	return B_OK;
1416 }
1417 
1418 
1419 static status_t
bfs_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)1420 bfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1421 	void* buffer, size_t* _length)
1422 {
1423 	//FUNCTION();
1424 	Inode* inode = (Inode*)_node->private_node;
1425 
1426 	if (!inode->HasUserAccessableStream()) {
1427 		*_length = 0;
1428 		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1429 	}
1430 
1431 	return inode->ReadAt(pos, (uint8*)buffer, _length);
1432 }
1433 
1434 
1435 static status_t
bfs_write(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const void * buffer,size_t * _length)1436 bfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1437 	const void* buffer, size_t* _length)
1438 {
1439 	//FUNCTION();
1440 	Volume* volume = (Volume*)_volume->private_volume;
1441 	Inode* inode = (Inode*)_node->private_node;
1442 
1443 	if (volume->IsReadOnly())
1444 		return B_READ_ONLY_DEVICE;
1445 
1446 	if (!inode->HasUserAccessableStream()) {
1447 		*_length = 0;
1448 		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1449 	}
1450 
1451 	file_cookie* cookie = (file_cookie*)_cookie;
1452 
1453 	if (cookie->open_mode & O_APPEND)
1454 		pos = inode->Size();
1455 
1456 	Transaction transaction;
1457 		// We are not starting the transaction here, since
1458 		// it might not be needed at all (the contents of
1459 		// regular files aren't logged)
1460 
1461 	status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
1462 		_length);
1463 	if (status == B_OK)
1464 		status = transaction.Done();
1465 	if (status == B_OK) {
1466 		InodeReadLocker locker(inode);
1467 
1468 		// periodically notify if the file size has changed
1469 		// TODO: should we better test for a change in the last_modified time only?
1470 		if (!inode->IsDeleted() && cookie->last_size != inode->Size()
1471 			&& system_time() > cookie->last_notification
1472 					+ INODE_NOTIFICATION_INTERVAL) {
1473 			notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1474 				B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
1475 			cookie->last_size = inode->Size();
1476 			cookie->last_notification = system_time();
1477 		}
1478 	}
1479 
1480 	return status;
1481 }
1482 
1483 
1484 static status_t
bfs_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)1485 bfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1486 {
1487 	FUNCTION();
1488 	return B_OK;
1489 }
1490 
1491 
1492 static status_t
bfs_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1493 bfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1494 {
1495 	FUNCTION();
1496 
1497 	file_cookie* cookie = (file_cookie*)_cookie;
1498 	Volume* volume = (Volume*)_volume->private_volume;
1499 	Inode* inode = (Inode*)_node->private_node;
1500 
1501 	Transaction transaction;
1502 	bool needsTrimming = false;
1503 
1504 	if (!volume->IsReadOnly() && !volume->IsCheckingThread()) {
1505 		InodeReadLocker locker(inode);
1506 		needsTrimming = inode->NeedsTrimming();
1507 
1508 		if ((cookie->open_mode & O_RWMASK) != 0
1509 			&& !inode->IsDeleted()
1510 			&& (needsTrimming
1511 				|| inode->OldLastModified() != inode->LastModified()
1512 				|| (inode->InSizeIndex()
1513 					// TODO: this can prevent the size update notification
1514 					// for nodes not in the index!
1515 					&& inode->OldSize() != inode->Size()))) {
1516 			locker.Unlock();
1517 			transaction.Start(volume, inode->BlockNumber());
1518 		}
1519 	}
1520 
1521 	status_t status = transaction.IsStarted() ? B_OK : B_ERROR;
1522 
1523 	if (status == B_OK) {
1524 		inode->WriteLockInTransaction(transaction);
1525 
1526 		// trim the preallocated blocks and update the size,
1527 		// and last_modified indices if needed
1528 		bool changedSize = false, changedTime = false;
1529 		Index index(volume);
1530 
1531 		if (needsTrimming) {
1532 			status = inode->TrimPreallocation(transaction);
1533 			if (status < B_OK) {
1534 				FATAL(("Could not trim preallocated blocks: inode %" B_PRIdINO
1535 					", transaction %d: %s!\n", inode->ID(),
1536 					(int)transaction.ID(), strerror(status)));
1537 
1538 				// we still want this transaction to succeed
1539 				status = B_OK;
1540 			}
1541 		}
1542 		if (inode->OldSize() != inode->Size()) {
1543 			if (inode->InSizeIndex())
1544 				index.UpdateSize(transaction, inode);
1545 			changedSize = true;
1546 		}
1547 		if (inode->OldLastModified() != inode->LastModified()) {
1548 			if (inode->InLastModifiedIndex()) {
1549 				index.UpdateLastModified(transaction, inode,
1550 					inode->LastModified());
1551 			}
1552 			changedTime = true;
1553 
1554 			// updating the index doesn't write back the inode
1555 			inode->WriteBack(transaction);
1556 		}
1557 
1558 		if (changedSize || changedTime) {
1559 			notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1560 				(changedTime ? B_STAT_MODIFICATION_TIME : 0)
1561 				| (changedSize ? B_STAT_SIZE : 0));
1562 		}
1563 	}
1564 	if (status == B_OK)
1565 		transaction.Done();
1566 
1567 	if ((cookie->open_mode & BFS_OPEN_MODE_CHECKING) != 0) {
1568 		// "chkbfs" exited abnormally, so we have to stop it here...
1569 		FATAL(("check process was aborted!\n"));
1570 		volume->CheckVisitor()->StopChecking();
1571 		volume->DeleteCheckVisitor();
1572 	}
1573 
1574 	if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)
1575 		file_cache_enable(inode->FileCache());
1576 
1577 	delete cookie;
1578 	return B_OK;
1579 }
1580 
1581 
1582 /*!	Checks access permissions, return B_NOT_ALLOWED if the action
1583 	is not allowed.
1584 */
1585 static status_t
bfs_access(fs_volume * _volume,fs_vnode * _node,int accessMode)1586 bfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1587 {
1588 	//FUNCTION();
1589 
1590 	Inode* inode = (Inode*)_node->private_node;
1591 	status_t status = inode->CheckPermissions(accessMode);
1592 	if (status < B_OK)
1593 		RETURN_ERROR(status);
1594 
1595 	return B_OK;
1596 }
1597 
1598 
1599 static status_t
bfs_read_link(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t * _bufferSize)1600 bfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
1601 	size_t* _bufferSize)
1602 {
1603 	FUNCTION();
1604 
1605 	Inode* inode = (Inode*)_node->private_node;
1606 
1607 	if (!inode->IsSymLink())
1608 		RETURN_ERROR(B_BAD_VALUE);
1609 
1610 	if ((inode->Flags() & INODE_LONG_SYMLINK) != 0) {
1611 		status_t status = inode->ReadAt(0, (uint8*)buffer, _bufferSize);
1612 		if (status < B_OK)
1613 			RETURN_ERROR(status);
1614 
1615 		*_bufferSize = inode->Size();
1616 		return B_OK;
1617 	}
1618 
1619 	size_t linkLength = strlen(inode->Node().short_symlink);
1620 
1621 	size_t bytesToCopy = min_c(linkLength, *_bufferSize);
1622 
1623 	*_bufferSize = linkLength;
1624 
1625 	memcpy(buffer, inode->Node().short_symlink, bytesToCopy);
1626 	return B_OK;
1627 }
1628 
1629 
1630 //	#pragma mark - Directory functions
1631 
1632 
1633 static status_t
bfs_create_dir(fs_volume * _volume,fs_vnode * _directory,const char * name,int mode)1634 bfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1635 	int mode)
1636 {
1637 	FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode));
1638 
1639 	Volume* volume = (Volume*)_volume->private_volume;
1640 	Inode* directory = (Inode*)_directory->private_node;
1641 
1642 	if (volume->IsReadOnly())
1643 		return B_READ_ONLY_DEVICE;
1644 
1645 	if (!directory->IsDirectory())
1646 		RETURN_ERROR(B_BAD_TYPE);
1647 
1648 	status_t status = directory->CheckPermissions(W_OK);
1649 	if (status < B_OK)
1650 		RETURN_ERROR(status);
1651 
1652 	Transaction transaction(volume, directory->BlockNumber());
1653 
1654 	// Inode::Create() locks the inode if we pass the "id" parameter, but we
1655 	// need it anyway
1656 	off_t id;
1657 	status = Inode::Create(transaction, directory, name,
1658 		S_DIRECTORY | (mode & S_IUMSK), 0, 0, NULL, &id);
1659 	if (status == B_OK) {
1660 		put_vnode(volume->FSVolume(), id);
1661 
1662 		entry_cache_add(volume->ID(), directory->ID(), name, id);
1663 
1664 		status = transaction.Done();
1665 		if (status == B_OK)
1666 			notify_entry_created(volume->ID(), directory->ID(), name, id);
1667 		else
1668 			entry_cache_remove(volume->ID(), directory->ID(), name);
1669 	}
1670 
1671 	return status;
1672 }
1673 
1674 
1675 static status_t
bfs_remove_dir(fs_volume * _volume,fs_vnode * _directory,const char * name)1676 bfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1677 {
1678 	FUNCTION_START(("name = \"%s\"\n", name));
1679 
1680 	Volume* volume = (Volume*)_volume->private_volume;
1681 	Inode* directory = (Inode*)_directory->private_node;
1682 
1683 	Transaction transaction(volume, directory->BlockNumber());
1684 
1685 	off_t id;
1686 	status_t status = directory->Remove(transaction, name, &id, true);
1687 	if (status == B_OK) {
1688 		// Remove the cache entry for the directory and potentially also
1689 		// the parent entry still belonging to the directory
1690 		entry_cache_remove(volume->ID(), directory->ID(), name);
1691 		entry_cache_remove(volume->ID(), id, "..");
1692 
1693 		status = transaction.Done();
1694 		if (status == B_OK)
1695 			notify_entry_removed(volume->ID(), directory->ID(), name, id);
1696 		else {
1697 			entry_cache_add(volume->ID(), directory->ID(), name, id);
1698 			entry_cache_add(volume->ID(), id, "..", id);
1699 		}
1700 	}
1701 
1702 	return status;
1703 }
1704 
1705 
1706 /*!	Opens a directory ready to be traversed.
1707 	bfs_open_dir() is also used by bfs_open_index_dir().
1708 */
1709 static status_t
bfs_open_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1710 bfs_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1711 {
1712 	FUNCTION();
1713 
1714 	Inode* inode = (Inode*)_node->private_node;
1715 	status_t status = inode->CheckPermissions(R_OK);
1716 	if (status < B_OK)
1717 		RETURN_ERROR(status);
1718 
1719 	// we don't ask here for directories only, because the bfs_open_index_dir()
1720 	// function utilizes us (so we must be able to open indices as well)
1721 	if (!inode->IsContainer())
1722 		RETURN_ERROR(B_NOT_A_DIRECTORY);
1723 
1724 	BPlusTree* tree = inode->Tree();
1725 	if (tree == NULL)
1726 		RETURN_ERROR(B_BAD_VALUE);
1727 
1728 	TreeIterator* iterator = new(std::nothrow) TreeIterator(tree);
1729 	if (iterator == NULL)
1730 		RETURN_ERROR(B_NO_MEMORY);
1731 
1732 	*_cookie = iterator;
1733 	return B_OK;
1734 }
1735 
1736 
1737 static status_t
bfs_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1738 bfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1739 	struct dirent* dirent, size_t bufferSize, uint32* _num)
1740 {
1741 	FUNCTION();
1742 
1743 	TreeIterator* iterator = (TreeIterator*)_cookie;
1744 	Volume* volume = (Volume*)_volume->private_volume;
1745 
1746 	uint32 maxCount = *_num;
1747 	uint32 count = 0;
1748 
1749 	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1750 		ino_t id;
1751 		uint16 length;
1752 		size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
1753 
1754 		status_t status = iterator->GetNextEntry(dirent->d_name, &length,
1755 			nameBufferSize, &id);
1756 
1757 		if (status == B_ENTRY_NOT_FOUND)
1758 			break;
1759 
1760 		if (status == B_BUFFER_OVERFLOW) {
1761 			// the remaining name buffer length was too small
1762 			if (count == 0)
1763 				RETURN_ERROR(B_BUFFER_OVERFLOW);
1764 			break;
1765 		}
1766 
1767 		if (status != B_OK)
1768 			RETURN_ERROR(status);
1769 
1770 		dirent->d_dev = volume->ID();
1771 		dirent->d_ino = id;
1772 
1773 		dirent = next_dirent(dirent, length, bufferSize);
1774 		count++;
1775 	}
1776 
1777 	*_num = count;
1778 	return B_OK;
1779 }
1780 
1781 
1782 /*!	Sets the TreeIterator back to the beginning of the directory. */
1783 static status_t
bfs_rewind_dir(fs_volume *,fs_vnode *,void * _cookie)1784 bfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie)
1785 {
1786 	FUNCTION();
1787 	TreeIterator* iterator = (TreeIterator*)_cookie;
1788 
1789 	return iterator->Rewind();
1790 }
1791 
1792 
1793 static status_t
bfs_close_dir(fs_volume *,fs_vnode *,void *)1794 bfs_close_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* /*_cookie*/)
1795 {
1796 	FUNCTION();
1797 	return B_OK;
1798 }
1799 
1800 
1801 static status_t
bfs_free_dir_cookie(fs_volume * _volume,fs_vnode * node,void * _cookie)1802 bfs_free_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
1803 {
1804 	delete (TreeIterator*)_cookie;
1805 	return B_OK;
1806 }
1807 
1808 
1809 //	#pragma mark - Attribute functions
1810 
1811 
1812 static status_t
bfs_open_attr_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1813 bfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1814 {
1815 	Inode* inode = (Inode*)_node->private_node;
1816 
1817 	FUNCTION();
1818 
1819 	AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
1820 	if (iterator == NULL)
1821 		RETURN_ERROR(B_NO_MEMORY);
1822 
1823 	*_cookie = iterator;
1824 	return B_OK;
1825 }
1826 
1827 
1828 static status_t
bfs_close_attr_dir(fs_volume * _volume,fs_vnode * node,void * cookie)1829 bfs_close_attr_dir(fs_volume* _volume, fs_vnode* node, void* cookie)
1830 {
1831 	FUNCTION();
1832 	return B_OK;
1833 }
1834 
1835 
1836 static status_t
bfs_free_attr_dir_cookie(fs_volume * _volume,fs_vnode * node,void * _cookie)1837 bfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
1838 {
1839 	FUNCTION();
1840 	AttributeIterator* iterator = (AttributeIterator*)_cookie;
1841 
1842 	delete iterator;
1843 	return B_OK;
1844 }
1845 
1846 
1847 static status_t
bfs_rewind_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1848 bfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1849 {
1850 	FUNCTION();
1851 
1852 	AttributeIterator* iterator = (AttributeIterator*)_cookie;
1853 	RETURN_ERROR(iterator->Rewind());
1854 }
1855 
1856 
1857 static status_t
bfs_read_attr_dir(fs_volume * _volume,fs_vnode * node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1858 bfs_read_attr_dir(fs_volume* _volume, fs_vnode* node, void* _cookie,
1859 	struct dirent* dirent, size_t bufferSize, uint32* _num)
1860 {
1861 	FUNCTION();
1862 	AttributeIterator* iterator = (AttributeIterator*)_cookie;
1863 
1864 	uint32 type;
1865 	size_t length;
1866 	status_t status = iterator->GetNext(dirent->d_name, &length, &type,
1867 		&dirent->d_ino);
1868 	if (status == B_ENTRY_NOT_FOUND) {
1869 		*_num = 0;
1870 		return B_OK;
1871 	} else if (status != B_OK) {
1872 		RETURN_ERROR(status);
1873 	}
1874 
1875 	Volume* volume = (Volume*)_volume->private_volume;
1876 
1877 	dirent->d_dev = volume->ID();
1878 	dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
1879 
1880 	*_num = 1;
1881 	return B_OK;
1882 }
1883 
1884 
1885 static status_t
bfs_create_attr(fs_volume * _volume,fs_vnode * _node,const char * name,uint32 type,int openMode,void ** _cookie)1886 bfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1887 	uint32 type, int openMode, void** _cookie)
1888 {
1889 	FUNCTION();
1890 
1891 	Volume* volume = (Volume*)_volume->private_volume;
1892 	if (volume->IsReadOnly())
1893 		return B_READ_ONLY_DEVICE;
1894 
1895 	Inode* inode = (Inode*)_node->private_node;
1896 	Attribute attribute(inode);
1897 
1898 	return attribute.Create(name, type, openMode, (attr_cookie**)_cookie);
1899 }
1900 
1901 
1902 static status_t
bfs_open_attr(fs_volume * _volume,fs_vnode * _node,const char * name,int openMode,void ** _cookie)1903 bfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1904 	int openMode, void** _cookie)
1905 {
1906 	FUNCTION();
1907 
1908 	Inode* inode = (Inode*)_node->private_node;
1909 	Attribute attribute(inode);
1910 
1911 	return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1912 }
1913 
1914 
1915 static status_t
bfs_close_attr(fs_volume * _volume,fs_vnode * _file,void * cookie)1916 bfs_close_attr(fs_volume* _volume, fs_vnode* _file, void* cookie)
1917 {
1918 	return B_OK;
1919 }
1920 
1921 
1922 static status_t
bfs_free_attr_cookie(fs_volume * _volume,fs_vnode * _file,void * cookie)1923 bfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _file, void* cookie)
1924 {
1925 	delete (attr_cookie*)cookie;
1926 	return B_OK;
1927 }
1928 
1929 
1930 static status_t
bfs_read_attr(fs_volume * _volume,fs_vnode * _file,void * _cookie,off_t pos,void * buffer,size_t * _length)1931 bfs_read_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie, off_t pos,
1932 	void* buffer, size_t* _length)
1933 {
1934 	FUNCTION();
1935 
1936 	attr_cookie* cookie = (attr_cookie*)_cookie;
1937 	Inode* inode = (Inode*)_file->private_node;
1938 
1939 	Attribute attribute(inode, cookie);
1940 
1941 	return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1942 }
1943 
1944 
1945 static status_t
bfs_write_attr(fs_volume * _volume,fs_vnode * _file,void * _cookie,off_t pos,const void * buffer,size_t * _length)1946 bfs_write_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie,
1947 	off_t pos, const void* buffer, size_t* _length)
1948 {
1949 	FUNCTION();
1950 
1951 	attr_cookie* cookie = (attr_cookie*)_cookie;
1952 	Volume* volume = (Volume*)_volume->private_volume;
1953 	Inode* inode = (Inode*)_file->private_node;
1954 
1955 	Transaction transaction(volume, inode->BlockNumber());
1956 	Attribute attribute(inode, cookie);
1957 
1958 	bool created;
1959 	status_t status = attribute.Write(transaction, cookie, pos,
1960 		(const uint8*)buffer, _length, &created);
1961 	if (status == B_OK) {
1962 		status = transaction.Done();
1963 		if (status == B_OK) {
1964 			notify_attribute_changed(volume->ID(), inode->ParentID(),
1965 				inode->ID(), cookie->name,
1966 				created ? B_ATTR_CREATED : B_ATTR_CHANGED);
1967 			notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1968 				B_STAT_CHANGE_TIME);
1969 		}
1970 	}
1971 
1972 	return status;
1973 }
1974 
1975 
1976 static status_t
bfs_read_attr_stat(fs_volume * _volume,fs_vnode * _file,void * _cookie,struct stat * stat)1977 bfs_read_attr_stat(fs_volume* _volume, fs_vnode* _file, void* _cookie,
1978 	struct stat* stat)
1979 {
1980 	FUNCTION();
1981 
1982 	attr_cookie* cookie = (attr_cookie*)_cookie;
1983 	Inode* inode = (Inode*)_file->private_node;
1984 
1985 	Attribute attribute(inode, cookie);
1986 
1987 	return attribute.Stat(*stat);
1988 }
1989 
1990 
1991 static status_t
bfs_write_attr_stat(fs_volume * _volume,fs_vnode * file,void * cookie,const struct stat * stat,int statMask)1992 bfs_write_attr_stat(fs_volume* _volume, fs_vnode* file, void* cookie,
1993 	const struct stat* stat, int statMask)
1994 {
1995 	// TODO: Implement (at least setting the size)!
1996 	return EOPNOTSUPP;
1997 }
1998 
1999 
2000 static status_t
bfs_rename_attr(fs_volume * _volume,fs_vnode * fromFile,const char * fromName,fs_vnode * toFile,const char * toName)2001 bfs_rename_attr(fs_volume* _volume, fs_vnode* fromFile, const char* fromName,
2002 	fs_vnode* toFile, const char* toName)
2003 {
2004 	FUNCTION_START(("name = \"%s\", to = \"%s\"\n", fromName, toName));
2005 
2006 	// TODO: implement bfs_rename_attr()!
2007 	// There will probably be an API to move one attribute to another file,
2008 	// making that function much more complicated - oh joy ;-)
2009 
2010 	return EOPNOTSUPP;
2011 }
2012 
2013 
2014 static status_t
bfs_remove_attr(fs_volume * _volume,fs_vnode * _node,const char * name)2015 bfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char* name)
2016 {
2017 	FUNCTION_START(("name = \"%s\"\n", name));
2018 
2019 	Volume* volume = (Volume*)_volume->private_volume;
2020 	Inode* inode = (Inode*)_node->private_node;
2021 
2022 	status_t status = inode->CheckPermissions(W_OK);
2023 	if (status != B_OK)
2024 		return status;
2025 
2026 	Transaction transaction(volume, inode->BlockNumber());
2027 
2028 	status = inode->RemoveAttribute(transaction, name);
2029 	if (status == B_OK)
2030 		status = transaction.Done();
2031 	if (status == B_OK) {
2032 		notify_attribute_changed(volume->ID(), inode->ParentID(), inode->ID(),
2033 			name, B_ATTR_REMOVED);
2034 	}
2035 
2036 	return status;
2037 }
2038 
2039 
2040 //	#pragma mark - Special Nodes
2041 
2042 
2043 status_t
bfs_create_special_node(fs_volume * _volume,fs_vnode * _directory,const char * name,fs_vnode * subVnode,mode_t mode,uint32 flags,fs_vnode * _superVnode,ino_t * _nodeID)2044 bfs_create_special_node(fs_volume* _volume, fs_vnode* _directory,
2045 	const char* name, fs_vnode* subVnode, mode_t mode, uint32 flags,
2046 	fs_vnode* _superVnode, ino_t* _nodeID)
2047 {
2048 	// no need to support entry-less nodes
2049 	if (name == NULL)
2050 		return B_UNSUPPORTED;
2051 
2052 	FUNCTION_START(("name = \"%s\", mode = %u, flags = 0x%" B_PRIx32
2053 		", subVnode: %p\n", name, (unsigned int)mode, flags, subVnode));
2054 
2055 	Volume* volume = (Volume*)_volume->private_volume;
2056 	Inode* directory = (Inode*)_directory->private_node;
2057 
2058 	if (volume->IsReadOnly())
2059 		return B_READ_ONLY_DEVICE;
2060 
2061 	if (!directory->IsDirectory())
2062 		RETURN_ERROR(B_BAD_TYPE);
2063 
2064 	status_t status = directory->CheckPermissions(W_OK);
2065 	if (status < B_OK)
2066 		RETURN_ERROR(status);
2067 
2068 	Transaction transaction(volume, directory->BlockNumber());
2069 
2070 	off_t id;
2071 	Inode* inode;
2072 	status = Inode::Create(transaction, directory, name, mode, O_EXCL, 0, NULL,
2073 		&id, &inode, subVnode ? subVnode->ops : NULL, flags);
2074 	if (status == B_OK) {
2075 		_superVnode->private_node = inode;
2076 		_superVnode->ops = &gBFSVnodeOps;
2077 		*_nodeID = id;
2078 
2079 		entry_cache_add(volume->ID(), directory->ID(), name, id);
2080 
2081 		status = transaction.Done();
2082 		if (status == B_OK)
2083 			notify_entry_created(volume->ID(), directory->ID(), name, id);
2084 		else
2085 			entry_cache_remove(volume->ID(), directory->ID(), name);
2086 	}
2087 
2088 	return status;
2089 }
2090 
2091 
2092 //	#pragma mark - Index functions
2093 
2094 
2095 static status_t
bfs_open_index_dir(fs_volume * _volume,void ** _cookie)2096 bfs_open_index_dir(fs_volume* _volume, void** _cookie)
2097 {
2098 	FUNCTION();
2099 
2100 	Volume* volume = (Volume*)_volume->private_volume;
2101 
2102 	if (volume->IndicesNode() == NULL) {
2103 		// This volume does not have any indices
2104 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2105 	}
2106 
2107 	// Since the indices root node is just a directory, and we are storing
2108 	// a pointer to it in our Volume object, we can just use the directory
2109 	// traversal functions.
2110 	// In fact we're storing it in the Volume object for that reason.
2111 
2112 	fs_vnode indicesNode;
2113 	indicesNode.private_node = volume->IndicesNode();
2114 
2115 	RETURN_ERROR(bfs_open_dir(_volume, &indicesNode, _cookie));
2116 }
2117 
2118 
2119 static status_t
bfs_close_index_dir(fs_volume * _volume,void * _cookie)2120 bfs_close_index_dir(fs_volume* _volume, void* _cookie)
2121 {
2122 	FUNCTION();
2123 
2124 	Volume* volume = (Volume*)_volume->private_volume;
2125 
2126 	fs_vnode indicesNode;
2127 	indicesNode.private_node = volume->IndicesNode();
2128 
2129 	RETURN_ERROR(bfs_close_dir(_volume, &indicesNode, _cookie));
2130 }
2131 
2132 
2133 static status_t
bfs_free_index_dir_cookie(fs_volume * _volume,void * _cookie)2134 bfs_free_index_dir_cookie(fs_volume* _volume, void* _cookie)
2135 {
2136 	FUNCTION();
2137 
2138 	Volume* volume = (Volume*)_volume->private_volume;
2139 
2140 	fs_vnode indicesNode;
2141 	indicesNode.private_node = volume->IndicesNode();
2142 
2143 	RETURN_ERROR(bfs_free_dir_cookie(_volume, &indicesNode, _cookie));
2144 }
2145 
2146 
2147 static status_t
bfs_rewind_index_dir(fs_volume * _volume,void * _cookie)2148 bfs_rewind_index_dir(fs_volume* _volume, void* _cookie)
2149 {
2150 	FUNCTION();
2151 
2152 	Volume* volume = (Volume*)_volume->private_volume;
2153 
2154 	fs_vnode indicesNode;
2155 	indicesNode.private_node = volume->IndicesNode();
2156 
2157 	RETURN_ERROR(bfs_rewind_dir(_volume, &indicesNode, _cookie));
2158 }
2159 
2160 
2161 static status_t
bfs_read_index_dir(fs_volume * _volume,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)2162 bfs_read_index_dir(fs_volume* _volume, void* _cookie, struct dirent* dirent,
2163 	size_t bufferSize, uint32* _num)
2164 {
2165 	FUNCTION();
2166 
2167 	Volume* volume = (Volume*)_volume->private_volume;
2168 
2169 	fs_vnode indicesNode;
2170 	indicesNode.private_node = volume->IndicesNode();
2171 
2172 	RETURN_ERROR(bfs_read_dir(_volume, &indicesNode, _cookie, dirent,
2173 		bufferSize, _num));
2174 }
2175 
2176 
2177 static status_t
bfs_create_index(fs_volume * _volume,const char * name,uint32 type,uint32 flags)2178 bfs_create_index(fs_volume* _volume, const char* name, uint32 type,
2179 	uint32 flags)
2180 {
2181 	FUNCTION_START(("name = \"%s\", type = %" B_PRIu32
2182 		", flags = %" B_PRIu32 "\n", name, type, flags));
2183 
2184 	Volume* volume = (Volume*)_volume->private_volume;
2185 
2186 	if (volume->IsReadOnly())
2187 		return B_READ_ONLY_DEVICE;
2188 
2189 	// only root users are allowed to create indices
2190 	if (geteuid() != 0)
2191 		return B_NOT_ALLOWED;
2192 
2193 	Transaction transaction(volume, volume->Indices());
2194 
2195 	Index index(volume);
2196 	status_t status = index.Create(transaction, name, type);
2197 
2198 	if (status == B_OK)
2199 		status = transaction.Done();
2200 
2201 	RETURN_ERROR(status);
2202 }
2203 
2204 
2205 static status_t
bfs_remove_index(fs_volume * _volume,const char * name)2206 bfs_remove_index(fs_volume* _volume, const char* name)
2207 {
2208 	FUNCTION();
2209 
2210 	Volume* volume = (Volume*)_volume->private_volume;
2211 
2212 	if (volume->IsReadOnly())
2213 		return B_READ_ONLY_DEVICE;
2214 
2215 	// only root users are allowed to remove indices
2216 	if (geteuid() != 0)
2217 		return B_NOT_ALLOWED;
2218 
2219 	Inode* indices = volume->IndicesNode();
2220 	if (indices == NULL)
2221 		return B_ENTRY_NOT_FOUND;
2222 
2223 	Transaction transaction(volume, volume->Indices());
2224 
2225 	status_t status = indices->Remove(transaction, name);
2226 	if (status == B_OK)
2227 		status = transaction.Done();
2228 
2229 	RETURN_ERROR(status);
2230 }
2231 
2232 
2233 static status_t
bfs_stat_index(fs_volume * _volume,const char * name,struct stat * stat)2234 bfs_stat_index(fs_volume* _volume, const char* name, struct stat* stat)
2235 {
2236 	FUNCTION_START(("name = %s\n", name));
2237 
2238 	Volume* volume = (Volume*)_volume->private_volume;
2239 
2240 	Index index(volume);
2241 	status_t status = index.SetTo(name);
2242 	if (status < B_OK)
2243 		RETURN_ERROR(status);
2244 
2245 	bfs_inode& node = index.Node()->Node();
2246 
2247 	stat->st_type = index.Type();
2248 	stat->st_mode = node.Mode();
2249 
2250 	stat->st_size = node.data.Size();
2251 	stat->st_blocks = index.Node()->AllocatedSize() / 512;
2252 
2253 	stat->st_nlink = 1;
2254 	stat->st_blksize = 65536;
2255 
2256 	stat->st_uid = node.UserID();
2257 	stat->st_gid = node.GroupID();
2258 
2259 	fill_stat_time(node, *stat);
2260 
2261 	return B_OK;
2262 }
2263 
2264 
2265 //	#pragma mark - Query functions
2266 
2267 
2268 static status_t
bfs_open_query(fs_volume * _volume,const char * queryString,uint32 flags,port_id port,uint32 token,void ** _cookie)2269 bfs_open_query(fs_volume* _volume, const char* queryString, uint32 flags,
2270 	port_id port, uint32 token, void** _cookie)
2271 {
2272 	FUNCTION_START(("bfs_open_query(\"%s\", flags = %" B_PRIu32
2273 		", port_id = %" B_PRId32 ", token = %" B_PRIu32 ")\n",
2274 		queryString, flags, port, token));
2275 
2276 	Volume* volume = (Volume*)_volume->private_volume;
2277 
2278 	Query* query;
2279 	status_t error = Query::Create(volume, queryString, flags, port, token, query);
2280 	if (error != B_OK)
2281 		return error;
2282 
2283 	*_cookie = (void*)query;
2284 
2285 	return B_OK;
2286 }
2287 
2288 
2289 static status_t
bfs_close_query(fs_volume * _volume,void * cookie)2290 bfs_close_query(fs_volume* _volume, void* cookie)
2291 {
2292 	FUNCTION();
2293 	return B_OK;
2294 }
2295 
2296 
2297 static status_t
bfs_free_query_cookie(fs_volume * _volume,void * cookie)2298 bfs_free_query_cookie(fs_volume* _volume, void* cookie)
2299 {
2300 	FUNCTION();
2301 
2302 	Query* query = (Query*)cookie;
2303 	delete query;
2304 
2305 	return B_OK;
2306 }
2307 
2308 
2309 static status_t
bfs_read_query(fs_volume *,void * cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)2310 bfs_read_query(fs_volume* /*_volume*/, void* cookie, struct dirent* dirent,
2311 	size_t bufferSize, uint32* _num)
2312 {
2313 	FUNCTION();
2314 	Query* query = (Query*)cookie;
2315 	status_t status = query->GetNextEntry(dirent, bufferSize);
2316 	if (status == B_OK)
2317 		*_num = 1;
2318 	else if (status == B_ENTRY_NOT_FOUND)
2319 		*_num = 0;
2320 	else
2321 		return status;
2322 
2323 	return B_OK;
2324 }
2325 
2326 
2327 static status_t
bfs_rewind_query(fs_volume *,void * cookie)2328 bfs_rewind_query(fs_volume* /*_volume*/, void* cookie)
2329 {
2330 	FUNCTION();
2331 
2332 	Query* query = (Query*)cookie;
2333 	return query->Rewind();
2334 }
2335 
2336 
2337 //	#pragma mark -
2338 
2339 
2340 static uint32
bfs_get_supported_operations(partition_data * partition,uint32 mask)2341 bfs_get_supported_operations(partition_data* partition, uint32 mask)
2342 {
2343 	// TODO: We should at least check the partition size.
2344 	return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2345 		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2346 		| B_DISK_SYSTEM_SUPPORTS_WRITING;
2347 }
2348 
2349 
2350 static status_t
bfs_initialize(int fd,partition_id partitionID,const char * name,const char * parameterString,off_t,disk_job_id job)2351 bfs_initialize(int fd, partition_id partitionID, const char* name,
2352 	const char* parameterString, off_t /*partitionSize*/, disk_job_id job)
2353 {
2354 	// check name
2355 	status_t status = check_volume_name(name);
2356 	if (status != B_OK)
2357 		return status;
2358 
2359 	// parse parameters
2360 	initialize_parameters parameters;
2361 	status = parse_initialize_parameters(parameterString, parameters);
2362 	if (status != B_OK)
2363 		return status;
2364 
2365 	update_disk_device_job_progress(job, 0);
2366 
2367 	// initialize the volume
2368 	Volume volume(NULL);
2369 	status = volume.Initialize(fd, name, parameters.blockSize,
2370 		parameters.flags);
2371 	if (status < B_OK) {
2372 		INFORM(("Initializing volume failed: %s\n", strerror(status)));
2373 		return status;
2374 	}
2375 
2376 	// rescan partition
2377 	status = scan_partition(partitionID);
2378 	if (status != B_OK)
2379 		return status;
2380 
2381 	update_disk_device_job_progress(job, 1);
2382 
2383 	// print some info, if desired
2384 	if (parameters.verbose) {
2385 		disk_super_block super = volume.SuperBlock();
2386 
2387 		INFORM(("Disk was initialized successfully.\n"));
2388 		INFORM(("\tname: \"%s\"\n", super.name));
2389 		INFORM(("\tnum blocks: %" B_PRIdOFF "\n", super.NumBlocks()));
2390 		INFORM(("\tused blocks: %" B_PRIdOFF "\n", super.UsedBlocks()));
2391 		INFORM(("\tblock size: %u bytes\n", (unsigned)super.BlockSize()));
2392 		INFORM(("\tnum allocation groups: %d\n",
2393 			(int)super.AllocationGroups()));
2394 		INFORM(("\tallocation group size: %ld blocks\n",
2395 			1L << super.AllocationGroupShift()));
2396 		INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length()));
2397 	}
2398 
2399 	return B_OK;
2400 }
2401 
2402 
2403 static status_t
bfs_uninitialize(int fd,partition_id partitionID,off_t partitionSize,uint32 blockSize,disk_job_id job)2404 bfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
2405 	uint32 blockSize, disk_job_id job)
2406 {
2407 	if (blockSize == 0)
2408 		return B_BAD_VALUE;
2409 
2410 	update_disk_device_job_progress(job, 0.0);
2411 
2412 	// just overwrite the superblock
2413 	disk_super_block superBlock;
2414 	memset(&superBlock, 0, sizeof(superBlock));
2415 
2416 	if (write_pos(fd, 512, &superBlock, sizeof(superBlock)) < 0)
2417 		return errno;
2418 
2419 	update_disk_device_job_progress(job, 1.0);
2420 
2421 	return B_OK;
2422 }
2423 
2424 
2425 //	#pragma mark -
2426 
2427 
2428 static status_t
bfs_std_ops(int32 op,...)2429 bfs_std_ops(int32 op, ...)
2430 {
2431 	switch (op) {
2432 		case B_MODULE_INIT:
2433 #ifdef BFS_DEBUGGER_COMMANDS
2434 			add_debugger_commands();
2435 #endif
2436 			return B_OK;
2437 		case B_MODULE_UNINIT:
2438 #ifdef BFS_DEBUGGER_COMMANDS
2439 			remove_debugger_commands();
2440 #endif
2441 			return B_OK;
2442 
2443 		default:
2444 			return B_ERROR;
2445 	}
2446 }
2447 
2448 fs_volume_ops gBFSVolumeOps = {
2449 	&bfs_unmount,
2450 	&bfs_read_fs_stat,
2451 	&bfs_write_fs_stat,
2452 	&bfs_sync,
2453 	&bfs_get_vnode,
2454 
2455 	/* index directory & index operations */
2456 	&bfs_open_index_dir,
2457 	&bfs_close_index_dir,
2458 	&bfs_free_index_dir_cookie,
2459 	&bfs_read_index_dir,
2460 	&bfs_rewind_index_dir,
2461 
2462 	&bfs_create_index,
2463 	&bfs_remove_index,
2464 	&bfs_stat_index,
2465 
2466 	/* query operations */
2467 	&bfs_open_query,
2468 	&bfs_close_query,
2469 	&bfs_free_query_cookie,
2470 	&bfs_read_query,
2471 	&bfs_rewind_query,
2472 };
2473 
2474 fs_vnode_ops gBFSVnodeOps = {
2475 	/* vnode operations */
2476 	&bfs_lookup,
2477 	&bfs_get_vnode_name,
2478 	&bfs_put_vnode,
2479 	&bfs_remove_vnode,
2480 
2481 	/* VM file access */
2482 	&bfs_can_page,
2483 	&bfs_read_pages,
2484 	&bfs_write_pages,
2485 
2486 	&bfs_io,
2487 	NULL,	// cancel_io()
2488 
2489 	&bfs_get_file_map,
2490 
2491 	&bfs_ioctl,
2492 	&bfs_set_flags,
2493 	NULL,	// fs_select
2494 	NULL,	// fs_deselect
2495 	&bfs_fsync,
2496 
2497 	&bfs_read_link,
2498 	&bfs_create_symlink,
2499 
2500 	&bfs_link,
2501 	&bfs_unlink,
2502 	&bfs_rename,
2503 
2504 	&bfs_access,
2505 	&bfs_read_stat,
2506 	&bfs_write_stat,
2507 	NULL,	// fs_preallocate
2508 
2509 	/* file operations */
2510 	&bfs_create,
2511 	&bfs_open,
2512 	&bfs_close,
2513 	&bfs_free_cookie,
2514 	&bfs_read,
2515 	&bfs_write,
2516 
2517 	/* directory operations */
2518 	&bfs_create_dir,
2519 	&bfs_remove_dir,
2520 	&bfs_open_dir,
2521 	&bfs_close_dir,
2522 	&bfs_free_dir_cookie,
2523 	&bfs_read_dir,
2524 	&bfs_rewind_dir,
2525 
2526 	/* attribute directory operations */
2527 	&bfs_open_attr_dir,
2528 	&bfs_close_attr_dir,
2529 	&bfs_free_attr_dir_cookie,
2530 	&bfs_read_attr_dir,
2531 	&bfs_rewind_attr_dir,
2532 
2533 	/* attribute operations */
2534 	&bfs_create_attr,
2535 	&bfs_open_attr,
2536 	&bfs_close_attr,
2537 	&bfs_free_attr_cookie,
2538 	&bfs_read_attr,
2539 	&bfs_write_attr,
2540 
2541 	&bfs_read_attr_stat,
2542 	&bfs_write_attr_stat,
2543 	&bfs_rename_attr,
2544 	&bfs_remove_attr,
2545 
2546 	/* special nodes */
2547 	&bfs_create_special_node
2548 };
2549 
2550 static file_system_module_info sBeFileSystem = {
2551 	{
2552 		"file_systems/bfs" BFS_ENDIAN_SUFFIX B_CURRENT_FS_API_VERSION,
2553 		0,
2554 		bfs_std_ops,
2555 	},
2556 
2557 	"bfs" BFS_ENDIAN_SUFFIX,						// short_name
2558 	"Be File System" BFS_ENDIAN_PRETTY_SUFFIX,		// pretty_name
2559 
2560 	// DDM flags
2561 	0
2562 //	| B_DISK_SYSTEM_SUPPORTS_CHECKING
2563 //	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
2564 //	| B_DISK_SYSTEM_SUPPORTS_RESIZING
2565 //	| B_DISK_SYSTEM_SUPPORTS_MOVING
2566 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
2567 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
2568 	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2569 	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2570 //	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING
2571 //	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED
2572 //	| B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
2573 //	| B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
2574 //	| B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED
2575 //	| B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED
2576 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED
2577 //	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED
2578 	| B_DISK_SYSTEM_SUPPORTS_WRITING
2579 	,
2580 
2581 	// scanning
2582 	bfs_identify_partition,
2583 	bfs_scan_partition,
2584 	bfs_free_identify_partition_cookie,
2585 	NULL,	// free_partition_content_cookie()
2586 
2587 	&bfs_mount,
2588 
2589 	/* capability querying operations */
2590 	&bfs_get_supported_operations,
2591 
2592 	NULL,	// validate_resize
2593 	NULL,	// validate_move
2594 	NULL,	// validate_set_content_name
2595 	NULL,	// validate_set_content_parameters
2596 	NULL,	// validate_initialize,
2597 
2598 	/* shadow partition modification */
2599 	NULL,	// shadow_changed
2600 
2601 	/* writing */
2602 	NULL,	// defragment
2603 	NULL,	// repair
2604 	NULL,	// resize
2605 	NULL,	// move
2606 	NULL,	// set_content_name
2607 	NULL,	// set_content_parameters
2608 	bfs_initialize,
2609 	bfs_uninitialize
2610 };
2611 
2612 module_info* modules[] = {
2613 	(module_info*)&sBeFileSystem,
2614 	NULL,
2615 };
2616