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 23 struct FileDevice::Cookie { 24 int fd; 25 26 Cookie(int fd) 27 : 28 fd(fd) 29 { 30 } 31 32 ~Cookie() 33 { 34 if (fd >= 0) 35 close(fd); 36 } 37 }; 38 39 40 FileDevice::FileDevice() 41 : 42 fFD(-1), 43 fFileSize(0) 44 { 45 } 46 47 48 FileDevice::~FileDevice() 49 { 50 if (fFD >= 0) 51 close(fFD); 52 } 53 54 55 status_t 56 FileDevice::Init(const char* path) 57 { 58 fFD = open(path, O_RDONLY | O_NOTRAVERSE); 59 if (fFD < 0) 60 return errno; 61 62 struct stat st; 63 if (fstat(fFD, &st) != 0) 64 return errno; 65 66 if (!S_ISREG(st.st_mode)) 67 return B_BAD_TYPE; 68 69 fFileSize = st.st_size / kBlockSize * kBlockSize; 70 71 return B_OK; 72 } 73 74 75 status_t 76 FileDevice::InitDevice() 77 { 78 return B_OK; 79 } 80 81 82 void 83 FileDevice::UninitDevice() 84 { 85 } 86 87 88 void 89 FileDevice::Removed() 90 { 91 delete this; 92 } 93 94 95 bool 96 FileDevice::HasSelect() const 97 { 98 return false; 99 } 100 101 102 bool 103 FileDevice::HasDeselect() const 104 { 105 return false; 106 } 107 108 109 bool 110 FileDevice::HasRead() const 111 { 112 return true; 113 } 114 115 116 bool 117 FileDevice::HasWrite() const 118 { 119 return true; 120 } 121 122 123 bool 124 FileDevice::HasIO() const 125 { 126 // TODO: Support! 127 return false; 128 } 129 130 131 status_t 132 FileDevice::Open(const char* path, int openMode, void** _cookie) 133 { 134 // get the vnode 135 struct vnode* vnode; 136 status_t error = vfs_get_vnode_from_fd(fFD, true, &vnode); 137 if (error != B_OK) 138 return error; 139 140 // open it 141 int fd = vfs_open_vnode(vnode, openMode, true); 142 if (fd < 0) { 143 vfs_put_vnode(vnode); 144 return fd; 145 } 146 // our vnode reference does now belong to the FD 147 148 Cookie* cookie = new(std::nothrow) Cookie(fd); 149 if (cookie == NULL) { 150 close(fd); 151 return B_NO_MEMORY; 152 } 153 154 *_cookie = cookie; 155 return B_OK; 156 } 157 158 159 status_t 160 FileDevice::Read(void* _cookie, off_t pos, void* buffer, size_t* _length) 161 { 162 Cookie* cookie = (Cookie*)_cookie; 163 164 ssize_t bytesRead = pread(cookie->fd, buffer, *_length, pos); 165 if (bytesRead < 0) { 166 *_length = 0; 167 return errno; 168 } 169 170 *_length = bytesRead; 171 return B_OK; 172 } 173 174 175 status_t 176 FileDevice::Write(void* _cookie, off_t pos, const void* buffer, size_t* _length) 177 { 178 Cookie* cookie = (Cookie*)_cookie; 179 180 ssize_t bytesWritten = pwrite(cookie->fd, buffer, *_length, pos); 181 if (bytesWritten < 0) { 182 *_length = 0; 183 return errno; 184 } 185 186 *_length = bytesWritten; 187 return B_OK; 188 } 189 190 191 status_t 192 FileDevice::IO(void* _cookie, io_request* request) 193 { 194 // Cookie* cookie = (Cookie*)_cookie; 195 // return do_fd_io(cookie->fd, request); 196 // TODO: The implementation is fine in principle, but do_fd_io() requires either 197 // the io() hook or the {read,write}_pages() hooks of the underlying FS to be 198 // implemented, which we can't guarantee. do_fd_io() should work around by using 199 // read() and write(), but it's all quite of a mess, since we mix up the io() 200 // hook -- which ATM has the semantics of uncached_io() hook (i.e. ignoring the 201 // file cache) -- with the actual io() hook semantics (i.e. using the file 202 // cache). 203 return B_UNSUPPORTED; 204 } 205 206 207 template<typename ResultType> 208 static status_t 209 set_ioctl_result(const ResultType& result, void* buffer, size_t length) 210 { 211 // NOTE: We omit the buffer size check for sake of callers (e.g. BFS) not 212 // specifying a length argument. 213 // if (sizeof(ResultType) < length) 214 // return B_BAD_VALUE; 215 216 if (buffer == NULL) 217 return B_BAD_ADDRESS; 218 219 if (!IS_USER_ADDRESS(buffer)) 220 return user_memcpy(buffer, &result, sizeof(ResultType)); 221 222 memcpy(buffer, &result, sizeof(ResultType)); 223 return B_OK; 224 } 225 226 227 status_t 228 FileDevice::Control(void* _cookie, int32 op, void* buffer, size_t length) 229 { 230 Cookie* cookie = (Cookie*)_cookie; 231 232 switch (op) { 233 case B_GET_DEVICE_SIZE: 234 return set_ioctl_result( 235 fFileSize > ~(size_t)0 ? ~(size_t)0 : (size_t)fFileSize, 236 buffer, length); 237 238 case B_SET_BLOCKING_IO: 239 case B_SET_NONBLOCKING_IO: 240 // TODO: Translate to O_NONBLOCK and pass on! 241 return B_OK; 242 243 case B_GET_READ_STATUS: 244 case B_GET_WRITE_STATUS: 245 // TODO: poll() the FD! 246 return set_ioctl_result(true, buffer, length); 247 248 case B_GET_ICON: 249 return B_UNSUPPORTED; 250 251 case B_GET_GEOMETRY: 252 case B_GET_BIOS_GEOMETRY: 253 { 254 // fill in the geometry 255 // Optimally we have only 1 block per sector and only one head. 256 // Since we have only a uint32 for the cylinder count, this won't 257 // work for files > 2TB. So, we set the head count to the minimally 258 // possible value. 259 off_t blocks = fFileSize / kBlockSize; 260 uint32 heads = (blocks + 0xfffffffe) / 0xffffffff; 261 if (heads == 0) 262 heads = 1; 263 264 device_geometry geometry; 265 geometry.bytes_per_sector = kBlockSize; 266 geometry.sectors_per_track = 1; 267 geometry.cylinder_count = blocks / heads; 268 geometry.head_count = heads; 269 geometry.device_type = B_DISK; 270 geometry.removable = false; 271 geometry.read_only = false; 272 geometry.write_once = false; 273 274 return set_ioctl_result(geometry, buffer, length); 275 } 276 277 case B_GET_MEDIA_STATUS: 278 return set_ioctl_result((status_t)B_OK, buffer, length); 279 280 case B_SET_INTERRUPTABLE_IO: 281 case B_SET_UNINTERRUPTABLE_IO: 282 return B_OK; 283 284 case B_FLUSH_DRIVE_CACHE: 285 return fsync(cookie->fd) == 0 ? B_OK : errno; 286 287 case B_GET_BIOS_DRIVE_ID: 288 return set_ioctl_result((uint8)0xf8, buffer, length); 289 290 case B_GET_DRIVER_FOR_DEVICE: 291 case B_SET_DEVICE_SIZE: 292 case B_SET_PARTITION: 293 case B_FORMAT_DEVICE: 294 case B_EJECT_DEVICE: 295 case B_LOAD_MEDIA: 296 case B_GET_NEXT_OPEN_DEVICE: 297 default: 298 return B_BAD_VALUE; 299 } 300 301 return B_OK; 302 } 303 304 305 status_t 306 FileDevice::Select(void* _cookie, uint8 event, selectsync* sync) 307 { 308 // TODO: Support (select_fd())! 309 return B_UNSUPPORTED; 310 } 311 312 313 status_t 314 FileDevice::Deselect(void* cookie, uint8 event, selectsync* sync) 315 { 316 // TODO: Support (deselect_fd())! 317 return B_UNSUPPORTED; 318 } 319 320 321 status_t 322 FileDevice::Close(void* cookie) 323 { 324 // TODO: This should probably really close the FD. Depending on the 325 // underlying FS operations could block and close() would be needed to 326 // unblock them. 327 return B_OK; 328 } 329 330 331 status_t 332 FileDevice::Free(void* _cookie) 333 { 334 delete (Cookie*)_cookie; 335 return B_OK; 336 } 337