Home | History | Annotate | Line # | Download | only in dist
file_media.c revision 1.2
      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
     62 extern __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  */
     77 typedef struct file_media *FILE_MEDIA;
     78 
     79 struct file_media {
     80     struct media	m;
     81     int			fd;
     82     int			regular_file;
     83 };
     84 
     85 struct file_media_globals {
     86     long		exists;
     87     long		kind;
     88 };
     89 
     90 typedef struct file_media_iterator *FILE_MEDIA_ITERATOR;
     91 
     92 struct file_media_iterator {
     93     struct media_iterator   m;
     94     long		    style;
     95     long		    index;
     96 };
     97 
     98 
     99 /*
    100  * Global Constants
    101  */
    102 int potential_block_sizes[] = {
    103     1, 512, 1024, 2048, 4096, 8192, 16834,
    104     0
    105 };
    106 
    107 enum {
    108     kSCSI_Disks = 0,
    109     kATA_Devices = 1,
    110     kSCSI_CDs = 2,
    111     kMaxStyle = 2
    112 };
    113 
    114 
    115 /*
    116  * Global Variables
    117  */
    118 static long file_inited = 0;
    119 static struct file_media_globals file_info;
    120 
    121 /*
    122  * Forward declarations
    123  */
    124 int compute_block_size(int fd);
    125 void file_init(void);
    126 FILE_MEDIA new_file_media(void);
    127 long read_file_media(MEDIA m, long long offset, uint32_t count, void *address);
    128 long write_file_media(MEDIA m, long long offset, uint32_t count, void *address);
    129 long close_file_media(MEDIA m);
    130 long os_reload_file_media(MEDIA m);
    131 FILE_MEDIA_ITERATOR new_file_iterator(void);
    132 void reset_file_iterator(MEDIA_ITERATOR m);
    133 char *step_file_iterator(MEDIA_ITERATOR m);
    134 void delete_file_iterator(MEDIA_ITERATOR m);
    135 
    136 
    137 /*
    138  * Routines
    139  */
    140 void
    141 file_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 
    152 FILE_MEDIA
    153 new_file_media(void)
    154 {
    155     return (FILE_MEDIA) new_media(sizeof(struct file_media));
    156 }
    157 
    158 
    159 int
    160 compute_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 
    201 MEDIA
    202 open_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 
    251 long
    252 read_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 
    296 long
    297 write_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 
    332 long
    333 close_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 
    350 long
    351 os_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 (at) 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 
    407 FILE_MEDIA_ITERATOR
    408 new_file_iterator(void)
    409 {
    410     return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator));
    411 }
    412 
    413 
    414 MEDIA_ITERATOR
    415 create_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 
    438 void
    439 reset_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 
    454 char *
    455 step_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 
    574 void
    575 delete_file_iterator(MEDIA_ITERATOR m)
    576 {
    577     return;
    578 }
    579