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