xref: /haiku/src/add-ons/kernel/file_systems/bfs/Volume.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2001-2012, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 //! superblock, mounting, etc.
8 
9 
10 #include "Attribute.h"
11 #include "Debug.h"
12 #include "Inode.h"
13 #include "Journal.h"
14 #include "Query.h"
15 #include "Volume.h"
16 
17 
18 static const int32 kDesiredAllocationGroups = 56;
19 	// This is the number of allocation groups that will be tried
20 	// to be given for newly initialized disks.
21 	// That's only relevant for smaller disks, though, since any
22 	// of today's disk sizes already reach the maximum length
23 	// of an allocation group (65536 blocks).
24 	// It seems to create appropriate numbers for smaller disks
25 	// with this setting, though (i.e. you can create a 400 MB
26 	// file on a 1 GB disk without the need for double indirect
27 	// blocks).
28 
29 
30 class DeviceOpener {
31 public:
32 						DeviceOpener(int fd, int mode);
33 						DeviceOpener(const char* device, int mode);
34 						~DeviceOpener();
35 
36 			int			Open(const char* device, int mode);
37 			int			Open(int fd, int mode);
38 			void*		InitCache(off_t numBlocks, uint32 blockSize);
39 			void		RemoveCache(bool allowWrites);
40 
41 			void		Keep();
42 
43 			int			Device() const { return fDevice; }
44 			int			Mode() const { return fMode; }
45 			bool		IsReadOnly() const { return _IsReadOnly(fMode); }
46 
47 			status_t	GetSize(off_t* _size, uint32* _blockSize = NULL);
48 
49 private:
50 	static	bool		_IsReadOnly(int mode)
51 							{ return (mode & O_RWMASK) == O_RDONLY;}
52 	static	bool		_IsReadWrite(int mode)
53 							{ return (mode & O_RWMASK) == O_RDWR;}
54 
55 			int			fDevice;
56 			int			fMode;
57 			void*		fBlockCache;
58 };
59 
60 
61 DeviceOpener::DeviceOpener(const char* device, int mode)
62 	:
63 	fBlockCache(NULL)
64 {
65 	Open(device, mode);
66 }
67 
68 
69 DeviceOpener::DeviceOpener(int fd, int mode)
70 	:
71 	fBlockCache(NULL)
72 {
73 	Open(fd, mode);
74 }
75 
76 
77 DeviceOpener::~DeviceOpener()
78 {
79 	if (fDevice >= 0) {
80 		RemoveCache(false);
81 		close(fDevice);
82 	}
83 }
84 
85 
86 int
87 DeviceOpener::Open(const char* device, int mode)
88 {
89 	fDevice = open(device, mode | O_NOCACHE);
90 	if (fDevice < 0)
91 		fDevice = errno;
92 
93 	if (fDevice < 0 && _IsReadWrite(mode)) {
94 		// try again to open read-only (don't rely on a specific error code)
95 		return Open(device, O_RDONLY | O_NOCACHE);
96 	}
97 
98 	if (fDevice >= 0) {
99 		// opening succeeded
100 		fMode = mode;
101 		if (_IsReadWrite(mode)) {
102 			// check out if the device really allows for read/write access
103 			device_geometry geometry;
104 			if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
105 				if (geometry.read_only) {
106 					// reopen device read-only
107 					close(fDevice);
108 					return Open(device, O_RDONLY | O_NOCACHE);
109 				}
110 			}
111 		}
112 	}
113 
114 	return fDevice;
115 }
116 
117 
118 int
119 DeviceOpener::Open(int fd, int mode)
120 {
121 	fDevice = dup(fd);
122 	if (fDevice < 0)
123 		return errno;
124 
125 	fMode = mode;
126 
127 	return fDevice;
128 }
129 
130 
131 void*
132 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
133 {
134 	return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
135 		IsReadOnly());
136 }
137 
138 
139 void
140 DeviceOpener::RemoveCache(bool allowWrites)
141 {
142 	if (fBlockCache == NULL)
143 		return;
144 
145 	block_cache_delete(fBlockCache, allowWrites);
146 	fBlockCache = NULL;
147 }
148 
149 
150 void
151 DeviceOpener::Keep()
152 {
153 	fDevice = -1;
154 }
155 
156 
157 /*!	Returns the size of the device in bytes. It uses B_GET_GEOMETRY
158 	to compute the size, or fstat() if that failed.
159 */
160 status_t
161 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
162 {
163 	device_geometry geometry;
164 	if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
165 		// maybe it's just a file
166 		struct stat stat;
167 		if (fstat(fDevice, &stat) < 0)
168 			return B_ERROR;
169 
170 		if (_size)
171 			*_size = stat.st_size;
172 		if (_blockSize)	// that shouldn't cause us any problems
173 			*_blockSize = 512;
174 
175 		return B_OK;
176 	}
177 
178 	if (_size) {
179 		*_size = 1LL * geometry.head_count * geometry.cylinder_count
180 			* geometry.sectors_per_track * geometry.bytes_per_sector;
181 	}
182 	if (_blockSize)
183 		*_blockSize = geometry.bytes_per_sector;
184 
185 	return B_OK;
186 }
187 
188 
189 //	#pragma mark -
190 
191 
192 bool
193 disk_super_block::IsValid() const
194 {
195 	if (Magic1() != (int32)SUPER_BLOCK_MAGIC1
196 		|| Magic2() != (int32)SUPER_BLOCK_MAGIC2
197 		|| Magic3() != (int32)SUPER_BLOCK_MAGIC3
198 		|| (int32)block_size != inode_size
199 		|| ByteOrder() != SUPER_BLOCK_FS_LENDIAN
200 		|| (1UL << BlockShift()) != BlockSize()
201 		|| AllocationGroups() < 1
202 		|| AllocationGroupShift() < 1
203 		|| BlocksPerAllocationGroup() < 1
204 		|| NumBlocks() < 10
205 		|| AllocationGroups() != divide_roundup(NumBlocks(),
206 			1L << AllocationGroupShift()))
207 		return false;
208 
209 	return true;
210 }
211 
212 
213 void
214 disk_super_block::Initialize(const char* diskName, off_t numBlocks,
215 	uint32 blockSize)
216 {
217 	memset(this, 0, sizeof(disk_super_block));
218 
219 	magic1 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC1);
220 	magic2 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC2);
221 	magic3 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC3);
222 	fs_byte_order = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_FS_LENDIAN);
223 	flags = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_DISK_CLEAN);
224 
225 	strlcpy(name, diskName, sizeof(name));
226 
227 	int32 blockShift = 9;
228 	while ((1UL << blockShift) < blockSize) {
229 		blockShift++;
230 	}
231 
232 	block_size = inode_size = HOST_ENDIAN_TO_BFS_INT32(blockSize);
233 	block_shift = HOST_ENDIAN_TO_BFS_INT32(blockShift);
234 
235 	num_blocks = HOST_ENDIAN_TO_BFS_INT64(numBlocks);
236 	used_blocks = 0;
237 
238 	// Get the minimum ag_shift (that's determined by the block size)
239 
240 	int32 bitsPerBlock = blockSize << 3;
241 	off_t bitmapBlocks = (numBlocks + bitsPerBlock - 1) / bitsPerBlock;
242 	int32 blocksPerGroup = 1;
243 	int32 groupShift = 13;
244 
245 	for (int32 i = 8192; i < bitsPerBlock; i *= 2) {
246 		groupShift++;
247 	}
248 
249 	// Many allocation groups help applying allocation policies, but if
250 	// they are too small, we will need to many block_runs to cover large
251 	// files (see above to get an explanation of the kDesiredAllocationGroups
252 	// constant).
253 
254 	int32 numGroups;
255 
256 	while (true) {
257 		numGroups = (bitmapBlocks + blocksPerGroup - 1) / blocksPerGroup;
258 		if (numGroups > kDesiredAllocationGroups) {
259 			if (groupShift == 16)
260 				break;
261 
262 			groupShift++;
263 			blocksPerGroup *= 2;
264 		} else
265 			break;
266 	}
267 
268 	num_ags = HOST_ENDIAN_TO_BFS_INT32(numGroups);
269 	blocks_per_ag = HOST_ENDIAN_TO_BFS_INT32(blocksPerGroup);
270 	ag_shift = HOST_ENDIAN_TO_BFS_INT32(groupShift);
271 }
272 
273 
274 //	#pragma mark -
275 
276 
277 Volume::Volume(fs_volume* volume)
278 	:
279 	fVolume(volume),
280 	fBlockAllocator(this),
281 	fRootNode(NULL),
282 	fIndicesNode(NULL),
283 	fDirtyCachedBlocks(0),
284 	fFlags(0),
285 	fCheckingThread(-1)
286 {
287 	mutex_init(&fLock, "bfs volume");
288 	mutex_init(&fQueryLock, "bfs queries");
289 }
290 
291 
292 Volume::~Volume()
293 {
294 	mutex_destroy(&fQueryLock);
295 	mutex_destroy(&fLock);
296 }
297 
298 
299 bool
300 Volume::IsValidSuperBlock() const
301 {
302 	return fSuperBlock.IsValid();
303 }
304 
305 
306 /*!	Checks whether the given block number may be the location of an inode block.
307 */
308 bool
309 Volume::IsValidInodeBlock(off_t block) const
310 {
311 	return block > fSuperBlock.LogEnd() && block < NumBlocks();
312 }
313 
314 
315 void
316 Volume::Panic()
317 {
318 	FATAL(("Disk corrupted... switch to read-only mode!\n"));
319 	fFlags |= VOLUME_READ_ONLY;
320 #if KDEBUG
321 	kernel_debugger("BFS panics!");
322 #endif
323 }
324 
325 
326 status_t
327 Volume::Mount(const char* deviceName, uint32 flags)
328 {
329 	// TODO: validate the FS in write mode as well!
330 #if (B_HOST_IS_LENDIAN && defined(BFS_BIG_ENDIAN_ONLY)) \
331 	|| (B_HOST_IS_BENDIAN && defined(BFS_LITTLE_ENDIAN_ONLY))
332 	// in big endian mode, we only mount read-only for now
333 	flags |= B_MOUNT_READ_ONLY;
334 #endif
335 
336 	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
337 		? O_RDONLY : O_RDWR);
338 	fDevice = opener.Device();
339 	if (fDevice < B_OK)
340 		RETURN_ERROR(fDevice);
341 
342 	if (opener.IsReadOnly())
343 		fFlags |= VOLUME_READ_ONLY;
344 
345 	// read the superblock
346 	if (Identify(fDevice, &fSuperBlock) != B_OK) {
347 		FATAL(("invalid superblock!\n"));
348 		return B_BAD_VALUE;
349 	}
350 
351 	// initialize short hands to the superblock (to save byte swapping)
352 	fBlockSize = fSuperBlock.BlockSize();
353 	fBlockShift = fSuperBlock.BlockShift();
354 	fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
355 
356 	// check if the device size is large enough to hold the file system
357 	off_t diskSize;
358 	if (opener.GetSize(&diskSize, &fDeviceBlockSize) != B_OK)
359 		RETURN_ERROR(B_ERROR);
360 	if (diskSize < (NumBlocks() << BlockShift()))
361 		RETURN_ERROR(B_BAD_VALUE);
362 
363 	// set the current log pointers, so that journaling will work correctly
364 	fLogStart = fSuperBlock.LogStart();
365 	fLogEnd = fSuperBlock.LogEnd();
366 
367 	if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL)
368 		return B_ERROR;
369 
370 	fJournal = new(std::nothrow) Journal(this);
371 	if (fJournal == NULL)
372 		return B_NO_MEMORY;
373 
374 	status_t status = fJournal->InitCheck();
375 	if (status < B_OK) {
376 		FATAL(("could not initialize journal: %s!\n", strerror(status)));
377 		return status;
378 	}
379 
380 	// replaying the log is the first thing we will do on this disk
381 	status = fJournal->ReplayLog();
382 	if (status != B_OK) {
383 		FATAL(("Replaying log failed, data may be corrupted, volume "
384 			"read-only.\n"));
385 		fFlags |= VOLUME_READ_ONLY;
386 			// TODO: if this is the boot volume, Bootscript will assume this
387 			// is a CD...
388 			// TODO: it would be nice to have a user visible alert instead
389 			// of letting him just find this in the syslog.
390 	}
391 
392 	status = fBlockAllocator.Initialize();
393 	if (status != B_OK) {
394 		FATAL(("could not initialize block bitmap allocator!\n"));
395 		return status;
396 	}
397 
398 	fRootNode = new(std::nothrow) Inode(this, ToVnode(Root()));
399 	if (fRootNode != NULL && fRootNode->InitCheck() == B_OK) {
400 		status = publish_vnode(fVolume, ToVnode(Root()), (void*)fRootNode,
401 			&gBFSVnodeOps, fRootNode->Mode(), 0);
402 		if (status == B_OK) {
403 			// try to get indices root dir
404 
405 			if (!Indices().IsZero()) {
406 				fIndicesNode = new(std::nothrow) Inode(this,
407 					ToVnode(Indices()));
408 			}
409 
410 			if (fIndicesNode == NULL
411 				|| fIndicesNode->InitCheck() < B_OK
412 				|| !fIndicesNode->IsContainer()) {
413 				INFORM(("bfs: volume doesn't have indices!\n"));
414 
415 				if (fIndicesNode) {
416 					// if this is the case, the index root node is gone bad,
417 					// and BFS switch to read-only mode
418 					fFlags |= VOLUME_READ_ONLY;
419 					delete fIndicesNode;
420 					fIndicesNode = NULL;
421 				}
422 			} else {
423 				// we don't use the vnode layer to access the indices node
424 			}
425 		} else {
426 			FATAL(("could not create root node: publish_vnode() failed!\n"));
427 			delete fRootNode;
428 			return status;
429 		}
430 	} else {
431 		status = B_BAD_VALUE;
432 		FATAL(("could not create root node!\n"));
433 		return status;
434 	}
435 
436 	// all went fine
437 	opener.Keep();
438 	return B_OK;
439 }
440 
441 
442 status_t
443 Volume::Unmount()
444 {
445 	put_vnode(fVolume, ToVnode(Root()));
446 
447 	fBlockAllocator.Uninitialize();
448 
449 	// This will also flush the log & all blocks to disk
450 	delete fJournal;
451 	fJournal = NULL;
452 
453 	delete fIndicesNode;
454 
455 	block_cache_delete(fBlockCache, !IsReadOnly());
456 	close(fDevice);
457 
458 	return B_OK;
459 }
460 
461 
462 status_t
463 Volume::Sync()
464 {
465 	return fJournal->FlushLogAndBlocks();
466 }
467 
468 
469 status_t
470 Volume::ValidateBlockRun(block_run run)
471 {
472 	if (run.AllocationGroup() < 0
473 		|| run.AllocationGroup() > (int32)AllocationGroups()
474 		|| run.Start() > (1UL << AllocationGroupShift())
475 		|| run.length == 0
476 		|| uint32(run.Length() + run.Start())
477 				> (1UL << AllocationGroupShift())) {
478 		Panic();
479 		FATAL(("*** invalid run(%d,%d,%d)\n", (int)run.AllocationGroup(),
480 			run.Start(), run.Length()));
481 		return B_BAD_DATA;
482 	}
483 	return B_OK;
484 }
485 
486 
487 block_run
488 Volume::ToBlockRun(off_t block) const
489 {
490 	block_run run;
491 	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(
492 		block >> AllocationGroupShift());
493 	run.start = HOST_ENDIAN_TO_BFS_INT16(
494 		block & ((1LL << AllocationGroupShift()) - 1));
495 	run.length = HOST_ENDIAN_TO_BFS_INT16(1);
496 	return run;
497 }
498 
499 
500 status_t
501 Volume::CreateIndicesRoot(Transaction& transaction)
502 {
503 	off_t id;
504 	status_t status = Inode::Create(transaction, NULL, NULL,
505 		S_INDEX_DIR | S_STR_INDEX | S_DIRECTORY | 0700, 0, 0, NULL, &id,
506 		&fIndicesNode);
507 	if (status < B_OK)
508 		RETURN_ERROR(status);
509 
510 	fSuperBlock.indices = ToBlockRun(id);
511 	return WriteSuperBlock();
512 }
513 
514 
515 status_t
516 Volume::CreateVolumeID(Transaction& transaction)
517 {
518 	Attribute attr(fRootNode);
519 	status_t status;
520 	attr_cookie* cookie;
521 	status = attr.Create("be:volume_id", B_UINT64_TYPE, O_RDWR, &cookie);
522 	if (status == B_OK) {
523 		static bool seeded = false;
524 		if (!seeded) {
525 			// seed the random number generator for the be:volume_id attribute.
526 			srand(time(NULL));
527 			seeded = true;
528 		}
529 		uint64_t id;
530 		size_t length = sizeof(id);
531 		id = ((uint64_t)rand() << 32) | rand();
532 		attr.Write(transaction, cookie, 0, (uint8_t *)&id, &length, NULL);
533 	}
534 	return status;
535 }
536 
537 
538 
539 status_t
540 Volume::AllocateForInode(Transaction& transaction, const Inode* parent,
541 	mode_t type, block_run& run)
542 {
543 	return fBlockAllocator.AllocateForInode(transaction, &parent->BlockRun(),
544 		type, run);
545 }
546 
547 
548 status_t
549 Volume::WriteSuperBlock()
550 {
551 	if (write_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block))
552 			!= sizeof(disk_super_block))
553 		return B_IO_ERROR;
554 
555 	return B_OK;
556 }
557 
558 
559 void
560 Volume::UpdateLiveQueries(Inode* inode, const char* attribute, int32 type,
561 	const uint8* oldKey, size_t oldLength, const uint8* newKey,
562 	size_t newLength)
563 {
564 	MutexLocker _(fQueryLock);
565 
566 	SinglyLinkedList<Query>::Iterator iterator = fQueries.GetIterator();
567 	while (iterator.HasNext()) {
568 		Query* query = iterator.Next();
569 		query->LiveUpdate(inode, attribute, type, oldKey, oldLength, newKey,
570 			newLength);
571 	}
572 }
573 
574 
575 void
576 Volume::UpdateLiveQueriesRenameMove(Inode* inode, ino_t oldDirectoryID,
577 	const char* oldName, ino_t newDirectoryID, const char* newName)
578 {
579 	MutexLocker _(fQueryLock);
580 
581 	size_t oldLength = strlen(oldName);
582 	size_t newLength = strlen(newName);
583 
584 	SinglyLinkedList<Query>::Iterator iterator = fQueries.GetIterator();
585 	while (iterator.HasNext()) {
586 		Query* query = iterator.Next();
587 		query->LiveUpdateRenameMove(inode, oldDirectoryID, oldName, oldLength,
588 			newDirectoryID, newName, newLength);
589 	}
590 }
591 
592 
593 /*!	Checks if there is a live query whose results depend on the presence
594 	or value of the specified attribute.
595 	Don't use it if you already have all the data together to evaluate
596 	the queries - it wouldn't safe you anything in this case.
597 */
598 bool
599 Volume::CheckForLiveQuery(const char* attribute)
600 {
601 	// TODO: check for a live query that depends on the specified attribute
602 	return true;
603 }
604 
605 
606 void
607 Volume::AddQuery(Query* query)
608 {
609 	MutexLocker _(fQueryLock);
610 	fQueries.Add(query);
611 }
612 
613 
614 void
615 Volume::RemoveQuery(Query* query)
616 {
617 	MutexLocker _(fQueryLock);
618 	fQueries.Remove(query);
619 }
620 
621 
622 //	#pragma mark - Disk scanning and initialization
623 
624 
625 /*static*/ status_t
626 Volume::CheckSuperBlock(const uint8* data, uint32* _offset)
627 {
628 	disk_super_block* superBlock = (disk_super_block*)(data + 512);
629 	if (superBlock->IsValid()) {
630 		if (_offset != NULL)
631 			*_offset = 512;
632 		return B_OK;
633 	}
634 
635 #ifndef BFS_LITTLE_ENDIAN_ONLY
636 	// For PPC, the superblock might be located at offset 0
637 	superBlock = (disk_super_block*)data;
638 	if (superBlock->IsValid()) {
639 		if (_offset != NULL)
640 			*_offset = 0;
641 		return B_OK;
642 	}
643 #endif
644 
645 	return B_BAD_VALUE;
646 }
647 
648 
649 /*static*/ status_t
650 Volume::Identify(int fd, disk_super_block* superBlock)
651 {
652 	uint8 buffer[1024];
653 	if (read_pos(fd, 0, buffer, sizeof(buffer)) != sizeof(buffer))
654 		return B_IO_ERROR;
655 
656 	uint32 offset;
657 	if (CheckSuperBlock(buffer, &offset) != B_OK)
658 		return B_BAD_VALUE;
659 
660 	memcpy(superBlock, buffer + offset, sizeof(disk_super_block));
661 	return B_OK;
662 }
663 
664 
665 status_t
666 Volume::Initialize(int fd, const char* name, uint32 blockSize,
667 	uint32 flags)
668 {
669 	// although there is no really good reason for it, we won't
670 	// accept '/' in disk names (mkbfs does this, too - and since
671 	// Tracker names mounted volumes like their name)
672 	if (strchr(name, '/') != NULL)
673 		return B_BAD_VALUE;
674 
675 	if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096
676 		&& blockSize != 8192)
677 		return B_BAD_VALUE;
678 
679 	DeviceOpener opener(fd, O_RDWR);
680 	if (opener.Device() < B_OK)
681 		return B_BAD_VALUE;
682 
683 	if (opener.IsReadOnly())
684 		return B_READ_ONLY_DEVICE;
685 
686 	fDevice = opener.Device();
687 
688 	uint32 deviceBlockSize;
689 	off_t deviceSize;
690 	if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK)
691 		return B_ERROR;
692 
693 	off_t numBlocks = deviceSize / blockSize;
694 
695 	// create valid superblock
696 
697 	fSuperBlock.Initialize(name, numBlocks, blockSize);
698 
699 	// initialize short hands to the superblock (to save byte swapping)
700 	fBlockSize = fSuperBlock.BlockSize();
701 	fBlockShift = fSuperBlock.BlockShift();
702 	fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
703 
704 	// determine log size depending on the size of the volume
705 	off_t logSize = 2048;
706 	if (numBlocks <= 20480)
707 		logSize = 512;
708 	if (deviceSize > 1LL * 1024 * 1024 * 1024)
709 		logSize = 4096;
710 
711 	// since the allocator has not been initialized yet, we
712 	// cannot use BlockAllocator::BitmapSize() here
713 	off_t bitmapBlocks = (numBlocks + blockSize * 8 - 1) / (blockSize * 8);
714 
715 	fSuperBlock.log_blocks = ToBlockRun(bitmapBlocks + 1);
716 	fSuperBlock.log_blocks.length = HOST_ENDIAN_TO_BFS_INT16(logSize);
717 	fSuperBlock.log_start = fSuperBlock.log_end = HOST_ENDIAN_TO_BFS_INT64(
718 		ToBlock(Log()));
719 
720 	// set the current log pointers, so that journaling will work correctly
721 	fLogStart = fSuperBlock.LogStart();
722 	fLogEnd = fSuperBlock.LogEnd();
723 
724 	if (!IsValidSuperBlock())
725 		RETURN_ERROR(B_ERROR);
726 
727 	if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL)
728 		return B_ERROR;
729 
730 	fJournal = new(std::nothrow) Journal(this);
731 	if (fJournal == NULL || fJournal->InitCheck() < B_OK)
732 		RETURN_ERROR(B_ERROR);
733 
734 	// ready to write data to disk
735 
736 	Transaction transaction(this, 0);
737 
738 	if (fBlockAllocator.InitializeAndClearBitmap(transaction) < B_OK)
739 		RETURN_ERROR(B_ERROR);
740 
741 	off_t id;
742 	status_t status = Inode::Create(transaction, NULL, NULL,
743 		S_DIRECTORY | 0755, 0, 0, NULL, &id, &fRootNode);
744 	if (status < B_OK)
745 		RETURN_ERROR(status);
746 
747 	fSuperBlock.root_dir = ToBlockRun(id);
748 
749 	if ((flags & VOLUME_NO_INDICES) == 0) {
750 		// The indices root directory will be created automatically
751 		// when the standard indices are created (or any other).
752 		Index index(this);
753 		status = index.Create(transaction, "name", B_STRING_TYPE);
754 		if (status < B_OK)
755 			return status;
756 
757 		status = index.Create(transaction, "BEOS:APP_SIG", B_STRING_TYPE);
758 		if (status < B_OK)
759 			return status;
760 
761 		status = index.Create(transaction, "last_modified", B_INT64_TYPE);
762 		if (status < B_OK)
763 			return status;
764 
765 		status = index.Create(transaction, "size", B_INT64_TYPE);
766 		if (status < B_OK)
767 			return status;
768 	}
769 
770 	CreateVolumeID(transaction);
771 
772 	WriteSuperBlock();
773 	transaction.Done();
774 
775 	Sync();
776 	opener.RemoveCache(true);
777 	return B_OK;
778 }
779