1 /* 2 * Copyright 2006-2011, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "KDiskDevice.h" 9 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 15 #include <KernelExport.h> 16 #include <Drivers.h> 17 18 #include "ddm_userland_interface.h" 19 #include "KDiskDeviceUtils.h" 20 #include "KPath.h" 21 #include "UserDataWriter.h" 22 23 24 // debugging 25 //#define DBG(x) 26 #define DBG(x) x 27 #define OUT dprintf 28 29 30 KDiskDevice::KDiskDevice(partition_id id) 31 : 32 KPartition(id), 33 fDeviceData(), 34 fFD(-1), 35 fMediaStatus(B_ERROR) 36 { 37 rw_lock_init(&fLocker, "disk device"); 38 39 Unset(); 40 fDevice = this; 41 fPublishedName = (char*)"raw"; 42 } 43 44 45 KDiskDevice::~KDiskDevice() 46 { 47 Unset(); 48 } 49 50 51 status_t 52 KDiskDevice::SetTo(const char* path) 53 { 54 // check initialization and parameter 55 status_t error = InitCheck(); 56 if (error != B_OK) 57 return error; 58 if (!path) 59 return B_BAD_VALUE; 60 Unset(); 61 // set the path 62 error = set_string(fDeviceData.path, path); 63 if (error != B_OK) 64 return error; 65 // open the device 66 fFD = open(path, O_RDONLY); 67 if (fFD < 0) 68 return errno; 69 // get media status 70 error = GetMediaStatus(&fMediaStatus); 71 if (error != B_OK) 72 return error; 73 if (fMediaStatus == B_DEV_MEDIA_CHANGED) 74 fMediaStatus = B_OK; 75 // get device geometry 76 if (fMediaStatus == B_OK) { 77 error = GetGeometry(&fDeviceData.geometry); 78 if (error != B_OK) 79 return error; 80 } else { 81 // no media present: reset the geometry 82 _ResetGeometry(); 83 } 84 85 // set device flags 86 _UpdateDeviceFlags(); 87 // update partition data 88 _InitPartitionData(); 89 return B_OK; 90 } 91 92 93 void 94 KDiskDevice::Unset() 95 { 96 if (fFD >= 0) { 97 close(fFD); 98 fFD = -1; 99 } 100 fMediaStatus = B_ERROR; 101 fDeviceData.id = -1; 102 fDeviceData.flags = 0; 103 if (fDeviceData.path) { 104 free(fDeviceData.path); 105 fDeviceData.path = NULL; 106 } 107 _ResetGeometry(); 108 } 109 110 111 status_t 112 KDiskDevice::InitCheck() const 113 { 114 return B_OK; 115 } 116 117 118 bool 119 KDiskDevice::ReadLock() 120 { 121 return rw_lock_read_lock(&fLocker) == B_OK; 122 } 123 124 125 void 126 KDiskDevice::ReadUnlock() 127 { 128 rw_lock_read_unlock(&fLocker); 129 } 130 131 132 bool 133 KDiskDevice::WriteLock() 134 { 135 return rw_lock_write_lock(&fLocker) == B_OK; 136 } 137 138 139 void 140 KDiskDevice::WriteUnlock() 141 { 142 rw_lock_write_unlock(&fLocker); 143 } 144 145 146 void 147 KDiskDevice::SetID(partition_id id) 148 { 149 KPartition::SetID(id); 150 fDeviceData.id = id; 151 } 152 153 154 status_t 155 KDiskDevice::PublishDevice() 156 { 157 // PublishDevice(), UnpublishDevice() and Republish are no-ops 158 // for KDiskDevices, since they are always published. 159 return B_OK; 160 } 161 162 163 status_t 164 KDiskDevice::UnpublishDevice() 165 { 166 // PublishDevice(), UnpublishDevice() and Republish are no-ops 167 // for KDiskDevices, since they are always published. 168 return B_OK; 169 } 170 171 172 status_t 173 KDiskDevice::RepublishDevice() 174 { 175 // PublishDevice(), UnpublishDevice() and Republish are no-ops 176 // for KDiskDevices, since they are always published. 177 return B_OK; 178 } 179 180 181 void 182 KDiskDevice::SetDeviceFlags(uint32 flags) 183 { 184 fDeviceData.flags = flags; 185 } 186 187 188 uint32 189 KDiskDevice::DeviceFlags() const 190 { 191 return fDeviceData.flags; 192 } 193 194 195 bool 196 KDiskDevice::IsReadOnlyMedia() const 197 { 198 return fDeviceData.geometry.read_only; 199 } 200 201 202 bool 203 KDiskDevice::IsWriteOnce() const 204 { 205 return fDeviceData.geometry.write_once; 206 } 207 208 209 bool 210 KDiskDevice::IsRemovable() const 211 { 212 return fDeviceData.geometry.removable; 213 } 214 215 216 bool 217 KDiskDevice::HasMedia() const 218 { 219 return fMediaStatus == B_OK || fMediaStatus == B_DEV_MEDIA_CHANGED; 220 } 221 222 223 bool 224 KDiskDevice::MediaChanged() const 225 { 226 return fMediaStatus == B_DEV_MEDIA_CHANGED; 227 } 228 229 230 void 231 KDiskDevice::UpdateMediaStatusIfNeeded() 232 { 233 // TODO: allow a device to notify us about its media status! 234 // This will then also need to clear any B_DEV_MEDIA_CHANGED 235 GetMediaStatus(&fMediaStatus); 236 } 237 238 239 void 240 KDiskDevice::UninitializeMedia() 241 { 242 UninitializeContents(); 243 _ResetGeometry(); 244 _UpdateDeviceFlags(); 245 _InitPartitionData(); 246 } 247 248 249 void 250 KDiskDevice::UpdateGeometry() 251 { 252 if (GetGeometry(&fDeviceData.geometry) != B_OK) 253 return; 254 255 _UpdateDeviceFlags(); 256 _InitPartitionData(); 257 } 258 259 260 status_t 261 KDiskDevice::SetPath(const char* path) 262 { 263 return set_string(fDeviceData.path, path); 264 } 265 266 267 const char* 268 KDiskDevice::Path() const 269 { 270 return fDeviceData.path; 271 } 272 273 274 status_t 275 KDiskDevice::GetFileName(char* buffer, size_t size) const 276 { 277 if (strlcpy(buffer, "raw", size) >= size) 278 return B_NAME_TOO_LONG; 279 return B_OK; 280 } 281 282 283 status_t 284 KDiskDevice::GetPath(KPath* path) const 285 { 286 if (!path || path->InitCheck() != B_OK) 287 return B_BAD_VALUE; 288 if (!fDeviceData.path) 289 return B_NO_INIT; 290 return path->SetPath(fDeviceData.path); 291 } 292 293 294 void 295 KDiskDevice::SetFD(int fd) 296 { 297 fFD = fd; 298 } 299 300 301 int 302 KDiskDevice::FD() const 303 { 304 return fFD; 305 } 306 307 308 disk_device_data* 309 KDiskDevice::DeviceData() 310 { 311 return &fDeviceData; 312 } 313 314 315 const disk_device_data* 316 KDiskDevice::DeviceData() const 317 { 318 return &fDeviceData; 319 } 320 321 322 void 323 KDiskDevice::WriteUserData(UserDataWriter& writer, user_partition_data* data) 324 { 325 KPartition::WriteUserData(writer, data); 326 } 327 328 329 void 330 KDiskDevice::WriteUserData(UserDataWriter& writer) 331 { 332 KPartition* partition = this; 333 user_disk_device_data* data 334 = writer.AllocateDeviceData(partition->CountChildren()); 335 char* path = writer.PlaceString(Path()); 336 if (data != NULL) { 337 data->device_flags = DeviceFlags(); 338 data->path = path; 339 writer.AddRelocationEntry(&data->path); 340 partition->WriteUserData(writer, &data->device_partition_data); 341 } else 342 partition->WriteUserData(writer, NULL); 343 } 344 345 346 void 347 KDiskDevice::Dump(bool deep, int32 level) 348 { 349 OUT("device %" B_PRId32 ": %s\n", ID(), Path()); 350 OUT(" media status: %s\n", strerror(fMediaStatus)); 351 OUT(" device flags: %" B_PRIx32 "\n", DeviceFlags()); 352 if (fMediaStatus == B_OK) 353 KPartition::Dump(deep, 0); 354 } 355 356 357 status_t 358 KDiskDevice::GetMediaStatus(status_t* mediaStatus) 359 { 360 status_t error = B_OK; 361 if (ioctl(fFD, B_GET_MEDIA_STATUS, mediaStatus, sizeof(*mediaStatus)) != 0) 362 error = errno; 363 // maybe the device driver doesn't implement this ioctl -- see, if getting 364 // the device geometry succeeds 365 if (error != B_OK) { 366 device_geometry geometry; 367 if (GetGeometry(&geometry) == B_OK) { 368 // if the device is not removable, we can ignore the failed ioctl 369 // and return a media status of B_OK 370 if (!geometry.removable) { 371 error = B_OK; 372 *mediaStatus = B_OK; 373 } 374 } 375 } 376 return error; 377 } 378 379 380 status_t 381 KDiskDevice::GetGeometry(device_geometry* geometry) 382 { 383 if (ioctl(fFD, B_GET_GEOMETRY, geometry, sizeof(*geometry)) != 0) 384 return errno; 385 return B_OK; 386 } 387 388 389 void 390 KDiskDevice::_InitPartitionData() 391 { 392 fDeviceData.id = fPartitionData.id; 393 fPartitionData.block_size = fDeviceData.geometry.bytes_per_sector; 394 fPartitionData.physical_block_size = fDeviceData.geometry.bytes_per_physical_sector; 395 fPartitionData.offset = 0; 396 fPartitionData.size = (off_t)fPartitionData.block_size 397 * fDeviceData.geometry.sectors_per_track 398 * fDeviceData.geometry.cylinder_count 399 * fDeviceData.geometry.head_count; 400 fPartitionData.flags |= B_PARTITION_IS_DEVICE; 401 402 char name[B_FILE_NAME_LENGTH]; 403 if (ioctl(fFD, B_GET_DEVICE_NAME, name, sizeof(name)) == B_OK) 404 fPartitionData.name = strdup(name); 405 } 406 407 408 void 409 KDiskDevice::_ResetGeometry() 410 { 411 fDeviceData.geometry.bytes_per_sector = 0; 412 fDeviceData.geometry.sectors_per_track = 0; 413 fDeviceData.geometry.cylinder_count = 0; 414 fDeviceData.geometry.head_count = 0; 415 fDeviceData.geometry.device_type = B_DISK; 416 fDeviceData.geometry.removable = true; 417 fDeviceData.geometry.read_only = true; 418 fDeviceData.geometry.write_once = false; 419 } 420 421 422 void 423 KDiskDevice::_UpdateDeviceFlags() 424 { 425 if (fDeviceData.geometry.removable) 426 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_REMOVABLE); 427 if (HasMedia()) 428 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_HAS_MEDIA); 429 else 430 SetDeviceFlags(DeviceFlags() & ~B_DISK_DEVICE_HAS_MEDIA); 431 432 if (fDeviceData.geometry.read_only) 433 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_READ_ONLY); 434 if (fDeviceData.geometry.write_once) 435 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_WRITE_ONCE); 436 } 437