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