1/*
2 * Copyright 2003-2013, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <fcntl.h>
8#include <unistd.h>
9#include <string.h>
10#include <stdio.h>
11
12#include <boot/partitions.h>
13#include <boot/platform.h>
14#include <boot/vfs.h>
15
16#include "Volume.h"
17#include "Directory.h"
18#include "Utility.h"
19
20
21#define TRACE_BFS 0
22#if TRACE_BFS
23#	define TRACE(x) dprintf x
24#else
25#	define TRACE(x) ;
26#endif
27
28
29using namespace BFS;
30using std::nothrow;
31
32
33Volume::Volume(boot::Partition *partition)
34	:
35	fRootNode(NULL)
36{
37	fDevice = open_node(partition, O_RDONLY);
38	if (fDevice < B_OK)
39		return;
40
41	if (read_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block)) < B_OK)
42		return;
43
44	if (!IsValidSuperBlock()) {
45#ifndef BFS_LITTLE_ENDIAN_ONLY
46		// try block 0 again (can only happen on the big endian BFS)
47		if (read_pos(fDevice, 0, &fSuperBlock, sizeof(disk_super_block)) < B_OK)
48			return;
49
50		if (!IsValidSuperBlock())
51			return;
52#else
53		return;
54#endif
55	}
56
57	TRACE(("bfs: we do have a valid superblock (name = %s)!\n", fSuperBlock.name));
58
59	fRootNode = new(nothrow) BFS::Directory(*this, Root());
60	if (fRootNode == NULL)
61		return;
62
63	if (fRootNode->InitCheck() < B_OK) {
64		TRACE(("bfs: init check for root node failed\n"));
65		delete fRootNode;
66		fRootNode = NULL;
67		return;
68	}
69}
70
71
72Volume::~Volume()
73{
74	close(fDevice);
75}
76
77
78status_t
79Volume::InitCheck()
80{
81	if (fDevice < B_OK)
82		return B_ERROR;
83
84	return fRootNode != NULL ? B_OK : B_ERROR;
85}
86
87
88bool
89Volume::IsValidSuperBlock()
90{
91	if (fSuperBlock.Magic1() != (int32)SUPER_BLOCK_MAGIC1
92		|| fSuperBlock.Magic2() != (int32)SUPER_BLOCK_MAGIC2
93		|| fSuperBlock.Magic3() != (int32)SUPER_BLOCK_MAGIC3
94		|| (int32)fSuperBlock.block_size != fSuperBlock.inode_size
95		|| fSuperBlock.ByteOrder() != SUPER_BLOCK_FS_LENDIAN
96		|| (1UL << fSuperBlock.BlockShift()) != fSuperBlock.BlockSize()
97		|| fSuperBlock.AllocationGroups() < 1
98		|| fSuperBlock.AllocationGroupShift() < 1
99		|| fSuperBlock.BlocksPerAllocationGroup() < 1
100		|| fSuperBlock.NumBlocks() < 10
101		|| fSuperBlock.AllocationGroups() != divide_roundup(fSuperBlock.NumBlocks(), 1L << fSuperBlock.AllocationGroupShift()))
102		return false;
103
104	return true;
105}
106
107
108status_t
109Volume::ValidateBlockRun(block_run run)
110{
111	if (run.AllocationGroup() < 0 || run.AllocationGroup() > (int32)AllocationGroups()
112		|| run.Start() > (1UL << AllocationGroupShift())
113		|| run.length == 0
114		|| uint32(run.Length() + run.Start()) > (1UL << AllocationGroupShift())) {
115		dprintf("bfs: invalid run(%" B_PRId32 ",%d,%d)\n",
116			run.AllocationGroup(), run.Start(), run.Length());
117		return B_BAD_DATA;
118	}
119	return B_OK;
120}
121
122
123block_run
124Volume::ToBlockRun(off_t block) const
125{
126	block_run run;
127	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(block >> fSuperBlock.AllocationGroupShift());
128	run.start = HOST_ENDIAN_TO_BFS_INT16(block & ~((1LL << fSuperBlock.AllocationGroupShift()) - 1));
129	run.length = HOST_ENDIAN_TO_BFS_INT16(1);
130	return run;
131}
132
133
134//	#pragma mark -
135
136
137float
138bfs_identify_file_system(boot::Partition *partition)
139{
140	Volume volume(partition);
141
142	return volume.InitCheck() < B_OK ? 0 : 0.8;
143}
144
145
146static status_t
147bfs_get_file_system(boot::Partition *partition, ::Directory **_root)
148{
149	Volume *volume = new(nothrow) Volume(partition);
150	if (volume == NULL)
151		return B_NO_MEMORY;
152
153	if (volume->InitCheck() < B_OK) {
154		delete volume;
155		return B_ERROR;
156	}
157
158	*_root = volume->RootNode();
159	return B_OK;
160}
161
162
163file_system_module_info gBFSFileSystemModule = {
164	"file_systems/bfs/v1",
165	kPartitionTypeBFS,
166	bfs_identify_file_system,
167	bfs_get_file_system
168};
169
170