2014-11-19 02:23:05 +00:00
|
|
|
/* Copyright 1995-2007,2009 Alain Knaff.
|
|
|
|
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
|
|
|
* This file is part of mtools.
|
|
|
|
*
|
|
|
|
* Mtools is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Mtools is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Io to a plain file or device
|
|
|
|
*
|
|
|
|
* written by:
|
|
|
|
*
|
|
|
|
* Alain L. Knaff
|
|
|
|
* alain@knaff.lu
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "stream.h"
|
|
|
|
#include "mtools.h"
|
|
|
|
#include "msdos.h"
|
|
|
|
#include "plain_io.h"
|
|
|
|
#include "partition.h"
|
|
|
|
#include "llong.h"
|
|
|
|
#include "force_io.h"
|
|
|
|
#include "devices.h"
|
|
|
|
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2021-06-05 20:39:01 +03:00
|
|
|
#include <fcntl.h>
|
2014-11-19 02:23:05 +00:00
|
|
|
|
|
|
|
struct SimpleFile_t
|
|
|
|
{
|
|
|
|
struct Class_t *Class;
|
|
|
|
int refs;
|
|
|
|
struct Stream_t *Next;
|
|
|
|
struct Stream_t *Buffer;
|
|
|
|
struct stat64 statbuf;
|
|
|
|
int fd;
|
|
|
|
off_t offset;
|
|
|
|
off_t lastwhere;
|
|
|
|
int seekable;
|
|
|
|
int privileged;
|
|
|
|
int scsi_sector_size;
|
|
|
|
void *extra_data; /* extra system dependant information for scsi */
|
|
|
|
int swap; /* do the word swapping */
|
|
|
|
};
|
|
|
|
|
|
|
|
static int lock_dev(int fd, int mode)
|
|
|
|
{
|
|
|
|
if (flock(fd, (mode ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
|
|
|
|
return errno == EINVAL || errno == EOPNOTSUPP ? 0 : 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef int (*iofn) (int, char *, int);
|
|
|
|
|
|
|
|
static void swap_buffer(char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i<len; i+=2)
|
|
|
|
{
|
|
|
|
char temp = buf[i];
|
|
|
|
buf[i] = buf[i+1];
|
|
|
|
buf[i+1] = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_io(struct Stream_t *Stream, char *buf, off_t where, int len, iofn io)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
|
|
|
|
where += This->offset;
|
|
|
|
|
|
|
|
if (This->seekable && where != This->lastwhere )
|
|
|
|
{
|
|
|
|
if(mt_lseek( This->fd, where, SEEK_SET) < 0 )
|
|
|
|
{
|
|
|
|
perror("seek");
|
|
|
|
This->lastwhere = (off_t) -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = io(This->fd, buf, len);
|
|
|
|
|
|
|
|
if (ret == -1 )
|
|
|
|
{
|
|
|
|
perror("plain_io");
|
|
|
|
This->lastwhere = (off_t) -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->lastwhere = where + ret;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_read(struct Stream_t *Stream, char *buf, off_t where, size_t len)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
|
|
|
|
int result = file_io(Stream, buf, where, len, (iofn) read);
|
|
|
|
|
|
|
|
if (This->swap)
|
|
|
|
swap_buffer(buf, len);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_write(struct Stream_t *Stream, char *buf, off_t where, size_t len)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
|
|
|
|
if (!This->swap)
|
|
|
|
return file_io(Stream, buf, where, len, (iofn) write);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char* swapping = malloc(len);
|
|
|
|
|
|
|
|
memcpy(swapping, buf, len);
|
|
|
|
swap_buffer(swapping, len);
|
|
|
|
|
|
|
|
int result = file_io(Stream, swapping, where, len, (iofn) write);
|
|
|
|
|
|
|
|
free(swapping);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_flush(struct Stream_t UNUSED(*Stream))
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_free(struct Stream_t *Stream)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
return (This->fd > 2) ? close(This->fd) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int file_geom(struct Stream_t *Stream, struct device *dev, struct device *orig_dev, int media, union bootsector *boot)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
|
|
|
|
dev->ssize = 2; /* allow for init_geom to change it */
|
|
|
|
dev->use_2m = 0x80; /* disable 2m mode to begin */
|
|
|
|
|
|
|
|
if(media == 0xf0 || media >= 0x100)
|
|
|
|
{
|
|
|
|
dev->heads = WORD(nheads);
|
|
|
|
dev->sectors = WORD(nsect);
|
|
|
|
|
|
|
|
size_t tot_sectors = DWORD(bigsect);
|
|
|
|
|
|
|
|
if (WORD(psect))
|
|
|
|
tot_sectors = WORD(psect);
|
|
|
|
|
|
|
|
int sect_per_track = dev->heads * dev->sectors;
|
|
|
|
|
|
|
|
if(sect_per_track == 0)
|
|
|
|
{
|
|
|
|
/* add some fake values if sect_per_track is
|
|
|
|
* zero. Indeed, some atari disks lack the
|
|
|
|
* geometry values (i.e. have zeroes in their
|
|
|
|
* place). In order to avoid division by zero
|
|
|
|
* errors later on, plug 1 everywhere
|
|
|
|
*/
|
|
|
|
dev->heads = 1;
|
|
|
|
dev->sectors = 1;
|
|
|
|
sect_per_track = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
tot_sectors += sect_per_track - 1; /* round size up */
|
|
|
|
dev->tracks = tot_sectors / sect_per_track;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr,"Unknown media type\n");
|
|
|
|
return -1; // TODO: make sure this is interpreted as invalid return code
|
|
|
|
}
|
|
|
|
|
|
|
|
int sectors = dev->sectors;
|
|
|
|
dev->sectors = dev->sectors * WORD(secsiz) / 512;
|
|
|
|
|
|
|
|
int ret = init_geom(This->fd,dev, orig_dev, &This->statbuf);
|
|
|
|
dev->sectors = sectors;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int file_data(struct Stream_t *Stream, time_t *date, size_t *size,
|
|
|
|
int *type, int *address)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
|
|
|
|
if(date)
|
|
|
|
*date = This->statbuf.st_mtime;
|
|
|
|
|
|
|
|
if(size)
|
|
|
|
*size = This->statbuf.st_size;
|
|
|
|
|
|
|
|
if(type)
|
|
|
|
*type = S_ISDIR(This->statbuf.st_mode);
|
|
|
|
|
|
|
|
if(address)
|
|
|
|
*address = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct Class_t SimpleFileClass =
|
|
|
|
{
|
|
|
|
file_read,
|
|
|
|
file_write,
|
|
|
|
file_flush,
|
|
|
|
file_free,
|
|
|
|
file_geom,
|
|
|
|
file_data,
|
|
|
|
0, /* pre_allocate */
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev,
|
|
|
|
const char *name, int mode, char *errmsg,
|
|
|
|
int mode2, int locked, size_t *maxSize)
|
|
|
|
{
|
|
|
|
struct SimpleFile_t *This;
|
|
|
|
|
|
|
|
This = New(struct SimpleFile_t);
|
|
|
|
|
|
|
|
if (!This)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s %d: Allocation memory for simple file failed.\n", __FILE__, __LINE__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->scsi_sector_size = 512;
|
|
|
|
This->seekable = 1;
|
|
|
|
|
|
|
|
This->Class = &SimpleFileClass;
|
|
|
|
if (!name || strcmp(name,"-") == 0)
|
|
|
|
{
|
|
|
|
This->fd = mode == O_RDONLY ? 0 : 1;
|
|
|
|
This->seekable = 0;
|
|
|
|
This->refs = 1;
|
|
|
|
This->Next = 0;
|
|
|
|
This->Buffer = 0;
|
|
|
|
|
|
|
|
if (fstat64(This->fd, &This->statbuf) < 0)
|
|
|
|
{
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
snprintf(errmsg,199,"Can't stat -: %s", strerror(errno));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (struct Stream_t *) This;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dev)
|
|
|
|
mode |= dev->mode;
|
|
|
|
|
|
|
|
This->fd = open(name, mode | O_LARGEFILE, 0666);
|
|
|
|
|
|
|
|
if (This->fd < 0)
|
|
|
|
{
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
snprintf(errmsg, 199, "Can't open %s: %s", name, strerror(errno));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fstat64(This->fd, &This->statbuf) < 0)
|
|
|
|
{
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
snprintf(errmsg, 199,"Can't stat %s: %s", name, strerror(errno));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lock the device on writes */
|
|
|
|
if (locked && lock_dev(This->fd, mode == O_RDWR))
|
|
|
|
{
|
|
|
|
if(errmsg)
|
|
|
|
snprintf(errmsg, 199, "plain floppy: device \"%s\" busy (%s):", dev ? dev->name : "unknown", strerror(errno));
|
|
|
|
close(This->fd);
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set default parameters, if needed */
|
|
|
|
if (dev)
|
|
|
|
{
|
|
|
|
if (dev->tracks && init_geom(This->fd, dev, orig_dev, &This->statbuf))
|
|
|
|
{
|
|
|
|
close(This->fd);
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
sprintf(errmsg,"init: set default params");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->offset = (off_t) dev->offset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
This->offset = 0;
|
|
|
|
|
|
|
|
This->refs = 1;
|
|
|
|
This->Next = 0;
|
|
|
|
This->Buffer = 0;
|
|
|
|
|
|
|
|
if(maxSize)
|
|
|
|
{
|
|
|
|
*maxSize = max_off_t_seek;
|
|
|
|
|
|
|
|
if(This->offset > 0 && (size_t) This->offset > *maxSize)
|
|
|
|
{
|
|
|
|
close(This->fd);
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
sprintf(errmsg,"init: Big disks not supported");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*maxSize -= This->offset;
|
|
|
|
}
|
|
|
|
/* partitioned drive */
|
|
|
|
|
|
|
|
This->swap = 0;
|
|
|
|
|
|
|
|
if(!(mode2 & NO_OFFSET) && dev && dev->partition > 4)
|
|
|
|
fprintf(stderr, "Invalid partition %d (must be between 0 and 4), ignoring it\n", dev->partition);
|
|
|
|
|
|
|
|
while(!(mode2 & NO_OFFSET) && dev && dev->partition && dev->partition <= 4)
|
|
|
|
{
|
|
|
|
unsigned char buf[2048];
|
|
|
|
struct partition *partTable = (struct partition *)(buf+ 0x1ae);
|
|
|
|
size_t partOff;
|
|
|
|
|
|
|
|
/* read the first sector, or part of it */
|
|
|
|
if (force_read((struct Stream_t *)This, (char*) buf, 0, 512) != 512)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( _WORD(buf + 510) != 0xaa55)
|
|
|
|
break;
|
|
|
|
|
|
|
|
partOff = BEGIN(partTable[dev->partition]);
|
|
|
|
if (maxSize)
|
|
|
|
{
|
|
|
|
if (partOff > *maxSize >> 9)
|
|
|
|
{
|
|
|
|
close(This->fd);
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
if(errmsg)
|
|
|
|
sprintf(errmsg,"init: Big disks not supported");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*maxSize -= (off_t) partOff << 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->offset += (off_t) partOff << 9;
|
|
|
|
if(!partTable[dev->partition].sys_ind)
|
|
|
|
{
|
|
|
|
if(errmsg)
|
|
|
|
sprintf(errmsg, "init: non-existent partition");
|
|
|
|
|
|
|
|
close(This->fd);
|
|
|
|
free(This);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!dev->tracks)
|
|
|
|
{
|
|
|
|
dev->heads = head(partTable[dev->partition].end) + 1;
|
|
|
|
dev->sectors = sector(partTable[dev->partition].end);
|
|
|
|
dev->tracks = cyl(partTable[dev->partition].end) - cyl(partTable[dev->partition].start) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->hidden = dev->sectors*head(partTable[dev->partition].start) + sector(partTable[dev->partition].start) - 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
This->lastwhere = -This->offset;
|
|
|
|
/* provoke a seek on those devices that don't start on a partition
|
|
|
|
* boundary */
|
|
|
|
|
|
|
|
return (struct Stream_t *) This;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SimpleFileClose(struct Stream_t* Stream)
|
|
|
|
{
|
|
|
|
DeclareThis(struct SimpleFile_t);
|
|
|
|
return close(This->fd);
|
|
|
|
}
|