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