/* * file_media.c - * * Written by Eryk Vershen */ /* * Copyright 1997,1998 by Apple Computer, Inc. * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies and * that both the copyright notice and this permission notice appear in * supporting documentation. * * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // for printf() #include // for malloc() & free() #include // for lseek(), read(), write(), close() #include // for open() #include // for LONG_MAX #include // for errno #include #ifdef __linux__ #include #include #include #include #else #ifdef __unix__ #include #include #endif #endif #include "file_media.h" #include "errors.h" /* * Defines */ #ifdef __linux__ #define LOFF_MAX 9223372036854775807LL extern __loff_t llseek __P ((int __fd, __loff_t __offset, int __whence)); #elif defined(__NetBSD__) || defined(__APPLE__) #define loff_t off_t #define llseek lseek #define LOFF_MAX LLONG_MAX #else #define loff_t long #define llseek lseek #define LOFF_MAX LONG_MAX #endif /* * Types */ typedef struct file_media *FILE_MEDIA; struct file_media { struct media m; int fd; int regular_file; }; struct file_media_globals { long exists; long kind; }; typedef struct file_media_iterator *FILE_MEDIA_ITERATOR; struct file_media_iterator { struct media_iterator m; long style; long index; }; /* * Global Constants */ int potential_block_sizes[] = { 1, 512, 1024, 2048, 4096, 8192, 16834, 0 }; enum { kSCSI_Disks = 0, kATA_Devices = 1, kSCSI_CDs = 2, kMaxStyle = 2 }; /* * Global Variables */ static long file_inited = 0; static struct file_media_globals file_info; /* * Forward declarations */ int compute_block_size(int fd); void file_init(void); FILE_MEDIA new_file_media(void); long read_file_media(MEDIA m, long long offset, uint32_t count, void *address); long write_file_media(MEDIA m, long long offset, uint32_t count, void *address); long close_file_media(MEDIA m); long os_reload_file_media(MEDIA m); FILE_MEDIA_ITERATOR new_file_iterator(void); void reset_file_iterator(MEDIA_ITERATOR m); char *step_file_iterator(MEDIA_ITERATOR m); void delete_file_iterator(MEDIA_ITERATOR m); /* * Routines */ void file_init(void) { if (file_inited != 0) { return; } file_inited = 1; file_info.kind = allocate_media_kind(); } FILE_MEDIA new_file_media(void) { return (FILE_MEDIA) new_media(sizeof(struct file_media)); } int compute_block_size(int fd) { int size; int max_size; loff_t x; long t; int i; char *buffer; max_size = 0; for (i = 0; ; i++) { size = potential_block_sizes[i]; if (size == 0) { break; } if (max_size < size) { max_size = size; } } buffer = malloc(max_size); if (buffer != 0) { for (i = 0; ; i++) { size = potential_block_sizes[i]; if (size == 0) { break; } if ((x = llseek(fd, (loff_t)0, 0)) < 0) { error(errno, "Can't seek on file"); break; } if ((t = read(fd, buffer, size)) == size) { free(buffer); return size; } } } return 0; } MEDIA open_file_as_media(char *file, int oflag) { FILE_MEDIA a; int fd; loff_t off; #if defined(__linux__) || defined(__unix__) struct stat info; #endif if (file_inited == 0) { file_init(); } a = 0; fd = open(file, oflag); if (fd >= 0) { a = new_file_media(); if (a != 0) { a->m.kind = file_info.kind; a->m.grain = compute_block_size(fd); off = llseek(fd, (loff_t)0, 2); /* seek to end of media */ #if !defined(__linux__) && !defined(__unix__) if (off <= 0) { off = 1; /* XXX not right? */ } #endif //printf("file size = %Ld\n", off); a->m.size_in_bytes = (long long) off; a->m.do_read = read_file_media; a->m.do_write = write_file_media; a->m.do_close = close_file_media; a->m.do_os_reload = os_reload_file_media; a->fd = fd; a->regular_file = 0; #if defined(__linux__) || defined(__unix__) if (fstat(fd, &info) < 0) { error(errno, "can't stat file '%s'", file); } else { a->regular_file = S_ISREG(info.st_mode); } #endif } else { close(fd); } } return (MEDIA) a; } long read_file_media(MEDIA m, long long offset, uint32_t count, void *address) { FILE_MEDIA a; long rtn_value; loff_t off; int t; a = (FILE_MEDIA) m; rtn_value = 0; if (a == 0) { /* no media */ fprintf(stderr,"no media\n"); } else if (a->m.kind != file_info.kind) { /* wrong kind - XXX need to error here - this is an internal problem */ fprintf(stderr,"wrong kind\n"); } else if (count <= 0 || count % a->m.grain != 0) { /* can't handle size */ fprintf(stderr,"bad size\n"); } else if (offset < 0 || offset % a->m.grain != 0) { /* can't handle offset */ fprintf(stderr,"bad offset\n"); } else if (offset + (long long) count > a->m.size_in_bytes && a->m.size_in_bytes != (long long) 0) { /* check for offset (and offset+count) too large */ fprintf(stderr,"offset+count too large\n"); } else if (offset + count > (long long) LOFF_MAX) { /* check for offset (and offset+count) too large */ fprintf(stderr,"offset+count too large 2\n"); } else { /* do the read */ off = offset; if ((off = llseek(a->fd, off, 0)) >= 0) { if ((t = read(a->fd, address, count)) == (ssize_t)count) { rtn_value = 1; } else { fprintf(stderr,"read failed\n"); } } else { fprintf(stderr,"lseek failed\n"); } } return rtn_value; } long write_file_media(MEDIA m, long long offset, uint32_t count, void *address) { FILE_MEDIA a; long rtn_value; loff_t off; int t; a = (FILE_MEDIA) m; rtn_value = 0; if (a == 0) { /* no media */ } else if (a->m.kind != file_info.kind) { /* wrong kind - XXX need to error here - this is an internal problem */ } else if (count <= 0 || count % a->m.grain != 0) { /* can't handle size */ } else if (offset < 0 || offset % a->m.grain != 0) { /* can't handle offset */ } else if (offset + count > (long long) LOFF_MAX) { /* check for offset (and offset+count) too large */ } else { /* do the write */ off = offset; if ((off = llseek(a->fd, off, 0)) >= 0) { if ((t = write(a->fd, address, count)) == (ssize_t)count) { if (off + (long long) count > a->m.size_in_bytes) { a->m.size_in_bytes = off + count; } rtn_value = 1; } } } return rtn_value; } long close_file_media(MEDIA m) { FILE_MEDIA a; a = (FILE_MEDIA) m; if (a == 0) { return 0; } else if (a->m.kind != file_info.kind) { /* XXX need to error here - this is an internal problem */ return 0; } close(a->fd); return 1; } long os_reload_file_media(MEDIA m) { FILE_MEDIA a; long rtn_value; #if defined(__linux__) int i; int saved_errno; #endif a = (FILE_MEDIA) m; rtn_value = 0; if (a == 0) { /* no media */ } else if (a->m.kind != file_info.kind) { /* wrong kind - XXX need to error here - this is an internal problem */ } else if (a->regular_file) { /* okay - nothing to do */ rtn_value = 1; } else { #ifdef __linux__ sync(); sleep(2); if ((i = ioctl(a->fd, BLKRRPART)) != 0) { saved_errno = errno; } else { // some kernel versions (1.2.x) seem to have trouble // rereading the partition table, but if asked to do it // twice, the second time works. - biro@yggdrasil.com */ sync(); sleep(2); if ((i = ioctl(a->fd, BLKRRPART)) != 0) { saved_errno = errno; } } // printf("Syncing disks.\n"); sync(); sleep(4); /* for sync() */ if (i < 0) { error(saved_errno, "Re-read of partition table failed"); printf("Reboot your system to ensure the " "partition table is updated.\n"); } #endif rtn_value = 1; } return rtn_value; } #if !defined(__linux__) && !defined(__unix__) #pragma mark - #endif FILE_MEDIA_ITERATOR new_file_iterator(void) { return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator)); } MEDIA_ITERATOR create_file_iterator(void) { FILE_MEDIA_ITERATOR a; if (file_inited == 0) { file_init(); } a = new_file_iterator(); if (a != 0) { a->m.kind = file_info.kind; a->m.state = kInit; a->m.do_reset = reset_file_iterator; a->m.do_step = step_file_iterator; a->m.do_delete = delete_file_iterator; a->style = 0; a->index = 0; } return (MEDIA_ITERATOR) a; } void reset_file_iterator(MEDIA_ITERATOR m) { FILE_MEDIA_ITERATOR a; a = (FILE_MEDIA_ITERATOR) m; if (a == 0) { /* no media */ } else if (a->m.kind != file_info.kind) { /* wrong kind - XXX need to error here - this is an internal problem */ } else if (a->m.state != kInit) { a->m.state = kReset; } } char * step_file_iterator(MEDIA_ITERATOR m) { FILE_MEDIA_ITERATOR a; char *result; struct stat info; int fd; int bump; int value; a = (FILE_MEDIA_ITERATOR) m; if (a == 0) { /* no media */ } else if (a->m.kind != file_info.kind) { /* wrong kind - XXX need to error here - this is an internal problem */ } else { switch (a->m.state) { case kInit: a->m.state = kReset; /* fall through to reset */ case kReset: a->style = 0 /* first style */; a->index = 0 /* first index */; a->m.state = kIterating; /* fall through to iterate */ case kIterating: while (1) { if (a->style > kMaxStyle) { break; } #ifndef notdef /* if old version of mklinux then skip CD drive */ if (a->style == kSCSI_Disks && a->index == 3) { a->index += 1; } #endif /* generate result */ result = (char *) malloc(20); if (result != NULL) { /* * for DR3 we should actually iterate through: * * /dev/sd[a...] # first missing is end of list * /dev/hd[a...] # may be holes in sequence * /dev/scd[0...] # first missing is end of list * * and stop in each group when either a stat of * the name fails or if an open fails for * particular reasons. */ bump = 0; value = (int) a->index; switch (a->style) { case kSCSI_Disks: if (value < 26) { snprintf(result, 20, "/dev/sd%c", 'a'+value); } else if (value < 676) { snprintf(result, 20, "/dev/sd%c%c", 'a' + value / 26, 'a' + value % 26); } else { bump = -1; } break; case kATA_Devices: if (value < 26) { snprintf(result, 20, "/dev/hd%c", 'a'+value); } else { bump = -1; } break; case kSCSI_CDs: if (value < 10) { snprintf(result, 20, "/dev/scd%c", '0'+value); } else { bump = -1; } break; } if (bump != 0) { // already set don't even check } else if (stat(result, &info) < 0) { bump = 1; } else if ((fd = open(result, O_RDONLY)) >= 0) { close(fd); #if defined(__linux__) || defined(__unix__) } else if (errno == ENXIO || errno == ENODEV) { if (a->style == kATA_Devices) { bump = -1; } else { bump = 1; } #endif } if (bump) { if (bump > 0) { a->style += 1; /* next style */ a->index = 0; /* first index again */ } else { a->index += 1; /* next index */ } free(result); continue; } } a->index += 1; /* next index */ return result; } a->m.state = kEnd; /* fall through to end */ case kEnd: default: break; } } return 0 /* no entry */; } void delete_file_iterator(MEDIA_ITERATOR m) { return; }