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 <disk_device_manager/ddm_userland_interface.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 * Unlocks the device for further use. 277 */ 278 status_t 279 BDiskDevice::CommitModifications(bool synchronously, 280 BMessenger progressMessenger, bool receiveCompleteProgressUpdates) 281 { 282 // TODO: Support parameters! 283 status_t error = InitCheck(); 284 if (error != B_OK) 285 return error; 286 287 if (!fDelegate) 288 return B_BAD_VALUE; 289 290 // generate jobs 291 DiskDeviceJobQueue jobQueue; 292 error = DiskDeviceJobGenerator(this, &jobQueue).GenerateJobs(); 293 294 // do the jobs 295 if (error == B_OK) 296 error = jobQueue.Execute(); 297 298 _DeleteDelegates(); 299 DiskSystemAddOnManager::Default()->UnloadDiskSystems(); 300 301 if (error == B_OK) 302 error = _SetTo(ID(), true, 0); 303 304 return error; 305 } 306 307 308 // CancelModifications 309 /*! \brief Cancels all modifications performed on the device. 310 * 311 * Nothing is written on the device and it is unlocked for further use. 312 */ 313 status_t 314 BDiskDevice::CancelModifications() 315 { 316 status_t error = InitCheck(); 317 if (error != B_OK) 318 return error; 319 320 if (!fDelegate) 321 return B_BAD_VALUE; 322 323 _DeleteDelegates(); 324 DiskSystemAddOnManager::Default()->UnloadDiskSystems(); 325 326 if (error == B_OK) 327 error = _SetTo(ID(), true, 0); 328 329 return error; 330 } 331 332 333 // copy constructor 334 /*! \brief Privatized copy constructor to avoid usage. 335 */ 336 BDiskDevice::BDiskDevice(const BDiskDevice&) 337 { 338 } 339 340 341 // = 342 /*! \brief Privatized assignment operator to avoid usage. 343 */ 344 BDiskDevice& 345 BDiskDevice::operator=(const BDiskDevice&) 346 { 347 return *this; 348 } 349 350 351 // _GetData 352 status_t 353 BDiskDevice::_GetData(partition_id id, bool deviceOnly, size_t neededSize, 354 user_disk_device_data** data) 355 { 356 // get the device data 357 void* buffer = NULL; 358 size_t bufferSize = 0; 359 if (neededSize > 0) { 360 // allocate initial buffer 361 buffer = malloc(neededSize); 362 if (!buffer) 363 return B_NO_MEMORY; 364 bufferSize = neededSize; 365 } 366 367 status_t error = B_OK; 368 do { 369 error = _kern_get_disk_device_data(id, deviceOnly, 370 (user_disk_device_data*)buffer, bufferSize, &neededSize); 371 if (error == B_BUFFER_OVERFLOW) { 372 // buffer to small re-allocate it 373 if (buffer) 374 free(buffer); 375 376 buffer = malloc(neededSize); 377 378 if (buffer) 379 bufferSize = neededSize; 380 else 381 error = B_NO_MEMORY; 382 } 383 } while (error == B_BUFFER_OVERFLOW); 384 385 // set result / cleanup on error 386 if (error == B_OK) 387 *data = (user_disk_device_data*)buffer; 388 else if (buffer) 389 free(buffer); 390 391 return error; 392 } 393 394 395 // _SetTo 396 status_t 397 BDiskDevice::_SetTo(partition_id id, bool deviceOnly, size_t neededSize) 398 { 399 Unset(); 400 401 // get the device data 402 user_disk_device_data* data = NULL; 403 status_t error = _GetData(id, deviceOnly, neededSize, &data); 404 405 // set the data 406 if (error == B_OK) 407 error = _SetTo(data); 408 409 // cleanup on error 410 if (error != B_OK && data) 411 free(data); 412 413 return error; 414 } 415 416 417 // _SetTo 418 status_t 419 BDiskDevice::_SetTo(user_disk_device_data* data) 420 { 421 Unset(); 422 423 if (!data) 424 return B_BAD_VALUE; 425 426 fDeviceData = data; 427 428 status_t error = BPartition::_SetTo(this, NULL, 429 &fDeviceData->device_partition_data); 430 if (error != B_OK) { 431 // If _SetTo() fails, the caller retains ownership of the supplied 432 // data. So, unset fDeviceData before calling Unset(). 433 fDeviceData = NULL; 434 Unset(); 435 } 436 437 return error; 438 } 439 440 441 // _Update 442 status_t 443 BDiskDevice::_Update(user_disk_device_data* data, bool* updated) 444 { 445 if (!data || !fDeviceData || ID() != data->device_partition_data.id) 446 return B_BAD_VALUE; 447 bool _updated; 448 if (!updated) 449 updated = &_updated; 450 *updated = false; 451 452 // clear the user_data fields first 453 _ClearUserData(&data->device_partition_data); 454 455 // remove obsolete partitions 456 status_t error = _RemoveObsoleteDescendants(&data->device_partition_data, 457 updated); 458 if (error != B_OK) 459 return error; 460 461 // update existing partitions and add new ones 462 error = BPartition::_Update(&data->device_partition_data, updated); 463 if (error == B_OK) { 464 user_disk_device_data* oldData = fDeviceData; 465 fDeviceData = data; 466 // check for changes 467 if (data->device_flags != oldData->device_flags 468 || strcmp(data->path, oldData->path)) { 469 *updated = true; 470 } 471 free(oldData); 472 } 473 474 return error; 475 } 476 477 478 // _AcceptVisitor 479 bool 480 BDiskDevice::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level) 481 { 482 return visitor->Visit(this); 483 } 484 485 486 // _ClearUserData 487 void 488 BDiskDevice::_ClearUserData(user_partition_data* data) 489 { 490 data->user_data = NULL; 491 492 // recurse 493 for (int i = 0; i < data->child_count; i++) 494 _ClearUserData(data->children[i]); 495 } 496