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 struct hd_geometry hdGeometry; 168 // BLKGETSIZE and BLKGETSIZE64 don't seem to work for 169 // partitions. So we get the device geometry (there only seems 170 // to be HDIO_GETGEO, which is kind of obsolete, BTW), and 171 // get the partition size via binary search. 172 if (ioctl(fd, HDIO_GETGEO, &hdGeometry) == 0) { 173 int blockSize = 512; 174 if (hdGeometry.heads == 0) { 175 off_t size; 176 if (ioctl(fd, BLKGETSIZE64, &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 188 error = errno; 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 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 } else 210 error = errno; 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 244 / 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 = read_pos(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 return read_pos(fd, pos, buffer, count); 394 } 395 396 397 fssh_ssize_t 398 fssh_write(int fd, const void *buffer, fssh_size_t count) 399 { 400 #if !defined(HAIKU_HOST_PLATFORM_FREEBSD) 401 fssh_off_t pos = -1; 402 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0) 403 return -1; 404 return write(fd, buffer, count); 405 #else 406 fssh_ssize_t written = write_pos(fd, fssh_lseek(fd, 0, FSSH_SEEK_CUR), 407 buffer, count); 408 if (written > 0) 409 fssh_lseek(fd, written, FSSH_SEEK_CUR); 410 return written; 411 #endif 412 } 413 414 415 fssh_ssize_t 416 fssh_write_pos(int fd, fssh_off_t pos, const void *buffer, fssh_size_t count) 417 { 418 if (FSShell::restricted_file_restrict_io(fd, pos, count) < 0) 419 return -1; 420 return write_pos(fd, pos, buffer, count); 421 } 422 423 424 // fssh_lseek() -- implemented in partition_support.cpp 425 426 427 fssh_gid_t 428 fssh_getegid(void) 429 { 430 return 0; 431 } 432 433 434 fssh_uid_t 435 fssh_geteuid(void) 436 { 437 return 0; 438 } 439 440 441 fssh_gid_t 442 fssh_getgid(void) 443 { 444 return 0; 445 } 446 447 448 #if 0 449 int 450 fssh_getgroups(int groupSize, fssh_gid_t groupList[]) 451 { 452 } 453 #endif // 0 454 455 456 fssh_uid_t 457 fssh_getuid(void) 458 { 459 return 0; 460 } 461