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