xref: /haiku/src/system/kernel/device_manager/FileDevice.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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