1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "FileDevice.h" 8 9 #include <errno.h> 10 #include <string.h> 11 #include <unistd.h> 12 13 #include <new> 14 15 #include <fs_interface.h> 16 17 #include <vfs.h> 18 19 20 static const uint32 kBlockSize = 512; 21 22 static const uint8 kDeviceIcon[] = { 23 0x6e, 0x63, 0x69, 0x66, 0x08, 0x05, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 24 0x06, 0x02, 0x3b, 0x01, 0x9b, 0x3a, 0xa2, 0x35, 0xbc, 0x24, 0x3e, 0x3c, 25 0x71, 0xd2, 0x48, 0xd1, 0x7c, 0x49, 0x84, 0x91, 0x00, 0xe7, 0xbb, 0x8f, 26 0xff, 0xc9, 0x98, 0x67, 0x02, 0x00, 0x06, 0x02, 0x3b, 0xa7, 0x11, 0x38, 27 0xd0, 0xc8, 0xbb, 0xf4, 0xb8, 0x3e, 0x90, 0xe6, 0x4a, 0xed, 0x7c, 0x48, 28 0x5b, 0xd7, 0x00, 0x8a, 0x56, 0x1d, 0xff, 0xb5, 0x7a, 0x3a, 0x02, 0x00, 29 0x06, 0x02, 0xbb, 0x6f, 0xcb, 0xb8, 0xd4, 0xc8, 0x39, 0xaa, 0x71, 0xbc, 30 0x39, 0x92, 0x49, 0x2f, 0xf1, 0x48, 0xd9, 0x6a, 0x00, 0xff, 0xc7, 0x90, 31 0xff, 0xff, 0xf4, 0xea, 0x03, 0x66, 0x33, 0x00, 0x03, 0xff, 0xdf, 0xc0, 32 0x03, 0xad, 0x72, 0x38, 0x11, 0x0a, 0x06, 0x26, 0x54, 0x3a, 0x46, 0x4c, 33 0x45, 0x5c, 0x4b, 0x4c, 0x60, 0x3e, 0x60, 0x0a, 0x06, 0x38, 0x22, 0x26, 34 0x2e, 0x26, 0x4f, 0x3c, 0x5a, 0x4e, 0x48, 0x4e, 0x2a, 0x0a, 0x04, 0x26, 35 0x2e, 0x26, 0x4f, 0x3c, 0x5a, 0x3c, 0x37, 0x0a, 0x04, 0x3c, 0x37, 0x3c, 36 0x5a, 0x4e, 0x48, 0x4e, 0x2a, 0x0a, 0x04, 0x38, 0x22, 0x26, 0x2e, 0x3c, 37 0x37, 0x4e, 0x2a, 0x0a, 0x04, 0x28, 0x32, 0x28, 0x4e, 0x3a, 0x57, 0x3a, 38 0x39, 0x0a, 0x04, 0x2a, 0x4d, 0x2b, 0x46, 0x38, 0x49, 0x2a, 0x43, 0x0a, 39 0x04, 0x2a, 0x4d, 0x36, 0x52, 0x38, 0x49, 0x2b, 0x46, 0x0a, 0x04, 0x2a, 40 0x4d, 0x38, 0x54, 0x38, 0x49, 0x36, 0x52, 0x0a, 0x04, 0x2e, 0x4c, 0xbb, 41 0x2b, 0xc5, 0xd3, 0xbb, 0x2b, 0xc5, 0x07, 0x2e, 0x4a, 0x0a, 0x04, 0x2c, 42 0x49, 0x34, 0x4d, 0x34, 0x4b, 0x2c, 0x47, 0x0a, 0x04, 0x2a, 0x35, 0x2a, 43 0x40, 0x2b, 0x38, 0x38, 0x3b, 0x0a, 0x04, 0x36, 0x44, 0x2a, 0x40, 0x38, 44 0x46, 0x38, 0x3b, 0x0a, 0x04, 0x2b, 0x38, 0x2a, 0x40, 0x36, 0x44, 0x38, 45 0x3b, 0x0a, 0x04, 0x2e, 0xbe, 0x67, 0x2e, 0xbf, 0x33, 0xbb, 0x2d, 0xc0, 46 0x3f, 0xbb, 0x2d, 0xbf, 0x73, 0x0a, 0x04, 0x2c, 0xbd, 0x29, 0x2c, 0xbd, 47 0xf5, 0x34, 0x3f, 0x34, 0x3d, 0x08, 0x02, 0x2a, 0x4e, 0x2a, 0x54, 0x0e, 48 0x0a, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x10, 0x10, 0x01, 0x17, 49 0x84, 0x20, 0x04, 0x0a, 0x00, 0x01, 0x10, 0x30, 0x30, 0x29, 0x01, 0x17, 50 0x84, 0x20, 0x04, 0x0a, 0x00, 0x01, 0x10, 0x30, 0x40, 0x1b, 0x01, 0x17, 51 0x84, 0x20, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x10, 0x01, 0x17, 0x84, 0x00, 52 0x04, 0x0a, 0x02, 0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 53 0x04, 0x01, 0x04, 0x00, 0x0a, 0x05, 0x01, 0x05, 0x00, 0x0a, 0x06, 0x02, 54 0x0b, 0x06, 0x00, 0x0a, 0x02, 0x02, 0x07, 0x0d, 0x00, 0x0a, 0x07, 0x02, 55 0x0c, 0x08, 0x00, 0x0a, 0x03, 0x02, 0x09, 0x0e, 0x08, 0x15, 0xff, 0x0a, 56 0x00, 0x02, 0x0a, 0x0f, 0x08, 0x15, 0xff 57 }; 58 59 60 struct FileDevice::Cookie { 61 int fd; 62 63 Cookie(int fd) 64 : 65 fd(fd) 66 { 67 } 68 69 ~Cookie() 70 { 71 if (fd >= 0) 72 close(fd); 73 } 74 }; 75 76 77 FileDevice::FileDevice() 78 : 79 fFD(-1), 80 fFileSize(0) 81 { 82 } 83 84 85 FileDevice::~FileDevice() 86 { 87 if (fFD >= 0) 88 close(fFD); 89 } 90 91 92 status_t 93 FileDevice::Init(const char* path) 94 { 95 fFD = open(path, O_RDONLY | O_NOTRAVERSE); 96 if (fFD < 0) 97 return errno; 98 99 struct stat st; 100 if (fstat(fFD, &st) != 0) 101 return errno; 102 103 if (!S_ISREG(st.st_mode)) 104 return B_BAD_TYPE; 105 106 fFileSize = st.st_size / kBlockSize * kBlockSize; 107 108 return B_OK; 109 } 110 111 112 status_t 113 FileDevice::InitDevice() 114 { 115 return B_OK; 116 } 117 118 119 void 120 FileDevice::UninitDevice() 121 { 122 } 123 124 125 void 126 FileDevice::Removed() 127 { 128 delete this; 129 } 130 131 132 bool 133 FileDevice::HasSelect() const 134 { 135 return false; 136 } 137 138 139 bool 140 FileDevice::HasDeselect() const 141 { 142 return false; 143 } 144 145 146 bool 147 FileDevice::HasRead() const 148 { 149 return true; 150 } 151 152 153 bool 154 FileDevice::HasWrite() const 155 { 156 return true; 157 } 158 159 160 bool 161 FileDevice::HasIO() const 162 { 163 // TODO: Support! 164 return false; 165 } 166 167 168 status_t 169 FileDevice::Open(const char* path, int openMode, void** _cookie) 170 { 171 // get the vnode 172 struct vnode* vnode; 173 status_t error = vfs_get_vnode_from_fd(fFD, true, &vnode); 174 if (error != B_OK) 175 return error; 176 177 // open it 178 int fd = vfs_open_vnode(vnode, openMode, true); 179 if (fd < 0) { 180 vfs_put_vnode(vnode); 181 return fd; 182 } 183 // our vnode reference does now belong to the FD 184 185 Cookie* cookie = new(std::nothrow) Cookie(fd); 186 if (cookie == NULL) { 187 close(fd); 188 return B_NO_MEMORY; 189 } 190 191 *_cookie = cookie; 192 return B_OK; 193 } 194 195 196 status_t 197 FileDevice::Read(void* _cookie, off_t pos, void* buffer, size_t* _length) 198 { 199 Cookie* cookie = (Cookie*)_cookie; 200 201 ssize_t bytesRead = pread(cookie->fd, buffer, *_length, pos); 202 if (bytesRead < 0) { 203 *_length = 0; 204 return errno; 205 } 206 207 *_length = bytesRead; 208 return B_OK; 209 } 210 211 212 status_t 213 FileDevice::Write(void* _cookie, off_t pos, const void* buffer, size_t* _length) 214 { 215 Cookie* cookie = (Cookie*)_cookie; 216 217 ssize_t bytesWritten = pwrite(cookie->fd, buffer, *_length, pos); 218 if (bytesWritten < 0) { 219 *_length = 0; 220 return errno; 221 } 222 223 *_length = bytesWritten; 224 return B_OK; 225 } 226 227 228 status_t 229 FileDevice::IO(void* _cookie, io_request* request) 230 { 231 // Cookie* cookie = (Cookie*)_cookie; 232 // return do_fd_io(cookie->fd, request); 233 // TODO: The implementation is fine in principle, but do_fd_io() requires either 234 // the io() hook or the {read,write}_pages() hooks of the underlying FS to be 235 // implemented, which we can't guarantee. do_fd_io() should work around by using 236 // read() and write(), but it's all quite of a mess, since we mix up the io() 237 // hook -- which ATM has the semantics of uncached_io() hook (i.e. ignoring the 238 // file cache) -- with the actual io() hook semantics (i.e. using the file 239 // cache). 240 return B_UNSUPPORTED; 241 } 242 243 244 template<typename ResultType> 245 static status_t 246 set_ioctl_result(const ResultType& result, void* buffer, size_t length) 247 { 248 // NOTE: We omit the buffer size check for sake of callers (e.g. BFS) not 249 // specifying a length argument. 250 // if (sizeof(ResultType) < length) 251 // return B_BAD_VALUE; 252 253 if (buffer == NULL) 254 return B_BAD_ADDRESS; 255 256 if (IS_USER_ADDRESS(buffer)) 257 return user_memcpy(buffer, &result, sizeof(ResultType)); 258 259 memcpy(buffer, &result, sizeof(ResultType)); 260 return B_OK; 261 } 262 263 264 status_t 265 FileDevice::Control(void* _cookie, int32 op, void* buffer, size_t length) 266 { 267 Cookie* cookie = (Cookie*)_cookie; 268 269 switch (op) { 270 case B_GET_DEVICE_SIZE: 271 return set_ioctl_result( 272 (uint64)fFileSize > (uint64)(~(size_t)0) ? ~(size_t)0 : (size_t)fFileSize, 273 buffer, length); 274 275 case B_SET_BLOCKING_IO: 276 case B_SET_NONBLOCKING_IO: 277 // TODO: Translate to O_NONBLOCK and pass on! 278 return B_OK; 279 280 case B_GET_READ_STATUS: 281 case B_GET_WRITE_STATUS: 282 // TODO: poll() the FD! 283 return set_ioctl_result(true, buffer, length); 284 285 case B_GET_ICON: 286 return B_UNSUPPORTED; 287 288 case B_GET_ICON_NAME: 289 return user_strlcpy((char *)buffer, "devices/device-volume", 290 B_FILE_NAME_LENGTH); 291 292 case B_GET_VECTOR_ICON: 293 { 294 if (length != sizeof(device_icon)) { 295 return B_BAD_VALUE; 296 } 297 298 device_icon iconData; 299 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) { 300 return B_BAD_ADDRESS; 301 } 302 303 if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) { 304 if (user_memcpy(iconData.icon_data, kDeviceIcon, 305 sizeof(kDeviceIcon)) != B_OK) { 306 return B_BAD_ADDRESS; 307 } 308 } 309 310 iconData.icon_size = sizeof(kDeviceIcon); 311 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 312 } 313 314 case B_GET_GEOMETRY: 315 case B_GET_BIOS_GEOMETRY: 316 { 317 // fill in the geometry 318 // Optimally we have only 1 block per sector and only one head. 319 // Since we have only a uint32 for the cylinder count, this won't 320 // work for files > 2TB. So, we set the head count to the minimally 321 // possible value. 322 off_t blocks = fFileSize / kBlockSize; 323 uint32 heads = (blocks + 0xfffffffe) / 0xffffffff; 324 if (heads == 0) 325 heads = 1; 326 327 device_geometry geometry; 328 geometry.bytes_per_sector = kBlockSize; 329 geometry.sectors_per_track = 1; 330 geometry.cylinder_count = blocks / heads; 331 geometry.head_count = heads; 332 geometry.device_type = B_DISK; 333 geometry.removable = false; 334 geometry.read_only = false; 335 geometry.write_once = false; 336 337 return set_ioctl_result(geometry, buffer, length); 338 } 339 340 case B_GET_MEDIA_STATUS: 341 return set_ioctl_result((status_t)B_OK, buffer, length); 342 343 case B_SET_INTERRUPTABLE_IO: 344 case B_SET_UNINTERRUPTABLE_IO: 345 return B_OK; 346 347 case B_FLUSH_DRIVE_CACHE: 348 return fsync(cookie->fd) == 0 ? B_OK : errno; 349 350 case B_GET_BIOS_DRIVE_ID: 351 return set_ioctl_result((uint8)0xf8, buffer, length); 352 353 case B_GET_DRIVER_FOR_DEVICE: 354 case B_SET_DEVICE_SIZE: 355 case B_SET_PARTITION: 356 case B_FORMAT_DEVICE: 357 case B_EJECT_DEVICE: 358 case B_LOAD_MEDIA: 359 case B_GET_NEXT_OPEN_DEVICE: 360 default: 361 return B_BAD_VALUE; 362 } 363 364 return B_OK; 365 } 366 367 368 status_t 369 FileDevice::Select(void* _cookie, uint8 event, selectsync* sync) 370 { 371 // TODO: Support (select_fd())! 372 return B_UNSUPPORTED; 373 } 374 375 376 status_t 377 FileDevice::Deselect(void* cookie, uint8 event, selectsync* sync) 378 { 379 // TODO: Support (deselect_fd())! 380 return B_UNSUPPORTED; 381 } 382 383 384 status_t 385 FileDevice::Close(void* cookie) 386 { 387 // TODO: This should probably really close the FD. Depending on the 388 // underlying FS operations could block and close() would be needed to 389 // unblock them. 390 return B_OK; 391 } 392 393 394 status_t 395 FileDevice::Free(void* _cookie) 396 { 397 delete (Cookie*)_cookie; 398 return B_OK; 399 } 400