1/*
2 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008-2012, Axel D��rfler, axeld@pinc-software.de.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include "BFSAddOn.h"
10#include "InitializeParameterEditor.h"
11
12#include <new>
13
14#include <Directory.h>
15#include <List.h>
16#include <Path.h>
17#include <Volume.h>
18
19#include <DiskDeviceTypes.h>
20#include <MutablePartition.h>
21
22#include <AutoDeleter.h>
23#include <StringForSize.h>
24
25#ifdef ASSERT
26#	undef ASSERT
27#endif
28
29#include "bfs.h"
30#include "bfs_control.h"
31#include "bfs_disk_system.h"
32
33
34using std::nothrow;
35
36
37static const uint32 kDiskSystemFlags =
38	0
39//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
40//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
41//	| B_DISK_SYSTEM_SUPPORTS_RESIZING
42//	| B_DISK_SYSTEM_SUPPORTS_MOVING
43//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
44//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
45	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
46	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
47//	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING
48//	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED
49	| B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
50	| B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
51//	| B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED
52//	| B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED
53//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED
54//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED
55;
56
57
58static BString
59size_string(double size)
60{
61	BString string;
62	char* buffer = string.LockBuffer(256);
63	string_for_size(size, buffer, 256);
64
65	string.UnlockBuffer();
66	return string;
67}
68
69
70// #pragma mark - BFSAddOn
71
72
73BFSAddOn::BFSAddOn()
74	: BDiskSystemAddOn(kPartitionTypeBFS, kDiskSystemFlags)
75{
76}
77
78
79BFSAddOn::~BFSAddOn()
80{
81}
82
83
84status_t
85BFSAddOn::CreatePartitionHandle(BMutablePartition* partition,
86	BPartitionHandle** _handle)
87{
88	BFSPartitionHandle* handle = new(nothrow) BFSPartitionHandle(partition);
89	if (!handle)
90		return B_NO_MEMORY;
91
92	status_t error = handle->Init();
93	if (error != B_OK) {
94		delete handle;
95		return error;
96	}
97
98	*_handle = handle;
99	return B_OK;
100}
101
102
103bool
104BFSAddOn::CanInitialize(const BMutablePartition* partition)
105{
106	return partition->Size() >= 1L * 1024 * 1024;
107}
108
109
110status_t
111BFSAddOn::ValidateInitialize(const BMutablePartition* partition, BString* name,
112	const char* parameterString)
113{
114	if (!CanInitialize(partition) || !name)
115		return B_BAD_VALUE;
116
117	// validate name
118
119	// truncate, if it is too long
120	if (name->Length() >= BFS_DISK_NAME_LENGTH)
121		name->Truncate(BFS_DISK_NAME_LENGTH - 1);
122
123	// replace '/' by '-'
124	name->ReplaceAll('/', '-');
125
126	// check parameters
127	initialize_parameters parameters;
128	status_t error = parse_initialize_parameters(parameterString, parameters);
129	if (error != B_OK)
130		return error;
131
132	off_t size = partition->Size();
133	uint32 blockSize = parameters.blockSize;
134	if (size < 2 * 1024 * 1024) {
135		if (blockSize != 1024)
136			return B_BAD_VALUE;
137	} else if (size < 4 * 1024 * 1024) {
138		if (blockSize >= 4 * 1024)
139			return B_BAD_VALUE;
140	} else if (size < 8 * 1024 * 1024) {
141		if (blockSize >= 8 * 1024)
142			return B_BAD_VALUE;
143	}
144
145	return B_OK;
146}
147
148
149status_t
150BFSAddOn::Initialize(BMutablePartition* partition, const char* name,
151	const char* parameterString, BPartitionHandle** _handle)
152{
153	if (!CanInitialize(partition) || check_volume_name(name) != B_OK)
154		return B_BAD_VALUE;
155
156	initialize_parameters parameters;
157	status_t error = parse_initialize_parameters(parameterString, parameters);
158	if (error != B_OK)
159		return error;
160
161	// create the handle
162	BFSPartitionHandle* handle = new(nothrow) BFSPartitionHandle(partition);
163	if (!handle)
164		return B_NO_MEMORY;
165	ObjectDeleter<BFSPartitionHandle> handleDeleter(handle);
166
167	// init the partition
168	error = partition->SetContentType(Name());
169	if (error != B_OK)
170		return error;
171// TODO: The content type could as well be set by the caller.
172
173	partition->SetContentName(name);
174	partition->SetContentParameters(parameterString);
175	uint32 blockSize = parameters.blockSize;
176	partition->SetBlockSize(blockSize);
177	partition->SetContentSize(partition->Size() / blockSize * blockSize);
178	partition->Changed(B_PARTITION_CHANGED_INITIALIZATION);
179
180	*_handle = handleDeleter.Detach();
181
182	return B_OK;
183}
184
185
186status_t
187BFSAddOn::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
188	BPartitionParameterEditor** editor)
189{
190	*editor = NULL;
191	if (type == B_INITIALIZE_PARAMETER_EDITOR) {
192		try {
193			*editor = new InitializeBFSEditor();
194		} catch (std::bad_alloc&) {
195			return B_NO_MEMORY;
196		}
197		return B_OK;
198	}
199	return B_NOT_SUPPORTED;
200}
201
202
203// #pragma mark - BFSPartitionHandle
204
205
206BFSPartitionHandle::BFSPartitionHandle(BMutablePartition* partition)
207	: BPartitionHandle(partition)
208{
209}
210
211
212BFSPartitionHandle::~BFSPartitionHandle()
213{
214}
215
216
217status_t
218BFSPartitionHandle::Init()
219{
220// TODO: Check parameters.
221	return B_OK;
222}
223
224
225uint32
226BFSPartitionHandle::SupportedOperations(uint32 mask)
227{
228	return kDiskSystemFlags & mask;
229}
230
231
232status_t
233BFSPartitionHandle::Repair(bool checkOnly)
234{
235	BVolume volume(Partition()->VolumeID());
236	BDirectory directory;
237	volume.GetRootDirectory(&directory);
238	BPath path;
239	path.SetTo(&directory, ".");
240
241	FileDescriptorCloser fd(open(path.Path(), O_RDONLY));
242	if (!fd.IsSet())
243	    return errno;
244
245	struct check_control result;
246	memset(&result, 0, sizeof(result));
247	result.magic = BFS_IOCTL_CHECK_MAGIC;
248	result.flags = 0;
249	if (!checkOnly) {
250		//printf("will fix any severe errors!\n");
251		result.flags |= BFS_FIX_BITMAP_ERRORS | BFS_REMOVE_WRONG_TYPES
252			| BFS_REMOVE_INVALID | BFS_FIX_NAME_MISMATCHES | BFS_FIX_BPLUSTREES;
253	}
254
255	// start checking
256	if (ioctl(fd.Get(), BFS_IOCTL_START_CHECKING, &result, sizeof(result)) < 0)
257	    return errno;
258
259	uint64 attributeDirectories = 0, attributes = 0;
260	uint64 files = 0, directories = 0, indices = 0;
261	uint64 counter = 0;
262	uint32 previousPass = result.pass;
263
264	// check all files and report errors
265	while (ioctl(fd.Get(), BFS_IOCTL_CHECK_NEXT_NODE, &result,
266			sizeof(result)) == 0) {
267		if (++counter % 50 == 0)
268			printf("%9" B_PRIu64 " nodes processed\x1b[1A\n", counter);
269
270		if (result.pass == BFS_CHECK_PASS_BITMAP) {
271			if (result.errors) {
272				printf("%s (inode = %" B_PRIdINO ")", result.name, result.inode);
273				if ((result.errors & BFS_MISSING_BLOCKS) != 0)
274					printf(", some blocks weren't allocated");
275				if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
276					printf(", has blocks already set");
277				if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
278					printf(", has invalid block run(s)");
279				if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
280					printf(", could not be opened");
281				if ((result.errors & BFS_WRONG_TYPE) != 0)
282					printf(", has wrong type");
283				if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
284					printf(", names don't match");
285				if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
286					printf(", invalid b+tree");
287				putchar('\n');
288			}
289
290			if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
291				indices++;
292			else if (result.mode & S_ATTR_DIR)
293				attributeDirectories++;
294			else if (result.mode & S_ATTR)
295				attributes++;
296			else if (S_ISDIR(result.mode))
297				directories++;
298			else
299				files++;
300		} else if (result.pass == BFS_CHECK_PASS_INDEX) {
301			if (previousPass != result.pass) {
302				printf("Recreating broken index b+trees...\n");
303				previousPass = result.pass;
304				counter = 0;
305			}
306		}
307	}
308
309	// stop checking
310	if (ioctl(fd.Get(), BFS_IOCTL_STOP_CHECKING, &result, sizeof(result)) != 0)
311		return errno;
312
313	printf("        %" B_PRIu64 " nodes checked,\n\t%" B_PRIu64 " blocks not "
314		"allocated,\n\t%" B_PRIu64 " blocks already set,\n\t%" B_PRIu64
315		" blocks could be freed\n\n", counter, result.stats.missing,
316		result.stats.already_set, result.stats.freed);
317	printf("\tfiles\t\t%" B_PRIu64 "\n\tdirectories\t%" B_PRIu64 "\n"
318		"\tattributes\t%" B_PRIu64 "\n\tattr. dirs\t%" B_PRIu64 "\n"
319		"\tindices\t\t%" B_PRIu64 "\n", files, directories, attributes,
320		attributeDirectories, indices);
321
322	printf("\n\tdirect block runs\t\t%" B_PRIu64 " (%s)\n",
323		result.stats.direct_block_runs, size_string(1.0
324			* result.stats.blocks_in_direct
325			* result.stats.block_size).String());
326	printf("\tindirect block runs\t\t%" B_PRIu64 " (in %" B_PRIu64
327		" array blocks, %s)\n", result.stats.indirect_block_runs,
328		result.stats.indirect_array_blocks,
329		size_string(1.0 * result.stats.blocks_in_indirect
330			* result.stats.block_size).String());
331	printf("\tdouble indirect block runs\t%" B_PRIu64 " (in %" B_PRIu64
332		" array blocks, %s)\n", result.stats.double_indirect_block_runs,
333		result.stats.double_indirect_array_blocks,
334		size_string(1.0 * result.stats.blocks_in_double_indirect
335			* result.stats.block_size).String());
336	// TODO: this is currently not maintained correctly
337	//printf("\tpartial block runs\t%" B_PRIu64 "\n",
338	//	result.stats.partial_block_runs);
339
340	if (result.status == B_ENTRY_NOT_FOUND)
341		result.status = B_OK;
342
343	return result.status;
344}
345
346
347// #pragma mark -
348
349
350status_t
351get_disk_system_add_ons(BList* addOns)
352{
353	BFSAddOn* addOn = new(nothrow) BFSAddOn;
354	if (!addOn)
355		return B_NO_MEMORY;
356
357	if (!addOns->AddItem(addOn)) {
358		delete addOn;
359		return B_NO_MEMORY;
360	}
361
362	return B_OK;
363}
364