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
34 using std::nothrow;
35
36
37 static 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
58 static BString
size_string(double size)59 size_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
BFSAddOn()73 BFSAddOn::BFSAddOn()
74 : BDiskSystemAddOn(kPartitionTypeBFS, kDiskSystemFlags)
75 {
76 }
77
78
~BFSAddOn()79 BFSAddOn::~BFSAddOn()
80 {
81 }
82
83
84 status_t
CreatePartitionHandle(BMutablePartition * partition,BPartitionHandle ** _handle)85 BFSAddOn::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
103 bool
CanInitialize(const BMutablePartition * partition)104 BFSAddOn::CanInitialize(const BMutablePartition* partition)
105 {
106 return partition->Size() >= 1L * 1024 * 1024;
107 }
108
109
110 status_t
ValidateInitialize(const BMutablePartition * partition,BString * name,const char * parameterString)111 BFSAddOn::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
149 status_t
Initialize(BMutablePartition * partition,const char * name,const char * parameterString,BPartitionHandle ** _handle)150 BFSAddOn::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
186 status_t
GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,BPartitionParameterEditor ** editor)187 BFSAddOn::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
BFSPartitionHandle(BMutablePartition * partition)206 BFSPartitionHandle::BFSPartitionHandle(BMutablePartition* partition)
207 : BPartitionHandle(partition)
208 {
209 }
210
211
~BFSPartitionHandle()212 BFSPartitionHandle::~BFSPartitionHandle()
213 {
214 }
215
216
217 status_t
Init()218 BFSPartitionHandle::Init()
219 {
220 // TODO: Check parameters.
221 return B_OK;
222 }
223
224
225 uint32
SupportedOperations(uint32 mask)226 BFSPartitionHandle::SupportedOperations(uint32 mask)
227 {
228 return kDiskSystemFlags & mask;
229 }
230
231
232 status_t
Repair(bool checkOnly)233 BFSPartitionHandle::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
350 status_t
get_disk_system_add_ons(BList * addOns)351 get_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