xref: /haiku/src/tools/fs_shell/unistd.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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
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
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
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
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
130 fssh_unlink(const char *name)
131 {
132 	return unlink(name);
133 }
134 
135 
136 int
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
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
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
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
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
436 fssh_getegid(void)
437 {
438 	return 0;
439 }
440 
441 
442 fssh_uid_t
443 fssh_geteuid(void)
444 {
445 	return 0;
446 }
447 
448 
449 fssh_gid_t
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
465 fssh_getuid(void)
466 {
467 	return 0;
468 }
469