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