Imported Upstream version 4.0.35
[platform/upstream/mtools.git] / file.c
diff --git a/file.c b/file.c
index bd1c2f0..d72c7d0 100644 (file)
--- a/file.c
+++ b/file.c
@@ -30,12 +30,18 @@ typedef struct File_t {
        struct Fs_t *Fs;        /* Filesystem that this fat file belongs to */
        Stream_t *Buffer;
 
-       int (*map)(struct File_t *this, off_t where, size_t *len, int mode,
+       int (*map)(struct File_t *this, uint32_t where, uint32_t *len, int mode,
                           mt_off_t *res);
-       size_t FileSize;
+       uint32_t FileSize;
 
-       size_t preallocatedSize;
-       int preallocatedClusters;
+       /* How many bytes do we project to need for this file
+          (includes those already in FileSize) */
+       uint32_t preallocatedSize;
+
+       /* 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;
@@ -46,7 +52,7 @@ typedef struct File_t {
        /* Relative position of previous cluster */
        unsigned int PreviousRelCluNr;
        direntry_t direntry;
-       int hint;
+       size_t hint;
        struct dirCache_t *dcp;
 
        unsigned int loopDetectRel;
@@ -54,7 +60,7 @@ typedef struct File_t {
 } File_t;
 
 static Class_t FileClass;
-T_HashTable *filehash;
+static T_HashTable *filehash;
 
 static File_t *getUnbufferedFile(Stream_t *Stream)
 {
@@ -78,14 +84,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;
-       int clus_size;
-       int neededPrealloc;
+       uint32_t currentClusters, neededClusters;
+       unsigned int clus_size;
+       uint32_t neededPrealloc;
        Fs_t *Fs = This->Fs;
-       int r;
 
 #if 0
        if(This->FileSize & 0xc0000000) {
@@ -97,15 +111,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 +158,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) {
@@ -146,7 +166,7 @@ static unsigned int _countBlocks(Fs_t *This, unsigned int block)
                block = fatDecode(This, block);
                rel++;
                if(_loopDetect(&oldrel, rel, &oldabs, block) < 0)
-                       block = -1;
+                       block = 1;
        }
        return blocks;
 }
@@ -162,7 +182,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,8 +194,8 @@ static size_t countBytes(Stream_t *Dir, unsigned int block)
 void printFat(Stream_t *Stream)
 {
        File_t *This = getUnbufferedFile(Stream);
-       unsigned long n;
-       int rel;
+       uint32_t n;
+       unsigned int rel;
        unsigned long begin, end;
        int first;
 
@@ -215,8 +235,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;
@@ -238,20 +258,20 @@ void printFatWithOffset(Stream_t *Stream, off_t offset) {
                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)
 {
-       int offset;
+       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;
-       int clus_size;
+       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 = This->Fs;
 
        *res = 0;
@@ -280,7 +300,7 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
        }
 
        RelCluNr = where / clus_size;
-       
+
        if (RelCluNr >= This->PreviousRelCluNr){
                CurCluNr = This->PreviousRelCluNr;
                AbsCluNr = This->PreviousAbsCluNr;
@@ -304,7 +324,7 @@ static int normal_map(File_t *This, off_t where, size_t *len, int mode,
                                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 */
@@ -332,11 +352,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,19 +369,19 @@ 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,
-                                       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;
 
-       if(Fs->dir_len * Fs->sector_size < (size_t) where) {
+       if(Fs->dir_len * Fs->sector_size < where) {
                *len = 0;
                errno = ENOSPC;
                return -2;
@@ -367,38 +390,48 @@ static int root_map(File_t *This, off_t where, size_t *len, int mode,
        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, mt_off_t iwhere,
+                        size_t ilen)
 {
        DeclareThis(File_t);
        mt_off_t pos;
        int err;
-       off_t where = truncBytes32(iwhere);
+       uint32_t where = truncMtOffTo32u(iwhere);
+       uint32_t len = truncSizeTo32u(ilen);
 
        Stream_t *Disk = This->Fs->Next;
-       
+
        err = This->map(This, where, &len, MT_READ, &pos);
        if(err <= 0)
                return err;
        return READS(Disk, buf, pos, len);
 }
 
-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,
+                         mt_off_t iwhere, size_t ilen)
 {
        DeclareThis(File_t);
        mt_off_t pos;
-       int ret;
-       size_t requestedLen;
+       ssize_t ret;
+       uint32_t requestedLen;
+       uint32_t bytesWritten;
        Stream_t *Disk = This->Fs->Next;
-       off_t where = truncBytes32(iwhere);
+       uint32_t where = truncMtOffTo32u(iwhere);
+       uint32_t maxLen = UINT32_MAX-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);
        if( err <= 0)
@@ -407,13 +440,21 @@ static int write_file(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len)
                ret = force_write(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;
+       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;
+       if (where + bytesWritten > This->FileSize )
+               This->FileSize = where + bytesWritten;
        recalcPreallocSize(This);
-       return ret;
+       return (ssize_t)bytesWritten;
 }
 
 
@@ -449,7 +490,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 +522,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)
@@ -502,7 +543,7 @@ static int free_file(Stream_t *Stream)
 {
        DeclareThis(File_t);
        Fs_t *Fs = This->Fs;
-       fsPreallocateClusters(Fs, -This->preallocatedClusters);
+       fsReleasePreallocateClusters(Fs, This->preallocatedClusters);
        FREE(&This->direntry.Dir);
        freeDirCache(Stream);
        return hash_remove(filehash, (void *) Stream, This->hint);
@@ -527,11 +568,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) {
@@ -549,7 +590,8 @@ static Class_t FileClass = {
        0, /* get_geom */
        get_file_data,
        pre_allocate_file,
-       get_dosConvert_pass_through
+       get_dosConvert_pass_through,
+       0 /* discard */
 };
 
 static unsigned int getAbsCluNr(File_t *This)
@@ -561,14 +603,14 @@ static unsigned int getAbsCluNr(File_t *This)
        return 1;
 }
 
-static unsigned int 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->Fs;
 }
 
-static unsigned int func2(void *Stream)
+static uint32_t func2(void *Stream)
 {
        DeclareThis(File_t);
 
@@ -588,7 +630,7 @@ static int comp(void *Stream, void *Stream2)
 static void init_hash(void)
 {
        static int is_initialised=0;
-       
+
        if(!is_initialised){
                make_ht(func1, func2, comp, 20, &filehash);
                is_initialised = 1;
@@ -597,7 +639,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);
@@ -666,7 +708,7 @@ 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));
@@ -694,7 +736,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);