#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#ifdef __linux__
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <sys/stat.h>
#else
#ifdef __unix__
#include <sys/ioctl.h>
#include <sys/stat.h>
#endif
#endif
#include "file_media.h"
#include "errors.h"
#ifdef __linux__
#define LOFF_MAX 9223372036854775807LL
extern __loff_t llseek __P ((int __fd, __loff_t __offset, int __whence));
#else
#ifdef __unix__
#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
#endif
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;
};
int potential_block_sizes[] = {
512, 1024, 2048,
0
};
enum {
kSCSI_Disks = 0,
kATA_Devices = 1,
kSCSI_CDs = 2,
kMaxStyle = 2
};
static long file_inited = 0;
static struct file_media_globals file_info;
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, unsigned long count, void *address);
long write_file_media(MEDIA m, long long offset, unsigned long 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);
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);
#if !defined(__linux__) && !defined(__unix__)
if (off <= 0) {
off = 1;
}
#endif
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, unsigned long count, void *address)
{
FILE_MEDIA a;
long rtn_value;
loff_t off;
int t;
a = (FILE_MEDIA) m;
rtn_value = 0;
if (a == 0) {
fprintf(stderr,"no media\n");
} else if (a->m.kind != file_info.kind) {
fprintf(stderr,"wrong kind\n");
} else if (count <= 0 || count % a->m.grain != 0) {
fprintf(stderr,"bad size\n");
} else if (offset < 0 || offset % a->m.grain != 0) {
fprintf(stderr,"bad offset\n");
} else if (offset + count > a->m.size_in_bytes && a->m.size_in_bytes != (long long) 0) {
fprintf(stderr,"offset+count too large\n");
} else if (offset + count > (long long) LOFF_MAX) {
fprintf(stderr,"offset+count too large 2\n");
} else {
off = offset;
if ((off = llseek(a->fd, off, 0)) >= 0) {
if ((t = read(a->fd, address, count)) == count) {
rtn_value = 1;
} else {
}
} else {
}
}
return rtn_value;
}
long
write_file_media(MEDIA m, long long offset, unsigned long count, void *address)
{
FILE_MEDIA a;
long rtn_value;
loff_t off;
int t;
a = (FILE_MEDIA) m;
rtn_value = 0;
if (a == 0) {
} else if (a->m.kind != file_info.kind) {
} else if (count <= 0 || count % a->m.grain != 0) {
} else if (offset < 0 || offset % a->m.grain != 0) {
} else if (offset + count > (long long) LOFF_MAX) {
} else {
off = offset;
if ((off = llseek(a->fd, off, 0)) >= 0) {
if ((t = write(a->fd, address, count)) == count) {
if (off + 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) {
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) {
} else if (a->m.kind != file_info.kind) {
} else if (a->regular_file) {
rtn_value = 1;
} else {
#ifdef __linux__
sync();
sleep(2);
if ((i = ioctl(a->fd, BLKRRPART)) != 0) {
saved_errno = errno;
} else {
sync();
sleep(2);
if ((i = ioctl(a->fd, BLKRRPART)) != 0) {
saved_errno = errno;
}
}
sync();
sleep(4);
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) {
} else if (a->m.kind != file_info.kind) {
} 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) {
} else if (a->m.kind != file_info.kind) {
} else {
switch (a->m.state) {
case kInit:
a->m.state = kReset;
case kReset:
a->style = 0 ;
a->index = 0 ;
a->m.state = kIterating;
case kIterating:
while (1) {
if (a->style > kMaxStyle) {
break;
}
#ifndef notdef
if (a->style == kSCSI_Disks && a->index == 3) {
a->index += 1;
}
#endif
result = (char *) malloc(20);
if (result != NULL) {
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) {
} 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;
a->index = 0;
} else {
a->index += 1;
}
free(result);
continue;
}
}
a->index += 1;
return result;
}
a->m.state = kEnd;
case kEnd:
default:
break;
}
}
return 0 ;
}
void
delete_file_iterator(MEDIA_ITERATOR m)
{
return;
}