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