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