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