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