1 /*
2 * Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include "compatibility.h"
7
8 #include "fssh_unistd.h"
9
10 #include <errno.h>
11 #include <stdarg.h>
12 #include <unistd.h>
13
14 #include <SupportDefs.h>
15
16 #include "fssh_drivers.h"
17 #include "fssh_errno.h"
18 #include "partition_support.h"
19
20 #if (defined(__BEOS__) || defined(__HAIKU__))
21 # include <Drivers.h>
22 #else
23 # if defined(HAIKU_HOST_PLATFORM_FREEBSD) \
24 || defined(HAIKU_HOST_PLATFORM_DARWIN)
25 # include <sys/ioctl.h>
26 # include <sys/stat.h>
27 # include <sys/disk.h>
28 # ifndef HAIKU_HOST_PLATFORM_DARWIN
29 # include <sys/disklabel.h>
30 # endif
31 # elif defined(HAIKU_HOST_PLATFORM_LINUX)
32 # include <linux/hdreg.h>
33 # include <linux/fs.h>
34 # include <sys/ioctl.h>
35 # else
36 // the (POSIX) correct place of definition for ioctl()
37 # include <stropts.h>
38 # endif
39 #endif
40
41
42 #if (!defined(__BEOS__) && !defined(__HAIKU__))
43 // Defined in libroot_build.so.
44 # define _kern_read _kernbuild_read
45 # define _kern_write _kernbuild_write
46 # define _kern_dup _kernbuild_dup
47 # define _kern_close _kernbuild_close
48 extern "C" ssize_t _kern_read(int fd, off_t pos, void *buffer, size_t bufferSize);
49 extern "C" ssize_t _kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize);
50 extern "C" int _kern_dup(int fd);
51 extern "C" status_t _kern_close(int fd);
52 #endif
53
54
55 #ifdef HAIKU_HOST_PLATFORM_LINUX
56
57 static bool
test_size(int fd,off_t size)58 test_size(int fd, off_t size)
59 {
60 char buffer[1];
61
62 if (size == 0)
63 return true;
64
65 if (lseek(fd, size - 1, SEEK_SET) < 0)
66 return false;
67
68 return (read(fd, &buffer, 1) == 1);
69 }
70
71
72 static off_t
get_partition_size(int fd,off_t maxSize)73 get_partition_size(int fd, off_t maxSize)
74 {
75 // binary search
76 off_t lower = 0;
77 off_t upper = maxSize;
78 while (lower < upper) {
79 off_t mid = (lower + upper + 1) / 2;
80 if (test_size(fd, mid))
81 lower = mid;
82 else
83 upper = mid - 1;
84 }
85
86 return lower;
87 }
88
89 #endif // HAIKU_HOST_PLATFORM_LINUX
90
91
92 int
fssh_dup(int fd)93 fssh_dup(int fd)
94 {
95 // Use the _kern_dup() defined in libroot on BeOS incompatible systems.
96 // Required for proper attribute emulation support.
97 int newFD;
98 #if (defined(__BEOS__) || defined(__HAIKU__))
99 newFD = dup(fd);
100 #else
101 newFD = _kern_dup(fd);
102 if (newFD < 0) {
103 fssh_set_errno(newFD);
104 newFD = -1;
105 }
106 #endif
107
108 FSShell::restricted_file_duped(fd, newFD);
109
110 return newFD;
111 }
112
113
114 int
fssh_close(int fd)115 fssh_close(int fd)
116 {
117 FSShell::restricted_file_closed(fd);
118
119 // Use the _kern_close() defined in libroot on BeOS incompatible systems.
120 // Required for proper attribute emulation support.
121 #if (defined(__BEOS__) || defined(__HAIKU__))
122 return close(fd);
123 #else
124 return _kern_close(fd);
125 #endif
126 }
127
128
129 int
fssh_unlink(const char * name)130 fssh_unlink(const char *name)
131 {
132 return unlink(name);
133 }
134
135
136 int
fssh_ioctl(int fd,unsigned long op,...)137 fssh_ioctl(int fd, unsigned long op, ...)
138 {
139 status_t error = B_BAD_VALUE;
140 va_list list;
141
142 // count arguments
143
144 va_start(list, op);
145
146 switch (op) {
147 case FSSH_B_GET_GEOMETRY:
148 {
149 fssh_device_geometry *geometry
150 = va_arg(list, fssh_device_geometry*);
151
152 #if (defined(__BEOS__) || defined(__HAIKU__))
153 device_geometry systemGeometry;
154 if (ioctl(fd, B_GET_GEOMETRY, &systemGeometry) == 0) {
155 geometry->bytes_per_sector
156 = systemGeometry.bytes_per_sector;
157 geometry->sectors_per_track
158 = systemGeometry.sectors_per_track;
159 geometry->cylinder_count = systemGeometry.cylinder_count;
160 geometry->head_count = systemGeometry.head_count;
161 geometry->device_type = systemGeometry.device_type;
162 geometry->removable = systemGeometry.removable;
163 geometry->read_only = systemGeometry.read_only;
164 geometry->write_once = systemGeometry.write_once;
165 error = B_OK;
166 } else
167 error = errno;
168
169 #elif defined(HAIKU_HOST_PLATFORM_LINUX)
170 // If BLKGETSIZE64 don't work for us, we will fall back to
171 // HDIO_GETGEO (which is kind of obsolete, BTW), and
172 // get the partition size via binary search.
173 struct hd_geometry hdGeometry;
174 int blockSize = 512;
175 off_t size;
176 if (ioctl(fd, BLKGETSIZE64, &size) == 0 && size > 0) {
177 off_t blocks = size / blockSize;
178 uint32_t heads = (blocks + ULONG_MAX - 1)
179 / ULONG_MAX;
180 if (heads == 0)
181 heads = 1;
182
183 geometry->head_count = heads;
184 geometry->cylinder_count = blocks / heads;
185 geometry->sectors_per_track = 1;
186 error = B_OK;
187 } else if (ioctl(fd, HDIO_GETGEO, &hdGeometry) == 0) {
188 if (hdGeometry.heads == 0) {
189 error = B_ERROR;
190 } else {
191 off_t bytesPerCylinder = (off_t)hdGeometry.heads
192 * hdGeometry.sectors * 512;
193 off_t deviceSize = bytesPerCylinder * hdGeometry.cylinders;
194 off_t partitionSize = get_partition_size(fd, deviceSize);
195
196 geometry->head_count = hdGeometry.heads;
197 geometry->cylinder_count = partitionSize / bytesPerCylinder;
198 geometry->sectors_per_track = hdGeometry.sectors;
199 error = B_OK;
200 }
201 } else
202 error = errno;
203
204 if (error == B_OK) {
205 // TODO: Get the real values...
206 geometry->bytes_per_sector = blockSize;
207 geometry->device_type = FSSH_B_DISK;
208 geometry->removable = false;
209 geometry->read_only = false;
210 geometry->write_once = false;
211 }
212
213 #elif HAIKU_HOST_PLATFORM_FREEBSD
214 {
215 // FreeBSD has not block devices
216
217 struct stat status;
218
219 if (fstat(fd, &status) == 0) {
220 // Do nothing for a regular file
221 if (S_ISREG(status.st_mode))
222 break;
223
224 struct disklabel disklabel;
225 off_t mediaSize;
226
227 memset(&disklabel,0,sizeof disklabel);
228
229 // Ignore errors, this way we can use memory devices (md%d)
230 ioctl(fd, DIOCGSECTORSIZE, &disklabel.d_secsize);
231 ioctl(fd, DIOCGFWSECTORS, &disklabel.d_nsectors);
232 ioctl(fd, DIOCGFWHEADS, &disklabel.d_ntracks);
233 ioctl(fd, DIOCGMEDIASIZE, &mediaSize);
234
235 if (disklabel.d_nsectors == 0) {
236 // Seems to be a md device, then ioctls returns lots of
237 // zeroes and hardcode some defaults
238 disklabel.d_nsectors = 64;
239 disklabel.d_ntracks = 16;
240 }
241
242 disklabel.d_secperunit = mediaSize / disklabel.d_secsize;
243 disklabel.d_ncylinders = mediaSize / disklabel.d_secsize
244 / disklabel.d_nsectors / disklabel.d_ntracks;
245
246 geometry->head_count = disklabel.d_ntracks;
247 geometry->cylinder_count = disklabel.d_ncylinders;
248 geometry->sectors_per_track = disklabel.d_nsectors;
249
250 geometry->bytes_per_sector = disklabel.d_secsize;
251 // FreeBSD supports device_type flag as disklabel.d_type,
252 // for now we harcod it to B_DISK.
253 geometry->device_type = FSSH_B_DISK;
254 geometry->removable = disklabel.d_flags & D_REMOVABLE > 0;
255 // read_only?
256 geometry->read_only = false;
257 // FreeBSD does not support write_once flag.
258 geometry->write_once = false;
259 error = B_OK;
260 } else
261 error = errno;
262 }
263 #elif HAIKU_HOST_PLATFORM_DARWIN
264 {
265 // Darwin does not seems to provide a way to access disk
266 // geometry directly
267
268 struct stat status;
269
270 if (fstat(fd, &status) == 0) {
271 // Do nothing for a regular file
272 if (S_ISREG(status.st_mode))
273 break;
274
275 off_t mediaSize;
276
277 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &mediaSize) != 0) {
278 error = errno;
279 break;
280 }
281
282 geometry->head_count = 4;
283 geometry->sectors_per_track = 63;
284 geometry->cylinder_count = mediaSize / geometry->head_count
285 / geometry->sectors_per_track;
286
287 while (geometry->cylinder_count > 1024
288 && geometry->head_count < 256) {
289 geometry->head_count *= 2;
290 geometry->cylinder_count /= 2;
291 }
292
293 if (geometry->head_count == 256) {
294 geometry->head_count = 255;
295 geometry->cylinder_count = mediaSize
296 / geometry->head_count
297 / geometry->sectors_per_track;
298 }
299
300 if (ioctl(fd, DKIOCGETBLOCKSIZE,
301 &geometry->bytes_per_sector) != 0) {
302 error = errno;
303 break;
304 }
305
306 uint32_t isWritable;
307 if (ioctl(fd, DKIOCISWRITABLE, &isWritable) != 0) {
308 error = errno;
309 break;
310 }
311
312 geometry->read_only = !isWritable;
313
314 // TODO: Get the real values...
315 geometry->device_type = FSSH_B_DISK;
316 geometry->removable = false;
317 geometry->write_once = false;
318
319 error = B_OK;
320 } else
321 error = errno;
322 }
323 #else
324 // Not implemented for this platform, i.e. we won't be able to
325 // deal with disk devices.
326 #endif
327
328 break;
329 }
330
331 case FSSH_B_FLUSH_DRIVE_CACHE:
332 {
333 #if (defined(__BEOS__) || defined(__HAIKU__))
334 if (ioctl(fd, B_FLUSH_DRIVE_CACHE) == 0)
335 error = B_OK;
336 else
337 error = errno;
338 #else
339 error = B_OK;
340 #endif
341
342 break;
343 }
344
345 case 10000: // IOCTL_FILE_UNCACHED_IO
346 {
347 #if (defined(__BEOS__) || defined(__HAIKU__))
348 if (ioctl(fd, 10000) == 0)
349 error = B_OK;
350 else
351 error = errno;
352 #else
353 error = B_OK;
354 #endif
355
356 break;
357 }
358 }
359
360 va_end(list);
361
362 if (error != B_OK) {
363 fssh_set_errno(error);
364 return -1;
365 }
366 return 0;
367 }
368
369
370 fssh_ssize_t
fssh_read(int fd,void * buffer,fssh_size_t count)371 fssh_read(int fd, void *buffer, fssh_size_t count)
372 {
373 #if !defined(HAIKU_HOST_PLATFORM_FREEBSD)
374 fssh_off_t pos = -1;
375 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0)
376 return -1;
377 return read(fd, buffer, count);
378 #else
379 fssh_ssize_t bytesRead = _kern_read(fd, fssh_lseek(fd, 0, FSSH_SEEK_CUR),
380 buffer, count);
381 if (bytesRead > 0)
382 fssh_lseek(fd, bytesRead, FSSH_SEEK_CUR);
383 return bytesRead;
384 #endif
385 }
386
387
388 fssh_ssize_t
fssh_read_pos(int fd,fssh_off_t pos,void * buffer,fssh_size_t count)389 fssh_read_pos(int fd, fssh_off_t pos, void *buffer, fssh_size_t count)
390 {
391 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0)
392 return -1;
393 #if defined(__HAIKU__)
394 return read_pos(fd, pos, buffer, count);
395 #else
396 return _kern_read(fd, pos, buffer, count);
397 #endif
398 }
399
400
401 fssh_ssize_t
fssh_write(int fd,const void * buffer,fssh_size_t count)402 fssh_write(int fd, const void *buffer, fssh_size_t count)
403 {
404 #if !defined(HAIKU_HOST_PLATFORM_FREEBSD)
405 fssh_off_t pos = -1;
406 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0)
407 return -1;
408 return write(fd, buffer, count);
409 #else
410 fssh_ssize_t written = write_pos(fd, fssh_lseek(fd, 0, FSSH_SEEK_CUR),
411 buffer, count);
412 if (written > 0)
413 fssh_lseek(fd, written, FSSH_SEEK_CUR);
414 return written;
415 #endif
416 }
417
418
419 fssh_ssize_t
fssh_write_pos(int fd,fssh_off_t pos,const void * buffer,fssh_size_t count)420 fssh_write_pos(int fd, fssh_off_t pos, const void *buffer, fssh_size_t count)
421 {
422 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0)
423 return -1;
424 #if defined(__HAIKU__)
425 return write_pos(fd, pos, buffer, count);
426 #else
427 return _kern_write(fd, pos, buffer, count);
428 #endif
429 }
430
431
432 // fssh_lseek() -- implemented in partition_support.cpp
433
434
435 fssh_gid_t
fssh_getegid(void)436 fssh_getegid(void)
437 {
438 return 0;
439 }
440
441
442 fssh_uid_t
fssh_geteuid(void)443 fssh_geteuid(void)
444 {
445 return 0;
446 }
447
448
449 fssh_gid_t
fssh_getgid(void)450 fssh_getgid(void)
451 {
452 return 0;
453 }
454
455
456 #if 0
457 int
458 fssh_getgroups(int groupSize, fssh_gid_t groupList[])
459 {
460 }
461 #endif // 0
462
463
464 fssh_uid_t
fssh_getuid(void)465 fssh_getuid(void)
466 {
467 return 0;
468 }
469