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