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