/* io.c - Virtual disk input/output
Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
The complete text of the GNU General Public License
can be found in /usr/share/common-licenses/GPL-3 file.
*/
/*
* Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
* Fixed nasty bug that caused every file with a name like
* xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
*/
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#define _LARGEFILE64_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fd.h>
#include "fsck.fat.h"
#include "common.h"
#include "io.h"
typedef struct _change {
void *data;
loff_t pos;
int size;
struct _change *next;
} CHANGE;
static CHANGE *changes, *last;
static int fd, did_change = 0;
unsigned device_no;
#ifdef __DJGPP__
#include "volume.h" /* DOS lowlevel disk access functions */
loff_t llseek(int fd, loff_t offset, int whence)
{
if ((whence != SEEK_SET) || (fd == 4711))
return -1; /* only those supported */
return VolumeSeek(offset);
}
#define open OpenVolume
#define close CloseVolume
#define read(a,b,c) ReadVolume(b,c)
#define write(a,b,c) WriteVolume(b,c)
#else
loff_t llseek(int fd, loff_t offset, int whence)
{
return (loff_t) lseek64(fd, (off64_t) offset, whence);
}
#endif
void fs_open(char *path, int rw)
{
struct stat stbuf;
if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) {
perror("open");
exit(6);
}
changes = last = NULL;
did_change = 0;
#ifndef _DJGPP_
if (fstat(fd, &stbuf) < 0)
pdie("fstat %s", path);
device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0;
#else
if (IsWorkingOnImageFile()) {
if (fstat(GetVolumeHandle(), &stbuf) < 0)
pdie("fstat image %s", path);
device_no = 0;
} else {
/* return 2 for floppy, 1 for ramdisk, 7 for loopback */
/* used by boot.c in Atari mode: floppy always FAT12, */
/* loopback / ramdisk only FAT12 if usual floppy size, */
/* harddisk always FAT16 on Atari... */
device_no = (GetVolumeHandle() < 2) ? 2 : 1;
/* telling "floppy" for A:/B:, "ramdisk" for the rest */
}
#endif
}
/**
* Read data from the partition, accounting for any pending updates that are
* queued for writing.
*
* @param[in] pos Byte offset, relative to the beginning of the partition,
* at which to read
* @param[in] size Number of bytes to read
* @param[out] data Where to put the data read
*/
void fs_read(loff_t pos, int size, void *data)
{
CHANGE *walk;
int got;
if (llseek(fd, pos, 0) != pos)
pdie("Seek to %lld", pos);
if ((got = read(fd, data, size)) < 0)
pdie("Read %d bytes at %lld", size, pos);
if (got != size)
die("Got %d bytes instead of %d at %lld", got, size, pos);
for (walk = changes; walk; walk = walk->next) {
if (walk->pos < pos + size && walk->pos + walk->size > pos) {
if (walk->pos < pos)
memcpy(data, (char *)walk->data + pos - walk->pos, min(size,
walk->
size -
pos +
walk->
pos));
else
memcpy((char *)data + walk->pos - pos, walk->data,
min(walk->size, size + pos - walk->pos));
}
}
}
int fs_test(loff_t pos, int size)
{
void *scratch;
int okay;
if (llseek(fd, pos, 0) != pos)
pdie("Seek to %lld", pos);
scratch = alloc(size);
okay = read(fd, scratch, size) == size;
free(scratch);
return okay;
}
void fs_write(loff_t pos, int size, void *data)
{
CHANGE *new;
int did;
if (write_immed) {
did_change = 1;
if (llseek(fd, pos, 0) != pos)
pdie("Seek to %lld", pos);
if ((did = write(fd, data, size)) == size)
return;
if (did < 0)
pdie("Write %d bytes at %lld", size, pos);
die("Wrote %d bytes instead of %d at %lld", did, size, pos);
}
new = alloc(sizeof(CHANGE));
new->pos = pos;
memcpy(new->data = alloc(new->size = size), data, size);
new->next = NULL;
if (last)
last->next = new;
else
changes = new;
last = new;
}
static void fs_flush(void)
{
CHANGE *this;
int size;
while (changes) {
this = changes;
changes = changes->next;
if (llseek(fd, this->pos, 0) != this->pos)
fprintf(stderr,
"Seek to %lld failed: %s\n Did not write %d bytes.\n",
(long long)this->pos, strerror(errno), this->size);
else if ((size = write(fd, this->data, this->size)) < 0)
fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size,
(long long)this->pos, strerror(errno));
else if (size != this->size)
fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld."
"\n", size, this->size, (long long)this->pos);
free(this->data);
free(this);
}
}
int fs_close(int write)
{
CHANGE *next;
int changed;
changed = ! !changes;
if (write)
fs_flush();
else
while (changes) {
next = changes->next;
free(changes->data);
free(changes);
changes = next;
}
if (close(fd) < 0)
pdie("closing filesystem");
return changed || did_change;
}
int fs_changed(void)
{
return ! !changes || did_change;
}