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
20static const uint32 kBlockSize = 512;
21
22static 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
60struct 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
77FileDevice::FileDevice()
78	:
79	fFD(-1),
80	fFileSize(0)
81{
82}
83
84
85FileDevice::~FileDevice()
86{
87	if (fFD >= 0)
88		close(fFD);
89}
90
91
92status_t
93FileDevice::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
112status_t
113FileDevice::InitDevice()
114{
115	return B_OK;
116}
117
118
119void
120FileDevice::UninitDevice()
121{
122}
123
124
125void
126FileDevice::Removed()
127{
128	delete this;
129}
130
131
132bool
133FileDevice::HasSelect() const
134{
135	return false;
136}
137
138
139bool
140FileDevice::HasDeselect() const
141{
142	return false;
143}
144
145
146bool
147FileDevice::HasRead() const
148{
149	return true;
150}
151
152
153bool
154FileDevice::HasWrite() const
155{
156	return true;
157}
158
159
160bool
161FileDevice::HasIO() const
162{
163	// TODO: Support!
164	return false;
165}
166
167
168status_t
169FileDevice::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
196status_t
197FileDevice::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
212status_t
213FileDevice::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
228status_t
229FileDevice::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
244template<typename ResultType>
245static status_t
246set_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
264status_t
265FileDevice::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
368status_t
369FileDevice::Select(void* _cookie, uint8 event, selectsync* sync)
370{
371	// TODO: Support (select_fd())!
372	return B_UNSUPPORTED;
373}
374
375
376status_t
377FileDevice::Deselect(void* cookie, uint8 event, selectsync* sync)
378{
379	// TODO: Support (deselect_fd())!
380	return B_UNSUPPORTED;
381}
382
383
384status_t
385FileDevice::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
394status_t
395FileDevice::Free(void* _cookie)
396{
397	delete (Cookie*)_cookie;
398	return B_OK;
399}
400