1/* 2 * file_media.c - 3 * 4 * Written by Eryk Vershen 5 */ 6 7/* 8 * Copyright 1997,1998 by Apple Computer, Inc. 9 * All Rights Reserved 10 * 11 * Permission to use, copy, modify, and distribute this software and 12 * its documentation for any purpose and without fee is hereby granted, 13 * provided that the above copyright notice appears in all copies and 14 * that both the copyright notice and this permission notice appear in 15 * supporting documentation. 16 * 17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE. 20 * 21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 */ 27 28// for printf() 29#include <stdio.h> 30// for malloc() & free() 31#include <stdlib.h> 32// for lseek(), read(), write(), close() 33#include <unistd.h> 34// for open() 35#include <fcntl.h> 36// for LONG_MAX 37#include <limits.h> 38// for errno 39#include <errno.h> 40 41#ifdef __linux__ 42#include <sys/ioctl.h> 43#include <linux/fs.h> 44#include <linux/hdreg.h> 45#include <sys/stat.h> 46#else 47#ifdef __unix__ 48#include <sys/ioctl.h> 49#include <sys/stat.h> 50#endif 51#endif 52 53#include "file_media.h" 54#include "errors.h" 55 56 57/* 58 * Defines 59 */ 60#ifdef __linux__ 61#define LOFF_MAX 9223372036854775807LL 62extern __loff_t llseek __P ((int __fd, __loff_t __offset, int __whence)); 63#else 64#ifdef __unix__ 65#define loff_t off_t 66#define llseek lseek 67#define LOFF_MAX LLONG_MAX 68#else 69#define loff_t long 70#define llseek lseek 71#define LOFF_MAX LONG_MAX 72#endif 73#endif 74 75 76/* 77 * Types 78 */ 79typedef struct file_media *FILE_MEDIA; 80 81struct file_media { 82 struct media m; 83 int fd; 84 int regular_file; 85}; 86 87struct file_media_globals { 88 long exists; 89 long kind; 90}; 91 92typedef struct file_media_iterator *FILE_MEDIA_ITERATOR; 93 94struct file_media_iterator { 95 struct media_iterator m; 96 long style; 97 long index; 98}; 99 100 101/* 102 * Global Constants 103 */ 104int potential_block_sizes[] = { 105 512, 1024, 2048, 106 0 107}; 108 109enum { 110 kSCSI_Disks = 0, 111 kATA_Devices = 1, 112 kSCSI_CDs = 2, 113 kMaxStyle = 2 114}; 115 116 117/* 118 * Global Variables 119 */ 120static long file_inited = 0; 121static struct file_media_globals file_info; 122 123/* 124 * Forward declarations 125 */ 126int compute_block_size(int fd); 127void file_init(void); 128FILE_MEDIA new_file_media(void); 129long read_file_media(MEDIA m, long long offset, unsigned long count, void *address); 130long write_file_media(MEDIA m, long long offset, unsigned long count, void *address); 131long close_file_media(MEDIA m); 132long os_reload_file_media(MEDIA m); 133FILE_MEDIA_ITERATOR new_file_iterator(void); 134void reset_file_iterator(MEDIA_ITERATOR m); 135char *step_file_iterator(MEDIA_ITERATOR m); 136void delete_file_iterator(MEDIA_ITERATOR m); 137 138 139/* 140 * Routines 141 */ 142void 143file_init(void) 144{ 145 if (file_inited != 0) { 146 return; 147 } 148 file_inited = 1; 149 150 file_info.kind = allocate_media_kind(); 151} 152 153 154FILE_MEDIA 155new_file_media(void) 156{ 157 return (FILE_MEDIA) new_media(sizeof(struct file_media)); 158} 159 160 161int 162compute_block_size(int fd) 163{ 164 int size; 165 int max_size; 166 loff_t x; 167 long t; 168 int i; 169 char *buffer; 170 171 max_size = 0; 172 for (i = 0; ; i++) { 173 size = potential_block_sizes[i]; 174 if (size == 0) { 175 break; 176 } 177 if (max_size < size) { 178 max_size = size; 179 } 180 } 181 182 buffer = malloc(max_size); 183 if (buffer != 0) { 184 for (i = 0; ; i++) { 185 size = potential_block_sizes[i]; 186 if (size == 0) { 187 break; 188 } 189 if ((x = llseek(fd, (loff_t)0, 0)) < 0) { 190 error(errno, "Can't seek on file"); 191 break; 192 } 193 if ((t = read(fd, buffer, size)) == size) { 194 free(buffer); 195 return size; 196 } 197 } 198 } 199 return potential_block_sizes[0]; 200} 201 202 203MEDIA 204open_file_as_media(char *file, int oflag) 205{ 206 FILE_MEDIA a; 207 int fd; 208 loff_t off; 209#if defined(__linux__) || defined(__unix__) 210 struct stat info; 211#endif 212 213 if (file_inited == 0) { 214 file_init(); 215 } 216 217 a = 0; 218 fd = open(file, oflag); 219 if (fd >= 0) { 220 a = new_file_media(); 221 if (a != 0) { 222 a->m.kind = file_info.kind; 223 a->m.grain = compute_block_size(fd); 224 off = llseek(fd, (loff_t)0, 2); /* seek to end of media */ 225#if !defined(__linux__) && !defined(__unix__) 226 if (off <= 0) { 227 off = 1; /* XXX not right? */ 228 } 229#endif 230 //printf("file size = %Ld\n", off); 231 a->m.size_in_bytes = (long long) off; 232 a->m.do_read = read_file_media; 233 a->m.do_write = write_file_media; 234 a->m.do_close = close_file_media; 235 a->m.do_os_reload = os_reload_file_media; 236 a->fd = fd; 237 a->regular_file = 0; 238#if defined(__linux__) || defined(__unix__) 239 if (fstat(fd, &info) < 0) { 240 error(errno, "can't stat file '%s'", file); 241 } else { 242 a->regular_file = S_ISREG(info.st_mode); 243 } 244#endif 245 } else { 246 close(fd); 247 } 248 } 249 return (MEDIA) a; 250} 251 252 253long 254read_file_media(MEDIA m, long long offset, unsigned long count, void *address) 255{ 256 FILE_MEDIA a; 257 long rtn_value; 258 loff_t off; 259 int t; 260 261 a = (FILE_MEDIA) m; 262 rtn_value = 0; 263 if (a == 0) { 264 /* no media */ 265 fprintf(stderr,"no media\n"); 266 } else if (a->m.kind != file_info.kind) { 267 /* wrong kind - XXX need to error here - this is an internal problem */ 268 fprintf(stderr,"wrong kind\n"); 269 } else if (count <= 0 || count % a->m.grain != 0) { 270 /* can't handle size */ 271 fprintf(stderr,"bad size\n"); 272 } else if (offset < 0 || offset % a->m.grain != 0) { 273 /* can't handle offset */ 274 fprintf(stderr,"bad offset\n"); 275 } else if (offset + count > a->m.size_in_bytes && a->m.size_in_bytes != (long long) 0) { 276 /* check for offset (and offset+count) too large */ 277 fprintf(stderr,"offset+count too large\n"); 278 } else if (offset + count > (long long) LOFF_MAX) { 279 /* check for offset (and offset+count) too large */ 280 fprintf(stderr,"offset+count too large 2\n"); 281 } else { 282 /* do the read */ 283 off = offset; 284 if ((off = llseek(a->fd, off, 0)) >= 0) { 285 if ((t = read(a->fd, address, count)) == (ssize_t)count) { 286 rtn_value = 1; 287 } else { 288 // fprintf(stderr,"read failed\n"); 289 } 290 } else { 291 // fprintf(stderr,"lseek failed\n"); 292 } 293 } 294 return rtn_value; 295} 296 297 298long 299write_file_media(MEDIA m, long long offset, unsigned long count, void *address) 300{ 301 FILE_MEDIA a; 302 long rtn_value; 303 loff_t off; 304 int t; 305 306 a = (FILE_MEDIA) m; 307 rtn_value = 0; 308 if (a == 0) { 309 /* no media */ 310 } else if (a->m.kind != file_info.kind) { 311 /* wrong kind - XXX need to error here - this is an internal problem */ 312 } else if (count <= 0 || count % a->m.grain != 0) { 313 /* can't handle size */ 314 } else if (offset < 0 || offset % a->m.grain != 0) { 315 /* can't handle offset */ 316 } else if (offset + count > (long long) LOFF_MAX) { 317 /* check for offset (and offset+count) too large */ 318 } else { 319 /* do the write */ 320 off = offset; 321 if ((off = llseek(a->fd, off, 0)) >= 0) { 322 if ((t = write(a->fd, address, count)) == (ssize_t)count) { 323 if (off + count > a->m.size_in_bytes) { 324 a->m.size_in_bytes = off + count; 325 } 326 rtn_value = 1; 327 } 328 } 329 } 330 return rtn_value; 331} 332 333 334long 335close_file_media(MEDIA m) 336{ 337 FILE_MEDIA a; 338 339 a = (FILE_MEDIA) m; 340 if (a == 0) { 341 return 0; 342 } else if (a->m.kind != file_info.kind) { 343 /* XXX need to error here - this is an internal problem */ 344 return 0; 345 } 346 347 close(a->fd); 348 return 1; 349} 350 351 352long 353os_reload_file_media(MEDIA m) 354{ 355 FILE_MEDIA a; 356 long rtn_value; 357#if defined(__linux__) 358 int i; 359 int saved_errno; 360#endif 361 362 a = (FILE_MEDIA) m; 363 rtn_value = 0; 364 if (a == 0) { 365 /* no media */ 366 } else if (a->m.kind != file_info.kind) { 367 /* wrong kind - XXX need to error here - this is an internal problem */ 368 } else if (a->regular_file) { 369 /* okay - nothing to do */ 370 rtn_value = 1; 371 } else { 372#ifdef __linux__ 373 sync(); 374 sleep(2); 375 if ((i = ioctl(a->fd, BLKRRPART)) != 0) { 376 saved_errno = errno; 377 } else { 378 // some kernel versions (1.2.x) seem to have trouble 379 // rereading the partition table, but if asked to do it 380 // twice, the second time works. - biro@yggdrasil.com */ 381 sync(); 382 sleep(2); 383 if ((i = ioctl(a->fd, BLKRRPART)) != 0) { 384 saved_errno = errno; 385 } 386 } 387 388 // printf("Syncing disks.\n"); 389 sync(); 390 sleep(4); /* for sync() */ 391 392 if (i < 0) { 393 error(saved_errno, "Re-read of partition table failed"); 394 printf("Reboot your system to ensure the " 395 "partition table is updated.\n"); 396 } 397#endif 398 rtn_value = 1; 399 } 400 return rtn_value; 401} 402 403 404#if !defined(__linux__) && !defined(__unix__) 405#pragma mark - 406#endif 407 408 409FILE_MEDIA_ITERATOR 410new_file_iterator(void) 411{ 412 return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator)); 413} 414 415 416MEDIA_ITERATOR 417create_file_iterator(void) 418{ 419 FILE_MEDIA_ITERATOR a; 420 421 if (file_inited == 0) { 422 file_init(); 423 } 424 425 a = new_file_iterator(); 426 if (a != 0) { 427 a->m.kind = file_info.kind; 428 a->m.state = kInit; 429 a->m.do_reset = reset_file_iterator; 430 a->m.do_step = step_file_iterator; 431 a->m.do_delete = delete_file_iterator; 432 a->style = 0; 433 a->index = 0; 434 } 435 436 return (MEDIA_ITERATOR) a; 437} 438 439 440void 441reset_file_iterator(MEDIA_ITERATOR m) 442{ 443 FILE_MEDIA_ITERATOR a; 444 445 a = (FILE_MEDIA_ITERATOR) m; 446 if (a == 0) { 447 /* no media */ 448 } else if (a->m.kind != file_info.kind) { 449 /* wrong kind - XXX need to error here - this is an internal problem */ 450 } else if (a->m.state != kInit) { 451 a->m.state = kReset; 452 } 453} 454 455 456char * 457step_file_iterator(MEDIA_ITERATOR m) 458{ 459 FILE_MEDIA_ITERATOR a; 460 char *result; 461 struct stat info; 462 int fd; 463 int bump; 464 int value; 465 466 a = (FILE_MEDIA_ITERATOR) m; 467 if (a == 0) { 468 /* no media */ 469 } else if (a->m.kind != file_info.kind) { 470 /* wrong kind - XXX need to error here - this is an internal problem */ 471 } else { 472 switch (a->m.state) { 473 case kInit: 474 a->m.state = kReset; 475 /* fall through to reset */ 476 case kReset: 477 a->style = 0 /* first style */; 478 a->index = 0 /* first index */; 479 a->m.state = kIterating; 480 /* fall through to iterate */ 481 case kIterating: 482 while (1) { 483 if (a->style > kMaxStyle) { 484 break; 485 } 486#ifndef notdef 487 /* if old version of mklinux then skip CD drive */ 488 if (a->style == kSCSI_Disks && a->index == 3) { 489 a->index += 1; 490 } 491#endif 492 /* generate result */ 493 result = (char *) malloc(20); 494 if (result != NULL) { 495 /* 496 * for DR3 we should actually iterate through: 497 * 498 * /dev/sd[a...] # first missing is end of list 499 * /dev/hd[a...] # may be holes in sequence 500 * /dev/scd[0...] # first missing is end of list 501 * 502 * and stop in each group when either a stat of 503 * the name fails or if an open fails for 504 * particular reasons. 505 */ 506 bump = 0; 507 value = (int) a->index; 508 switch (a->style) { 509 case kSCSI_Disks: 510 if (value < 26) { 511 snprintf(result, 20, "/dev/sd%c", 'a'+value); 512 } else if (value < 676) { 513 snprintf(result, 20, "/dev/sd%c%c", 514 'a' + value / 26, 515 'a' + value % 26); 516 } else { 517 bump = -1; 518 } 519 break; 520 case kATA_Devices: 521 if (value < 26) { 522 snprintf(result, 20, "/dev/hd%c", 'a'+value); 523 } else { 524 bump = -1; 525 } 526 break; 527 case kSCSI_CDs: 528 if (value < 10) { 529 snprintf(result, 20, "/dev/scd%c", '0'+value); 530 } else { 531 bump = -1; 532 } 533 break; 534 } 535 if (bump != 0) { 536 // already set don't even check 537 } else if (stat(result, &info) < 0) { 538 bump = 1; 539 } else if ((fd = open(result, O_RDONLY)) >= 0) { 540 close(fd); 541#if defined(__linux__) || defined(__unix__) 542 } else if (errno == ENXIO || errno == ENODEV) { 543 if (a->style == kATA_Devices) { 544 bump = -1; 545 } else { 546 bump = 1; 547 } 548#endif 549 } 550 if (bump) { 551 if (bump > 0) { 552 a->style += 1; /* next style */ 553 a->index = 0; /* first index again */ 554 } else { 555 a->index += 1; /* next index */ 556 } 557 free(result); 558 continue; 559 } 560 } 561 562 a->index += 1; /* next index */ 563 return result; 564 } 565 a->m.state = kEnd; 566 /* fall through to end */ 567 case kEnd: 568 default: 569 break; 570 } 571 } 572 return 0 /* no entry */; 573} 574 575 576void 577delete_file_iterator(MEDIA_ITERATOR m) 578{ 579 return; 580} 581