aboutsummaryrefslogtreecommitdiff
path: root/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'file.c')
-rw-r--r--file.c301
1 files changed, 194 insertions, 107 deletions
diff --git a/file.c b/file.c
index 6ae7555..8165d4f 100644
--- a/file.c
+++ b/file.c
@@ -23,19 +23,25 @@
#include "file.h"
#include "htable.h"
#include "dirCache.h"
+#include "buffer.h"
typedef struct File_t {
- Class_t *Class;
- int refs;
- struct Fs_t *Fs; /* Filesystem that this fat file belongs to */
- Stream_t *Buffer;
+ struct Stream_t head;
- int (*map)(struct File_t *this, off_t where, size_t *len, int mode,
+ struct Stream_t *Buffer;
+
+ int (*map)(struct File_t *this, uint32_t where, uint32_t *len, int mode,
mt_off_t *res);
- size_t FileSize;
+ uint32_t FileSize;
+
+ /* How many bytes do we project to need for this file
+ (includes those already in FileSize) */
+ uint32_t preallocatedSize;
- size_t preallocatedSize;
- int preallocatedClusters;
+ /* How many clusters we have asked the lower layer to reserve
+ for us (only what we will need in the future, excluding already
+ allocated clusters in FileSize) */
+ uint32_t preallocatedClusters;
/* Absolute position of first cluster of file */
unsigned int FirstAbsCluNr;
@@ -51,6 +57,8 @@ typedef struct File_t {
unsigned int loopDetectRel;
unsigned int loopDetectAbs;
+
+ uint32_t where;
} File_t;
static Class_t FileClass;
@@ -63,9 +71,14 @@ static File_t *getUnbufferedFile(Stream_t *Stream)
return (File_t *) Stream;
}
+static inline Fs_t *_getFs(File_t *File)
+{
+ return (Fs_t *) File->head.Next;
+}
+
Fs_t *getFs(Stream_t *Stream)
{
- return getUnbufferedFile(Stream)->Fs;
+ return (Fs_t *)getUnbufferedFile(Stream)->head.Next;
}
struct dirCache_t **getDirCacheP(Stream_t *Stream)
@@ -78,14 +91,22 @@ direntry_t *getDirentry(Stream_t *Stream)
return &getUnbufferedFile(Stream)->direntry;
}
+/**
+ * Overflow-safe conversion of bytes to cluster
+ */
+static uint32_t filebytesToClusters(uint32_t bytes, uint32_t clus_size) {
+ uint32_t ret = bytes / clus_size;
+ if(bytes % clus_size)
+ ret++;
+ return ret;
+}
static int recalcPreallocSize(File_t *This)
{
- size_t currentClusters, neededClusters;
+ uint32_t currentClusters, neededClusters;
unsigned int clus_size;
- int neededPrealloc;
- Fs_t *Fs = This->Fs;
- int r;
+ uint32_t neededPrealloc;
+ Fs_t *Fs = _getFs(This);
#if 0
if(This->FileSize & 0xc0000000) {
@@ -97,15 +118,21 @@ static int recalcPreallocSize(File_t *This)
}
#endif
clus_size = Fs->cluster_size * Fs->sector_size;
-
- currentClusters = (This->FileSize + clus_size - 1) / clus_size;
- neededClusters = (This->preallocatedSize + clus_size - 1) / clus_size;
- neededPrealloc = neededClusters - currentClusters;
- if(neededPrealloc < 0)
+ currentClusters = filebytesToClusters(This->FileSize, clus_size);
+ neededClusters = filebytesToClusters(This->preallocatedSize, clus_size);
+ if(neededClusters < currentClusters)
neededPrealloc = 0;
- r = fsPreallocateClusters(Fs, neededPrealloc - This->preallocatedClusters);
- if(r)
- return r;
+ else
+ neededPrealloc = neededClusters - currentClusters;
+ if(neededPrealloc > This->preallocatedClusters) {
+ int r = fsPreallocateClusters(Fs, neededPrealloc-
+ This->preallocatedClusters);
+ if(r)
+ return r;
+ } else {
+ fsReleasePreallocateClusters(Fs, This->preallocatedClusters -
+ neededPrealloc);
+ }
This->preallocatedClusters = neededPrealloc;
return 0;
}
@@ -138,7 +165,7 @@ static unsigned int _countBlocks(Fs_t *This, unsigned int block)
unsigned int rel, oldabs, oldrel;
blocks = 0;
-
+
oldabs = oldrel = rel = 0;
while (block <= This->last_fat && block != 1 && block) {
@@ -162,7 +189,7 @@ unsigned int countBlocks(Stream_t *Dir, unsigned int block)
/* returns number of bytes in a directory. Represents a file size, and
* can hence be not bigger than 2^32
*/
-static size_t countBytes(Stream_t *Dir, unsigned int block)
+static uint32_t countBytes(Stream_t *Dir, unsigned int block)
{
Stream_t *Stream = GetFs(Dir);
DeclareThis(Fs_t);
@@ -174,7 +201,7 @@ static size_t countBytes(Stream_t *Dir, unsigned int block)
void printFat(Stream_t *Stream)
{
File_t *This = getUnbufferedFile(Stream);
- unsigned long n;
+ uint32_t n;
unsigned int rel;
unsigned long begin, end;
int first;
@@ -201,11 +228,11 @@ void printFat(Stream_t *Stream)
end++;
}
first = 0;
- n = fatDecode(This->Fs, n);
+ n = fatDecode(_getFs(This), n);
rel++;
if(loopDetect(This, rel, n) < 0)
n = 1;
- } while (n <= This->Fs->last_fat && n != 1);
+ } while (n <= _getFs(This)->last_fat && n != 1);
if(!first) {
if (begin != end)
printf("-%lu", end);
@@ -215,8 +242,8 @@ void printFat(Stream_t *Stream)
void printFatWithOffset(Stream_t *Stream, off_t offset) {
File_t *This = getUnbufferedFile(Stream);
- unsigned long n;
- int rel;
+ uint32_t n;
+ unsigned int rel;
off_t clusSize;
n = This->FirstAbsCluNr;
@@ -225,34 +252,34 @@ void printFatWithOffset(Stream_t *Stream, off_t offset) {
return;
}
- clusSize = This->Fs->cluster_size * This->Fs->sector_size;
+ clusSize = _getFs(This)->cluster_size * _getFs(This)->sector_size;
rel = 0;
while(offset >= clusSize) {
- n = fatDecode(This->Fs, n);
+ n = fatDecode(_getFs(This), n);
rel++;
if(loopDetect(This, rel, n) < 0)
return;
- if(n > This->Fs->last_fat)
+ if(n > _getFs(This)->last_fat)
return;
offset -= clusSize;
}
- printf("%lu", n);
+ printf("%lu", (unsigned long) n);
}
-static int normal_map(File_t *This, off_t where, size_t *len, int mode,
- mt_off_t *res)
+static int normal_map(File_t *This, uint32_t where, uint32_t *len, int mode,
+ mt_off_t *res)
{
unsigned int offset;
size_t end;
- int NrClu; /* number of clusters to read */
- unsigned int RelCluNr;
- unsigned int CurCluNr;
- unsigned int NewCluNr;
- unsigned int AbsCluNr;
- unsigned int clus_size;
- Fs_t *Fs = This->Fs;
+ uint32_t NrClu; /* number of clusters to read */
+ uint32_t RelCluNr;
+ uint32_t CurCluNr;
+ uint32_t NewCluNr;
+ uint32_t AbsCluNr;
+ uint32_t clus_size;
+ Fs_t *Fs = _getFs(This);
*res = 0;
clus_size = Fs->cluster_size * Fs->sector_size;
@@ -268,7 +295,7 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
*len = 0;
return 0;
}
- NewCluNr = get_next_free_cluster(This->Fs, 1);
+ NewCluNr = get_next_free_cluster(_getFs(This), 1);
if (NewCluNr == 1 ){
errno = ENOSPC;
return -2;
@@ -276,11 +303,11 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
hash_remove(filehash, (void *) This, This->hint);
This->FirstAbsCluNr = NewCluNr;
hash_add(filehash, (void *) This, &This->hint);
- fatAllocate(This->Fs, NewCluNr, Fs->end_fat);
+ fatAllocate(_getFs(This), NewCluNr, Fs->end_fat);
}
RelCluNr = where / clus_size;
-
+
if (RelCluNr >= This->PreviousRelCluNr){
CurCluNr = This->PreviousRelCluNr;
AbsCluNr = This->PreviousAbsCluNr;
@@ -298,22 +325,22 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
This->PreviousRelCluNr = RelCluNr;
This->PreviousAbsCluNr = AbsCluNr;
}
- NewCluNr = fatDecode(This->Fs, AbsCluNr);
+ NewCluNr = fatDecode(_getFs(This), AbsCluNr);
if (NewCluNr == 1 || NewCluNr == 0){
fprintf(stderr,"Fat problem while decoding %d %x\n",
AbsCluNr, NewCluNr);
exit(1);
}
- if(CurCluNr == RelCluNr + NrClu)
+ if(CurCluNr == RelCluNr + NrClu)
break;
if (NewCluNr > Fs->last_fat && mode == MT_WRITE){
/* if at end, and writing, extend it */
- NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr);
+ NewCluNr = get_next_free_cluster(_getFs(This), AbsCluNr);
if (NewCluNr == 1 ){ /* no more space */
errno = ENOSPC;
return -2;
}
- fatAppend(This->Fs, AbsCluNr, NewCluNr);
+ fatAppend(_getFs(This), AbsCluNr, NewCluNr);
}
if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat){
@@ -332,11 +359,14 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
}
maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
-
+
end = where + *len;
if(batchmode &&
mode == MT_WRITE &&
end >= This->FileSize) {
+ /* In batch mode, when writing at end of file, "pad"
+ * to nearest cluster boundary so that we don't have
+ * to read that data back from disk. */
*len += ROUND_UP(end, clus_size) - end;
}
@@ -346,76 +376,108 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
exit(1);
}
- *res = sectorsToBytes((Stream_t*)Fs,
- (This->PreviousAbsCluNr-2) * Fs->cluster_size +
- Fs->clus_start) + offset;
+ *res = sectorsToBytes(Fs,
+ (This->PreviousAbsCluNr-2) * Fs->cluster_size +
+ Fs->clus_start) + to_mt_off_t(offset);
return 1;
}
-static int root_map(File_t *This, off_t where, size_t *len, int mode UNUSEDP,
- mt_off_t *res)
+static int root_map(File_t *This, uint32_t where, uint32_t *len,
+ int mode UNUSEDP, mt_off_t *res)
{
- Fs_t *Fs = This->Fs;
+ Fs_t *Fs = _getFs(This);
- if(Fs->dir_len * Fs->sector_size < (size_t) where) {
+ if(Fs->dir_len * Fs->sector_size < where) {
*len = 0;
errno = ENOSPC;
return -2;
}
- sizemaximize(*len, Fs->dir_len * Fs->sector_size - where);
+ maximize(*len, Fs->dir_len * Fs->sector_size - where);
if (*len == 0)
return 0;
-
- *res = sectorsToBytes((Stream_t*)Fs, Fs->dir_start) + where;
+
+ *res = sectorsToBytes(Fs, Fs->dir_start) +
+ to_mt_off_t(where);
return 1;
}
-
-static int read_file(Stream_t *Stream, char *buf, mt_off_t iwhere,
- size_t len)
+static ssize_t read_file(Stream_t *Stream, char *buf, size_t ilen)
{
DeclareThis(File_t);
mt_off_t pos;
int err;
- off_t where = truncBytes32(iwhere);
-
- Stream_t *Disk = This->Fs->Next;
+ uint32_t len = truncSizeTo32u(ilen);
+ ssize_t ret;
- err = This->map(This, where, &len, MT_READ, &pos);
+ Stream_t *Disk = _getFs(This)->head.Next;
+
+ err = This->map(This, This->where, &len, MT_READ, &pos);
if(err <= 0)
return err;
- return READS(Disk, buf, pos, len);
+ ret = PREADS(Disk, buf, pos, len);
+ if(ret < 0)
+ return ret;
+ This->where += (size_t) ret;
+ return ret;
}
-static int write_file(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len)
+static ssize_t write_file(Stream_t *Stream, char *buf, size_t ilen)
{
DeclareThis(File_t);
mt_off_t pos;
- int ret;
- size_t requestedLen;
- Stream_t *Disk = This->Fs->Next;
- off_t where = truncBytes32(iwhere);
+ ssize_t ret;
+ uint32_t requestedLen;
+ uint32_t bytesWritten;
+ Stream_t *Disk = _getFs(This)->head.Next;
+ uint32_t maxLen = UINT32_MAX-This->where;
+ uint32_t len;
int err;
+ if(ilen > maxLen) {
+ len = maxLen;
+ } else
+ len = (uint32_t) ilen;
requestedLen = len;
- err = This->map(This, where, &len, MT_WRITE, &pos);
+ err = This->map(This, This->where, &len, MT_WRITE, &pos);
if( err <= 0)
return err;
if(batchmode)
- ret = force_write(Disk, buf, pos, len);
+ ret = force_pwrite(Disk, buf, pos, len);
else
- ret = WRITES(Disk, buf, pos, len);
- if(ret > (signed int) requestedLen)
- ret = requestedLen;
- if (ret > 0 &&
- where + ret > (off_t) This->FileSize )
- This->FileSize = where + ret;
+ ret = PWRITES(Disk, buf, pos, len);
+ if(ret < 0)
+ /* Error occured */
+ return ret;
+ if((uint32_t)ret > requestedLen)
+ /* More data than requested may be written to lower
+ * levels if batch mode is active, in order to "pad"
+ * the last cluster of a file, so that we don't have
+ * to read that back from disk */
+ bytesWritten = requestedLen;
+ else
+ bytesWritten = (uint32_t)ret;
+ This->where += bytesWritten;
+ if (This->where > This->FileSize )
+ This->FileSize = This->where;
recalcPreallocSize(This);
- return ret;
+ return (ssize_t) bytesWritten;
}
+static ssize_t pread_file(Stream_t *Stream, char *buf, mt_off_t where,
+ size_t ilen) {
+ DeclareThis(File_t);
+ This->where = truncMtOffTo32u(where);
+ return read_file(Stream, buf, ilen);
+}
+
+static ssize_t pwrite_file(Stream_t *Stream, char *buf, mt_off_t where,
+ size_t ilen) {
+ DeclareThis(File_t);
+ This->where = truncMtOffTo32u(where);
+ return write_file(Stream, buf, ilen);
+}
/*
* Convert an MSDOS time & date stamp to the Unix time() format
@@ -449,7 +511,7 @@ static __inline__ time_t conv_stamp(struct directory *dir)
{
struct timeval tv;
struct timezone tz;
-
+
gettimeofday(&tv, &tz);
tzone = tz.tz_minuteswest * 60L;
}
@@ -481,15 +543,15 @@ static __inline__ time_t conv_stamp(struct directory *dir)
}
-static int get_file_data(Stream_t *Stream, time_t *date, mt_size_t *size,
- int *type, int *address)
+static int get_file_data(Stream_t *Stream, time_t *date, mt_off_t *size,
+ int *type, uint32_t *address)
{
DeclareThis(File_t);
if(date)
*date = conv_stamp(& This->direntry.dir);
if(size)
- *size = (mt_size_t) This->FileSize;
+ *size = to_mt_off_t(This->FileSize);
if(type)
*type = This->direntry.dir.attr & ATTR_DIR;
if(address)
@@ -501,8 +563,8 @@ static int get_file_data(Stream_t *Stream, time_t *date, mt_size_t *size,
static int free_file(Stream_t *Stream)
{
DeclareThis(File_t);
- Fs_t *Fs = This->Fs;
- fsPreallocateClusters(Fs, -This->preallocatedClusters);
+ Fs_t *Fs = _getFs(This);
+ fsReleasePreallocateClusters(Fs, This->preallocatedClusters);
FREE(&This->direntry.Dir);
freeDirCache(Stream);
return hash_remove(filehash, (void *) Stream, This->hint);
@@ -527,11 +589,11 @@ static int flush_file(Stream_t *Stream)
}
-static int pre_allocate_file(Stream_t *Stream, mt_size_t isize)
+static int pre_allocate_file(Stream_t *Stream, mt_off_t isize)
{
DeclareThis(File_t);
- size_t size = truncBytes32(isize);
+ uint32_t size = truncMtOffTo32u(isize);
if(size > This->FileSize &&
size > This->preallocatedSize) {
@@ -544,6 +606,8 @@ static int pre_allocate_file(Stream_t *Stream, mt_size_t isize)
static Class_t FileClass = {
read_file,
write_file,
+ pread_file,
+ pwrite_file,
flush_file, /* flush */
free_file, /* free */
0, /* get_geom */
@@ -562,14 +626,14 @@ static unsigned int getAbsCluNr(File_t *This)
return 1;
}
-static size_t func1(void *Stream)
+static uint32_t func1(void *Stream)
{
DeclareThis(File_t);
- return getAbsCluNr(This) ^ (long) This->Fs;
+ return getAbsCluNr(This) ^ (uint32_t) (unsigned long) This->head.Next;
}
-static size_t func2(void *Stream)
+static uint32_t func2(void *Stream)
{
DeclareThis(File_t);
@@ -582,14 +646,14 @@ static int comp(void *Stream, void *Stream2)
File_t *This2 = (File_t *) Stream2;
- return This->Fs != This2->Fs ||
+ return _getFs(This) != _getFs(This2) ||
getAbsCluNr(This) != getAbsCluNr(This2);
}
static void init_hash(void)
{
static int is_initialised=0;
-
+
if(!is_initialised){
make_ht(func1, func2, comp, 20, &filehash);
is_initialised = 1;
@@ -598,7 +662,7 @@ static void init_hash(void)
static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
- size_t size, direntry_t *entry)
+ uint32_t size, direntry_t *entry)
{
Stream_t *Stream = GetFs(Dir);
DeclareThis(Fs_t);
@@ -606,13 +670,12 @@ static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
File_t *File;
init_hash();
- This->refs++;
+ This->head.refs++;
if(first != 1){
/* we use the illegal cluster 1 to mark newly created files.
* do not manage those by hashtable */
- Pattern.Fs = This;
- Pattern.Class = &FileClass;
+ init_head(&Pattern.head, &FileClass, &This->head);
if(first || (entry && !IS_DIR(entry)))
Pattern.map = normal_map;
else
@@ -622,8 +685,8 @@ static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
Pattern.loopDetectAbs = first;
if(!hash_lookup(filehash, (T_HashTableEl) &Pattern,
(T_HashTableEl **)&File, 0)){
- File->refs++;
- This->refs--;
+ File->head.refs++;
+ This->head.refs--;
return (Stream_t *) File;
}
}
@@ -631,6 +694,8 @@ static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
File = New(File_t);
if (!File)
return NULL;
+ init_head(&File->head, &FileClass, &This->head);
+ File->Buffer = NULL;
File->dcp = 0;
File->preallocatedClusters = 0;
File->preallocatedSize = 0;
@@ -640,9 +705,7 @@ static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
File->direntry.Dir = (Stream_t *) File; /* root directory */
else
COPY(File->direntry.Dir);
-
- File->Class = &FileClass;
- File->Fs = This;
+ File->where = 0;
if(first || (entry && !IS_DIR(entry)))
File->map = normal_map;
else
@@ -657,17 +720,41 @@ static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
File->PreviousRelCluNr = 0xffff;
File->FileSize = size;
- File->refs = 1;
- File->Buffer = 0;
hash_add(filehash, (void *) File, &File->hint);
return (Stream_t *) File;
}
+static void bufferize(Stream_t **Dir)
+{
+ Stream_t *BDir;
+ File_t *file = (File_t *) *Dir;
+
+ if(!*Dir)
+ return;
+
+ if(file->Buffer){
+ (*Dir)->refs--;
+ file->Buffer->refs++;
+ *Dir = file->Buffer;
+ return;
+ }
+
+ BDir = buf_init(*Dir, 64*16384, 512, MDIR_SIZE);
+ if(!BDir){
+ FREE(Dir);
+ *Dir = NULL;
+ } else {
+ file->Buffer = BDir;
+ *Dir = BDir;
+ }
+}
+
+
Stream_t *OpenRoot(Stream_t *Dir)
{
unsigned int num;
direntry_t entry;
- size_t size;
+ uint32_t size;
Stream_t *file;
memset(&entry, 0, sizeof(direntry_t));
@@ -695,7 +782,7 @@ Stream_t *OpenFileByDirentry(direntry_t *entry)
{
Stream_t *file;
unsigned int first;
- size_t size;
+ uint32_t size;
first = getStart(entry->Dir, &entry->dir);