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