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 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 73 BFSAddOn::BFSAddOn() 74 : BDiskSystemAddOn(kPartitionTypeBFS, kDiskSystemFlags) 75 { 76 } 77 78 79 BFSAddOn::~BFSAddOn() 80 { 81 } 82 83 84 status_t 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 104 BFSAddOn::CanInitialize(const BMutablePartition* partition) 105 { 106 return partition->Size() >= 1L * 1024 * 1024; 107 } 108 109 110 status_t 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 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 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 206 BFSPartitionHandle::BFSPartitionHandle(BMutablePartition* partition) 207 : BPartitionHandle(partition) 208 { 209 } 210 211 212 BFSPartitionHandle::~BFSPartitionHandle() 213 { 214 } 215 216 217 status_t 218 BFSPartitionHandle::Init() 219 { 220 // TODO: Check parameters. 221 return B_OK; 222 } 223 224 225 uint32 226 BFSPartitionHandle::SupportedOperations(uint32 mask) 227 { 228 return kDiskSystemFlags & mask; 229 } 230 231 232 status_t 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 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