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
57static bool
58test_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
72static off_t
73get_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
92int
93fssh_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
114int
115fssh_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
129int
130fssh_unlink(const char *name)
131{
132	return unlink(name);
133}
134
135
136int
137fssh_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
370fssh_ssize_t
371fssh_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
388fssh_ssize_t
389fssh_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
401fssh_ssize_t
402fssh_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
419fssh_ssize_t
420fssh_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
435fssh_gid_t
436fssh_getegid(void)
437{
438	return 0;
439}
440
441
442fssh_uid_t
443fssh_geteuid(void)
444{
445	return 0;
446}
447
448
449fssh_gid_t
450fssh_getgid(void)
451{
452	return 0;
453}
454
455
456#if 0
457int
458fssh_getgroups(int groupSize, fssh_gid_t groupList[])
459{
460}
461#endif	// 0
462
463
464fssh_uid_t
465fssh_getuid(void)
466{
467	return 0;
468}
469