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