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