2 * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include <DiskDevice.h>
17 #include <DiskDevice.h>
18 #include <DiskDeviceVisitor.h>
24 #include <ddm_userland_interface_defs.h>
26 #include "DiskDeviceJob.h"
27 #include "DiskDeviceJobGenerator.h"
28 #include "DiskDeviceJobQueue.h"
29 #include <DiskSystemAddOnManager.h>
32 //#define TRACE_DISK_DEVICE
34 #ifdef TRACE_DISK_DEVICE
35 # define TRACE(x...) printf(x)
37 # define TRACE(x...) do {} while (false)
41 /*! \class BDiskDevice
42 \brief A BDiskDevice object represents a storage device.
47 /*! \brief Creates an uninitialized BDiskDevice object.
49 BDiskDevice::BDiskDevice()
57 /*! \brief Frees all resources associated with this object.
59 BDiskDevice::~BDiskDevice()
61 CancelModifications();
66 /*! \brief Returns whether the device contains a media.
67 \return \c true, if the device contains a media, \c false otherwise.
70 BDiskDevice::HasMedia() const
73 && (fDeviceData
->device_flags
& B_DISK_DEVICE_HAS_MEDIA
) != 0;
78 /*! \brief Returns whether the device media are removable.
79 \return \c true, if the device media are removable, \c false otherwise.
82 BDiskDevice::IsRemovableMedia() const
85 && (fDeviceData
->device_flags
& B_DISK_DEVICE_REMOVABLE
) != 0;
91 BDiskDevice::IsReadOnlyMedia() const
94 && (fDeviceData
->device_flags
& B_DISK_DEVICE_READ_ONLY
) != 0;
100 BDiskDevice::IsWriteOnceMedia() const
103 && (fDeviceData
->device_flags
& B_DISK_DEVICE_WRITE_ONCE
) != 0;
108 /*! \brief Eject the device's media.
110 The device media must, of course, be removable, and the device must
111 support ejecting the media.
113 \param update If \c true, Update() is invoked after successful ejection.
115 - \c B_OK: Everything went fine.
116 - \c B_NO_INIT: The device is not properly initialized.
117 - \c B_BAD_VALUE: The device media is not removable.
121 BDiskDevice::Eject(bool update
)
123 if (fDeviceData
== NULL
)
126 // check whether the device media is removable
127 if (!IsRemovableMedia())
130 // open, eject and close the device
131 int fd
= open(fDeviceData
->path
, O_RDONLY
);
135 status_t status
= B_OK
;
136 if (ioctl(fd
, B_EJECT_DEVICE
) != 0)
141 if (status
== B_OK
&& update
)
150 BDiskDevice::SetTo(partition_id id
)
152 return _SetTo(id
, true, 0);
157 /*! \brief Updates the object to reflect the latest changes to the device.
159 Note, that subobjects (BSessions, BPartitions) may be deleted during this
160 operation. It is also possible, that the device doesn't exist anymore --
161 e.g. if it is hot-pluggable. Then an error is returned and the object is
164 \param updated Pointer to a bool variable which shall be set to \c true,
165 if the object needed to be updated and to \c false otherwise.
166 May be \c NULL. Is not touched, if the method fails.
167 \return \c B_OK, if the update went fine, another error code otherwise.
170 BDiskDevice::Update(bool* updated
)
172 if (InitCheck() != B_OK
)
175 // get the device data
176 user_disk_device_data
* data
= NULL
;
177 status_t error
= _GetData(ID(), true, 0, &data
);
181 error
= _Update(data
, updated
);
184 if (error
!= B_OK
&& data
)
195 BPartition::_Unset();
203 BDiskDevice::InitCheck() const
205 return fDeviceData
? B_OK
: B_NO_INIT
;
211 BDiskDevice::GetPath(BPath
* path
) const
213 if (!path
|| !fDeviceData
)
215 return path
->SetTo(fDeviceData
->path
);
221 BDiskDevice::IsModified() const
223 if (InitCheck() != B_OK
)
226 struct IsModifiedVisitor
: public BDiskDeviceVisitor
{
227 virtual bool Visit(BDiskDevice
* device
)
229 return Visit(device
, 0);
232 virtual bool Visit(BPartition
* partition
, int32 level
)
234 return partition
->_IsModified();
238 return (VisitEachDescendant(&visitor
) != NULL
);
242 // PrepareModifications
243 /*! \brief Initializes the partition hierarchy for modifications.
245 * Subsequent modifications are performed on so-called \a shadow structure
246 * and not written to device until \ref CommitModifications is called.
248 * \note This call locks the device. You need to call \ref CommitModifications
249 * or \ref CancelModifications to unlock it.
252 BDiskDevice::PrepareModifications()
254 TRACE("%p->BDiskDevice::PrepareModifications()\n", this);
256 // check initialization
257 status_t error
= InitCheck();
259 TRACE(" InitCheck() failed\n");
263 TRACE(" already prepared!\n");
267 // make sure the disk system add-ons are loaded
268 error
= DiskSystemAddOnManager::Default()->LoadDiskSystems();
270 TRACE(" failed to load disk systems\n");
274 // recursively create the delegates
275 error
= _CreateDelegates();
279 error
= _InitDelegates();
281 TRACE(" failed to create delegates\n");
283 // delete all of them, if something went wrong
285 TRACE(" failed to init delegates\n");
287 DiskSystemAddOnManager::Default()->UnloadDiskSystems();
294 // CommitModifications
295 /*! \brief Commits modifications to device.
297 * Creates a set of jobs that perform all the changes done after
298 * \ref PrepareModifications. The changes are then written to device.
299 * Deletes and recreates all BPartition children to apply the changes,
300 * so cached pointers to BPartition objects cannot be used after this
302 * Unlocks the device for further use.
305 BDiskDevice::CommitModifications(bool synchronously
,
306 BMessenger progressMessenger
, bool receiveCompleteProgressUpdates
)
308 // TODO: Support parameters!
309 status_t error
= InitCheck();
317 DiskDeviceJobQueue jobQueue
;
318 error
= DiskDeviceJobGenerator(this, &jobQueue
).GenerateJobs();
322 error
= jobQueue
.Execute();
325 DiskSystemAddOnManager::Default()->UnloadDiskSystems();
328 error
= _SetTo(ID(), true, 0);
334 // CancelModifications
335 /*! \brief Cancels all modifications performed on the device.
337 * Nothing is written on the device and it is unlocked for further use.
340 BDiskDevice::CancelModifications()
342 status_t error
= InitCheck();
350 DiskSystemAddOnManager::Default()->UnloadDiskSystems();
353 error
= _SetTo(ID(), true, 0);
359 /*! \brief Returns whether or not this device is a virtual device backed
363 BDiskDevice::IsFile() const
366 && (fDeviceData
->device_flags
& B_DISK_DEVICE_IS_FILE
) != 0;
370 /*! \brief Retrieves the path of the file backing up the disk device.*/
372 BDiskDevice::GetFilePath(BPath
* path
) const
379 char pathBuffer
[B_PATH_NAME_LENGTH
];
380 status_t status
= _kern_get_file_disk_device_path(
381 fDeviceData
->device_partition_data
.id
, pathBuffer
, sizeof(pathBuffer
));
385 return path
->SetTo(pathBuffer
);
390 /*! \brief Privatized copy constructor to avoid usage.
392 BDiskDevice::BDiskDevice(const BDiskDevice
&)
398 /*! \brief Privatized assignment operator to avoid usage.
401 BDiskDevice::operator=(const BDiskDevice
&)
409 BDiskDevice::_GetData(partition_id id
, bool deviceOnly
, size_t neededSize
,
410 user_disk_device_data
** data
)
412 // get the device data
414 size_t bufferSize
= 0;
415 if (neededSize
> 0) {
416 // allocate initial buffer
417 buffer
= malloc(neededSize
);
420 bufferSize
= neededSize
;
423 status_t error
= B_OK
;
425 error
= _kern_get_disk_device_data(id
, deviceOnly
,
426 (user_disk_device_data
*)buffer
, bufferSize
, &neededSize
);
427 if (error
== B_BUFFER_OVERFLOW
) {
428 // buffer to small re-allocate it
431 buffer
= malloc(neededSize
);
434 bufferSize
= neededSize
;
438 } while (error
== B_BUFFER_OVERFLOW
);
440 // set result / cleanup on error
442 *data
= (user_disk_device_data
*)buffer
;
452 BDiskDevice::_SetTo(partition_id id
, bool deviceOnly
, size_t neededSize
)
456 // get the device data
457 user_disk_device_data
* data
= NULL
;
458 status_t error
= _GetData(id
, deviceOnly
, neededSize
, &data
);
462 error
= _SetTo(data
);
465 if (error
!= B_OK
&& data
)
474 BDiskDevice::_SetTo(user_disk_device_data
* data
)
483 status_t error
= BPartition::_SetTo(this, NULL
,
484 &fDeviceData
->device_partition_data
);
486 // If _SetTo() fails, the caller retains ownership of the supplied
487 // data. So, unset fDeviceData before calling Unset().
498 BDiskDevice::_Update(user_disk_device_data
* data
, bool* updated
)
500 if (!data
|| !fDeviceData
|| ID() != data
->device_partition_data
.id
)
507 // clear the user_data fields first
508 _ClearUserData(&data
->device_partition_data
);
510 // remove obsolete partitions
511 status_t error
= _RemoveObsoleteDescendants(&data
->device_partition_data
,
516 // update existing partitions and add new ones
517 error
= BPartition::_Update(&data
->device_partition_data
, updated
);
519 user_disk_device_data
* oldData
= fDeviceData
;
522 if (data
->device_flags
!= oldData
->device_flags
523 || strcmp(data
->path
, oldData
->path
)) {
535 BDiskDevice::_AcceptVisitor(BDiskDeviceVisitor
* visitor
, int32 level
)
537 return visitor
->Visit(this);
543 BDiskDevice::_ClearUserData(user_partition_data
* data
)
545 data
->user_data
= NULL
;
548 for (int i
= 0; i
< data
->child_count
; i
++)
549 _ClearUserData(data
->children
[i
]);