1 /* 2 * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <DiskDevice.h> 7 8 #include <ctype.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <new> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <DiskDevice.h> 18 #include <DiskDeviceVisitor.h> 19 #include <Drivers.h> 20 #include <Message.h> 21 #include <Path.h> 22 23 #include <syscalls.h> 24 #include <ddm_userland_interface_defs.h> 25 26 #include "DiskDeviceJob.h" 27 #include "DiskDeviceJobGenerator.h" 28 #include "DiskDeviceJobQueue.h" 29 #include "DiskSystemAddOnManager.h" 30 31 32 /*! \class BDiskDevice 33 \brief A BDiskDevice object represents a storage device. 34 */ 35 36 37 // constructor 38 /*! \brief Creates an uninitialized BDiskDevice object. 39 */ 40 BDiskDevice::BDiskDevice() 41 : fDeviceData(NULL) 42 { 43 } 44 45 46 // destructor 47 /*! \brief Frees all resources associated with this object. 48 */ 49 BDiskDevice::~BDiskDevice() 50 { 51 CancelModifications(); 52 } 53 54 55 // HasMedia 56 /*! \brief Returns whether the device contains a media. 57 \return \c true, if the device contains a media, \c false otherwise. 58 */ 59 bool 60 BDiskDevice::HasMedia() const 61 { 62 return (fDeviceData 63 && fDeviceData->device_flags & B_DISK_DEVICE_HAS_MEDIA); 64 } 65 66 67 // IsRemovableMedia 68 /*! \brief Returns whether the device media are removable. 69 \return \c true, if the device media are removable, \c false otherwise. 70 */ 71 bool 72 BDiskDevice::IsRemovableMedia() const 73 { 74 return (fDeviceData 75 && fDeviceData->device_flags & B_DISK_DEVICE_REMOVABLE); 76 } 77 78 79 // IsReadOnlyMedia 80 bool 81 BDiskDevice::IsReadOnlyMedia() const 82 { 83 return (fDeviceData 84 && fDeviceData->device_flags & B_DISK_DEVICE_READ_ONLY); 85 } 86 87 88 // IsWriteOnceMedia 89 bool 90 BDiskDevice::IsWriteOnceMedia() const 91 { 92 return (fDeviceData 93 && fDeviceData->device_flags & B_DISK_DEVICE_WRITE_ONCE); 94 } 95 96 97 // Eject 98 /*! \brief Eject the device's media. 99 100 The device media must, of course, be removable, and the device must 101 support ejecting the media. 102 103 \param update If \c true, Update() is invoked after successful ejection. 104 \return 105 - \c B_OK: Everything went fine. 106 - \c B_NO_INIT: The device is not properly initialized. 107 - \c B_BAD_VALUE: The device media is not removable. 108 - other error codes 109 */ 110 status_t 111 BDiskDevice::Eject(bool update) 112 { 113 /* // get path 114 const char* path = Path(); 115 status_t error = (path ? B_OK : B_NO_INIT); 116 // check whether the device media is removable 117 if (error == B_OK && !IsRemovable()) 118 error = B_BAD_VALUE; 119 // open, eject and close the device 120 if (error == B_OK) { 121 int fd = open(path, O_RDONLY); 122 if (fd < 0 || ioctl(fd, B_EJECT_DEVICE) != 0) 123 error = errno; 124 if (fd >= 0) 125 close(fd); 126 } 127 if (error == B_OK && update) 128 error = Update(); 129 return error; 130 */ 131 // not implemented 132 return B_ERROR; 133 } 134 135 136 // SetTo 137 status_t 138 BDiskDevice::SetTo(partition_id id) 139 { 140 return _SetTo(id, true, 0); 141 } 142 143 144 // Update 145 /*! \brief Updates the object to reflect the latest changes to the device. 146 147 Note, that subobjects (BSessions, BPartitions) may be deleted during this 148 operation. It is also possible, that the device doesn't exist anymore -- 149 e.g. if it is hot-pluggable. Then an error is returned and the object is 150 uninitialized. 151 152 \param updated Pointer to a bool variable which shall be set to \c true, 153 if the object needed to be updated and to \c false otherwise. 154 May be \c NULL. Is not touched, if the method fails. 155 \return \c B_OK, if the update went fine, another error code otherwise. 156 */ 157 status_t 158 BDiskDevice::Update(bool* updated) 159 { 160 if (InitCheck() != B_OK) 161 return InitCheck(); 162 163 // get the device data 164 user_disk_device_data* data = NULL; 165 status_t error = _GetData(ID(), true, 0, &data); 166 167 // set the data 168 if (error == B_OK) 169 error = _Update(data, updated); 170 171 // cleanup on error 172 if (error != B_OK && data) 173 free(data); 174 175 return error; 176 } 177 178 179 // Unset 180 void 181 BDiskDevice::Unset() 182 { 183 BPartition::_Unset(); 184 free(fDeviceData); 185 fDeviceData = NULL; 186 } 187 188 189 // InitCheck 190 status_t 191 BDiskDevice::InitCheck() const 192 { 193 return (fDeviceData ? B_OK : B_NO_INIT); 194 } 195 196 197 // GetPath 198 status_t 199 BDiskDevice::GetPath(BPath* path) const 200 { 201 if (!path || !fDeviceData) 202 return B_BAD_VALUE; 203 return path->SetTo(fDeviceData->path); 204 } 205 206 207 // IsModified 208 bool 209 BDiskDevice::IsModified() const 210 { 211 if (InitCheck() != B_OK) 212 return false; 213 214 struct IsModifiedVisitor : public BDiskDeviceVisitor { 215 virtual bool Visit(BDiskDevice *device) 216 { 217 return Visit(device, 0); 218 } 219 220 virtual bool Visit(BPartition *partition, int32 level) 221 { 222 return partition->_IsModified(); 223 } 224 } visitor; 225 226 return (VisitEachDescendant(&visitor) != NULL); 227 } 228 229 230 // PrepareModifications 231 /*! \brief Initializes the partition hierarchy for modifications. 232 * 233 * Subsequent modifications are performed on so-called \a shadow structure 234 * and not written to device until \ref CommitModifications is called. 235 * 236 * \note This call locks the device. You need to call \ref CommitModifications 237 * or \ref CancelModifications to unlock it. 238 */ 239 status_t 240 BDiskDevice::PrepareModifications() 241 { 242 // check initialization 243 status_t error = InitCheck(); 244 if (error != B_OK) 245 return error; 246 if (fDelegate) 247 return B_BAD_VALUE; 248 249 // make sure the disk system add-ons are loaded 250 error = DiskSystemAddOnManager::Default()->LoadDiskSystems(); 251 if (error != B_OK) 252 return error; 253 254 // recursively create the delegates 255 error = _CreateDelegates(); 256 257 // init them 258 if (error == B_OK) 259 error = _InitDelegates(); 260 261 // delete all of them, if something went wrong 262 if (error != B_OK) { 263 _DeleteDelegates(); 264 DiskSystemAddOnManager::Default()->UnloadDiskSystems(); 265 } 266 267 return error; 268 } 269 270 271 // CommitModifications 272 /*! \brief Commits modifications to device. 273 * 274 * Creates a set of jobs that perform all the changes done after 275 * \ref PrepareModifications. The changes are then written to device. 276 * Deletes and recreates all BPartition children to apply the changes, 277 * so cached pointers to BPartition objects cannot be used after this 278 * call. 279 * Unlocks the device for further use. 280 */ 281 status_t 282 BDiskDevice::CommitModifications(bool synchronously, 283 BMessenger progressMessenger, bool receiveCompleteProgressUpdates) 284 { 285 // TODO: Support parameters! 286 status_t error = InitCheck(); 287 if (error != B_OK) 288 return error; 289 290 if (!fDelegate) 291 return B_BAD_VALUE; 292 293 // generate jobs 294 DiskDeviceJobQueue jobQueue; 295 error = DiskDeviceJobGenerator(this, &jobQueue).GenerateJobs(); 296 297 // do the jobs 298 if (error == B_OK) 299 error = jobQueue.Execute(); 300 301 _DeleteDelegates(); 302 DiskSystemAddOnManager::Default()->UnloadDiskSystems(); 303 304 if (error == B_OK) 305 error = _SetTo(ID(), true, 0); 306 307 return error; 308 } 309 310 311 // CancelModifications 312 /*! \brief Cancels all modifications performed on the device. 313 * 314 * Nothing is written on the device and it is unlocked for further use. 315 */ 316 status_t 317 BDiskDevice::CancelModifications() 318 { 319 status_t error = InitCheck(); 320 if (error != B_OK) 321 return error; 322 323 if (!fDelegate) 324 return B_BAD_VALUE; 325 326 _DeleteDelegates(); 327 DiskSystemAddOnManager::Default()->UnloadDiskSystems(); 328 329 if (error == B_OK) 330 error = _SetTo(ID(), true, 0); 331 332 return error; 333 } 334 335 336 // copy constructor 337 /*! \brief Privatized copy constructor to avoid usage. 338 */ 339 BDiskDevice::BDiskDevice(const BDiskDevice&) 340 { 341 } 342 343 344 // = 345 /*! \brief Privatized assignment operator to avoid usage. 346 */ 347 BDiskDevice& 348 BDiskDevice::operator=(const BDiskDevice&) 349 { 350 return *this; 351 } 352 353 354 // _GetData 355 status_t 356 BDiskDevice::_GetData(partition_id id, bool deviceOnly, size_t neededSize, 357 user_disk_device_data** data) 358 { 359 // get the device data 360 void* buffer = NULL; 361 size_t bufferSize = 0; 362 if (neededSize > 0) { 363 // allocate initial buffer 364 buffer = malloc(neededSize); 365 if (!buffer) 366 return B_NO_MEMORY; 367 bufferSize = neededSize; 368 } 369 370 status_t error = B_OK; 371 do { 372 error = _kern_get_disk_device_data(id, deviceOnly, 373 (user_disk_device_data*)buffer, bufferSize, &neededSize); 374 if (error == B_BUFFER_OVERFLOW) { 375 // buffer to small re-allocate it 376 if (buffer) 377 free(buffer); 378 379 buffer = malloc(neededSize); 380 381 if (buffer) 382 bufferSize = neededSize; 383 else 384 error = B_NO_MEMORY; 385 } 386 } while (error == B_BUFFER_OVERFLOW); 387 388 // set result / cleanup on error 389 if (error == B_OK) 390 *data = (user_disk_device_data*)buffer; 391 else if (buffer) 392 free(buffer); 393 394 return error; 395 } 396 397 398 // _SetTo 399 status_t 400 BDiskDevice::_SetTo(partition_id id, bool deviceOnly, size_t neededSize) 401 { 402 Unset(); 403 404 // get the device data 405 user_disk_device_data* data = NULL; 406 status_t error = _GetData(id, deviceOnly, neededSize, &data); 407 408 // set the data 409 if (error == B_OK) 410 error = _SetTo(data); 411 412 // cleanup on error 413 if (error != B_OK && data) 414 free(data); 415 416 return error; 417 } 418 419 420 // _SetTo 421 status_t 422 BDiskDevice::_SetTo(user_disk_device_data* data) 423 { 424 Unset(); 425 426 if (!data) 427 return B_BAD_VALUE; 428 429 fDeviceData = data; 430 431 status_t error = BPartition::_SetTo(this, NULL, 432 &fDeviceData->device_partition_data); 433 if (error != B_OK) { 434 // If _SetTo() fails, the caller retains ownership of the supplied 435 // data. So, unset fDeviceData before calling Unset(). 436 fDeviceData = NULL; 437 Unset(); 438 } 439 440 return error; 441 } 442 443 444 // _Update 445 status_t 446 BDiskDevice::_Update(user_disk_device_data* data, bool* updated) 447 { 448 if (!data || !fDeviceData || ID() != data->device_partition_data.id) 449 return B_BAD_VALUE; 450 bool _updated; 451 if (!updated) 452 updated = &_updated; 453 *updated = false; 454 455 // clear the user_data fields first 456 _ClearUserData(&data->device_partition_data); 457 458 // remove obsolete partitions 459 status_t error = _RemoveObsoleteDescendants(&data->device_partition_data, 460 updated); 461 if (error != B_OK) 462 return error; 463 464 // update existing partitions and add new ones 465 error = BPartition::_Update(&data->device_partition_data, updated); 466 if (error == B_OK) { 467 user_disk_device_data* oldData = fDeviceData; 468 fDeviceData = data; 469 // check for changes 470 if (data->device_flags != oldData->device_flags 471 || strcmp(data->path, oldData->path)) { 472 *updated = true; 473 } 474 free(oldData); 475 } 476 477 return error; 478 } 479 480 481 // _AcceptVisitor 482 bool 483 BDiskDevice::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level) 484 { 485 return visitor->Visit(this); 486 } 487 488 489 // _ClearUserData 490 void 491 BDiskDevice::_ClearUserData(user_partition_data* data) 492 { 493 data->user_data = NULL; 494 495 // recurse 496 for (int i = 0; i < data->child_count; i++) 497 _ClearUserData(data->children[i]); 498 } 499