1/*
2 * Copyright 2003-2009, 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#include <util/kernel_cpp.h>
16
17#include "Volume.h"
18#include "Directory.h"
19#include "Utility.h"
20
21
22#define TRACE_BFS 0
23#if TRACE_BFS
24#	define TRACE(x) printf x
25#else
26#	define TRACE(x) ;
27#endif
28
29
30using namespace BFS;
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(%ld,%d,%d)\n", run.AllocationGroup(), run.Start(), run.Length());
116		return B_BAD_DATA;
117	}
118	return B_OK;
119}
120
121
122block_run
123Volume::ToBlockRun(off_t block) const
124{
125	block_run run;
126	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(block >> fSuperBlock.AllocationGroupShift());
127	run.start = HOST_ENDIAN_TO_BFS_INT16(block & ~((1LL << fSuperBlock.AllocationGroupShift()) - 1));
128	run.length = HOST_ENDIAN_TO_BFS_INT16(1);
129	return run;
130}
131
132
133//	#pragma mark -
134
135
136float
137bfs_identify_file_system(boot::Partition *partition)
138{
139	Volume volume(partition);
140
141	return volume.InitCheck() < B_OK ? 0 : 0.8;
142}
143
144
145static status_t
146bfs_get_file_system(boot::Partition *partition, ::Directory **_root)
147{
148	Volume *volume = new(nothrow) Volume(partition);
149	if (volume == NULL)
150		return B_NO_MEMORY;
151
152	if (volume->InitCheck() < B_OK) {
153		delete volume;
154		return B_ERROR;
155	}
156
157	*_root = volume->RootNode();
158	return B_OK;
159}
160
161
162file_system_module_info gBFSFileSystemModule = {
163	"file_systems/bfs/v1",
164	kPartitionTypeBFS,
165	bfs_identify_file_system,
166	bfs_get_file_system
167};
168
169