file.c

Go to the documentation of this file.
00001 #include "sysincludes.h"
00002 #include "msdos.h"
00003 #include "stream.h"
00004 #include "mtools.h"
00005 #include "fsP.h"
00006 #include "file.h"
00007 #include "htable.h"
00008 #include "dirCache.h"
00009 
00010 typedef struct File_t {
00011         Class_t *Class;
00012         int refs;
00013         struct Fs_t *Fs;        /* Filesystem that this fat file belongs to */
00014         Stream_t *Buffer;
00015 
00016         int (*map)(struct File_t *this, off_t where, size_t *len, int mode,
00017                            mt_off_t *res);
00018         size_t FileSize;
00019 
00020         size_t preallocatedSize;
00021         int preallocatedClusters;
00022 
00023         /* Absolute position of first cluster of file */
00024         unsigned int FirstAbsCluNr;
00025 
00026         /* Absolute position of previous cluster */
00027         unsigned int PreviousAbsCluNr;
00028 
00029         /* Relative position of previous cluster */
00030         unsigned int PreviousRelCluNr;
00031         direntry_t direntry;
00032         int hint;
00033         struct dirCache_t *dcp;
00034 
00035         unsigned int loopDetectRel;
00036         unsigned int loopDetectAbs;
00037 } File_t;
00038 
00039 static Class_t FileClass;
00040 T_HashTable *filehash;
00041 
00042 static File_t *getUnbufferedFile(Stream_t *Stream)
00043 {
00044         while(Stream->Class != &FileClass)
00045                 Stream = Stream->Next;
00046         return (File_t *) Stream;
00047 }
00048 
00049 Fs_t *getFs(Stream_t *Stream)
00050 {
00051         return getUnbufferedFile(Stream)->Fs;
00052 }
00053 
00054 struct dirCache_t **getDirCacheP(Stream_t *Stream)
00055 {
00056         return &getUnbufferedFile(Stream)->dcp;
00057 }
00058 
00059 direntry_t *getDirentry(Stream_t *Stream)
00060 {
00061         return &getUnbufferedFile(Stream)->direntry;
00062 }
00063 
00064 
00065 static int recalcPreallocSize(File_t *This)
00066 {
00067         size_t currentClusters, neededClusters;
00068         int clus_size;
00069         int neededPrealloc;
00070         Fs_t *Fs = This->Fs;
00071         int r;
00072 
00073         if(This->FileSize & 0xc0000000) {
00074                 fprintf(stderr, "Bad filesize\n");
00075         }
00076         if(This->preallocatedSize & 0xc0000000) {
00077                 fprintf(stderr, "Bad preallocated size %x\n", 
00078                                 (int) This->preallocatedSize);
00079         }
00080 
00081         clus_size = Fs->cluster_size * Fs->sector_size;
00082 
00083         currentClusters = (This->FileSize + clus_size - 1) / clus_size;
00084         neededClusters = (This->preallocatedSize + clus_size - 1) / clus_size;
00085         neededPrealloc = neededClusters - currentClusters;
00086         if(neededPrealloc < 0)
00087                 neededPrealloc = 0;
00088         r = fsPreallocateClusters(Fs, neededPrealloc - This->preallocatedClusters);
00089         if(r)
00090                 return r;
00091         This->preallocatedClusters = neededPrealloc;
00092         return 0;
00093 }
00094 
00095 static int _loopDetect(unsigned int *oldrel, unsigned int rel, 
00096                                            unsigned int *oldabs, unsigned int abs)
00097 {
00098         if(*oldrel && rel > *oldrel && abs == *oldabs) {
00099                 fprintf(stderr, "loop detected! oldrel=%d newrel=%d abs=%d\n",
00100                                 *oldrel, rel, abs);
00101                 return -1;
00102         }
00103 
00104         if(rel >= 2 * *oldrel + 1) {
00105                 *oldrel = rel;
00106                 *oldabs = abs;
00107         }
00108         return 0;
00109 }
00110 
00111 
00112 static int loopDetect(File_t *This, unsigned int rel, unsigned int abs)
00113 {
00114         return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, abs);
00115 }
00116 
00117 static unsigned int _countBlocks(Fs_t *This, unsigned int block)
00118 {
00119         unsigned int blocks;
00120         unsigned int rel, oldabs, oldrel;
00121 
00122         blocks = 0;
00123         
00124         oldabs = oldrel = rel = 0;
00125 
00126         while (block <= This->last_fat && block != 1 && block) {
00127                 blocks++;
00128                 block = fatDecode(This, block);
00129                 rel++;
00130                 if(_loopDetect(&oldrel, rel, &oldabs, block) < 0)
00131                         block = -1;
00132         }
00133         return blocks;
00134 }
00135 
00136 unsigned int countBlocks(Stream_t *Dir, unsigned int block)
00137 {
00138         Stream_t *Stream = GetFs(Dir);
00139         DeclareThis(Fs_t);
00140 
00141         return _countBlocks(This, block);
00142 }
00143 
00144 /* returns number of bytes in a directory.  Represents a file size, and
00145  * can hence be not bigger than 2^32
00146  */
00147 static size_t countBytes(Stream_t *Dir, unsigned int block)
00148 {
00149         Stream_t *Stream = GetFs(Dir);
00150         DeclareThis(Fs_t);
00151 
00152         return _countBlocks(This, block) * 
00153                 This->sector_size * This->cluster_size;
00154 }
00155 
00156 void printFat(Stream_t *Stream)
00157 {
00158         File_t *This = getUnbufferedFile(Stream);
00159         unsigned long n;
00160         int rel;
00161         unsigned long begin, end;
00162         int first;
00163 
00164         n = This->FirstAbsCluNr;
00165         if(!n) {
00166                 printf("Root directory or empty file\n");
00167                 return;
00168         }
00169 
00170         rel = 0;
00171         first = 1;
00172         begin = end = 0;
00173         do {
00174                 if (first || n != end+1) {
00175                         if (!first) {
00176                                 if (begin != end)
00177                                         printf("-%lu", end);
00178                                 printf("> ");
00179                         }
00180                         begin = end = n;
00181                         printf("<%lu", begin);
00182                 } else {
00183                         end++;
00184                 }
00185                 first = 0;
00186                 n = fatDecode(This->Fs, n);
00187                 rel++;
00188                 if(loopDetect(This, rel, n) < 0)
00189                         n = 1;
00190         } while (n <= This->Fs->last_fat && n != 1);
00191         if(!first) {
00192                 if (begin != end)
00193                         printf("-%lu", end);
00194                 printf(">");
00195         }
00196 }
00197 
00198 static int normal_map(File_t *This, off_t where, size_t *len, int mode,
00199                                                    mt_off_t *res)
00200 {
00201         int offset;
00202         off_t end;
00203         int NrClu; /* number of clusters to read */
00204         unsigned int RelCluNr;
00205         unsigned int CurCluNr;
00206         unsigned int NewCluNr;
00207         unsigned int AbsCluNr;
00208         int clus_size;
00209         Fs_t *Fs = This->Fs;
00210 
00211         *res = 0;
00212         clus_size = Fs->cluster_size * Fs->sector_size;
00213         offset = where % clus_size;
00214 
00215         if (mode == MT_READ)
00216                 maximize(*len, This->FileSize - where);
00217         if (*len == 0 )
00218                 return 0;
00219 
00220         if (This->FirstAbsCluNr < 2){
00221                 if( mode == MT_READ || *len == 0){
00222                         *len = 0;
00223                         return 0;
00224                 }
00225                 NewCluNr = get_next_free_cluster(This->Fs, 1);
00226                 if (NewCluNr == 1 ){
00227                         errno = ENOSPC;
00228                         return -2;
00229                 }
00230                 hash_remove(filehash, (void *) This, This->hint);
00231                 This->FirstAbsCluNr = NewCluNr;
00232                 hash_add(filehash, (void *) This, &This->hint);
00233                 fatAllocate(This->Fs, NewCluNr, Fs->end_fat);
00234         }
00235 
00236         RelCluNr = where / clus_size;
00237         
00238         if (RelCluNr >= This->PreviousRelCluNr){
00239                 CurCluNr = This->PreviousRelCluNr;
00240                 AbsCluNr = This->PreviousAbsCluNr;
00241         } else {
00242                 CurCluNr = 0;
00243                 AbsCluNr = This->FirstAbsCluNr;
00244         }
00245 
00246 
00247         NrClu = (offset + *len - 1) / clus_size;
00248         while (CurCluNr <= RelCluNr + NrClu){
00249                 if (CurCluNr == RelCluNr){
00250                         /* we have reached the beginning of our zone. Save
00251                          * coordinates */
00252                         This->PreviousRelCluNr = RelCluNr;
00253                         This->PreviousAbsCluNr = AbsCluNr;
00254                 }
00255                 NewCluNr = fatDecode(This->Fs, AbsCluNr);
00256                 if (NewCluNr == 1 || NewCluNr == 0){
00257                         fprintf(stderr,"Fat problem while decoding %d %x\n", 
00258                                 AbsCluNr, NewCluNr);
00259                         exit(1);
00260                 }
00261                 if(CurCluNr == RelCluNr + NrClu)                        
00262                         break;
00263                 if (NewCluNr > Fs->last_fat && mode == MT_WRITE){
00264                         /* if at end, and writing, extend it */
00265                         NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr);
00266                         if (NewCluNr == 1 ){ /* no more space */
00267                                 errno = ENOSPC;
00268                                 return -2;
00269                         }
00270                         fatAppend(This->Fs, AbsCluNr, NewCluNr);
00271                 }
00272 
00273                 if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat){
00274                         *len = 0;
00275                         return 0;
00276                 }
00277 
00278                 if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
00279                         break;
00280                 CurCluNr++;
00281                 AbsCluNr = NewCluNr;
00282                 if(loopDetect(This, CurCluNr, AbsCluNr)) {
00283                         errno = EIO;
00284                         return -2;
00285                 }
00286         }
00287 
00288         maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
00289         
00290         end = where + *len;
00291         if(batchmode && mode == MT_WRITE && end >= This->FileSize) {
00292                 *len += ROUND_UP(end, clus_size) - end;
00293         }
00294 
00295         if((*len + offset) / clus_size + This->PreviousAbsCluNr-2 >
00296                 Fs->num_clus) {
00297                 fprintf(stderr, "cluster too big\n");
00298                 exit(1);
00299         }
00300 
00301         *res = sectorsToBytes((Stream_t*)Fs, 
00302                                                   (This->PreviousAbsCluNr-2) * Fs->cluster_size +
00303                                                   Fs->clus_start) + offset;
00304         return 1;
00305 }
00306 
00307 
00308 static int root_map(File_t *This, off_t where, size_t *len, int mode,
00309                                         mt_off_t *res)
00310 {
00311         Fs_t *Fs = This->Fs;
00312 
00313         if(Fs->dir_len * Fs->sector_size < where) {
00314                 *len = 0;
00315                 errno = ENOSPC;
00316                 return -2;
00317         }
00318 
00319         maximize(*len, Fs->dir_len * Fs->sector_size - where);
00320         if (*len == 0)
00321             return 0;
00322         
00323         *res = sectorsToBytes((Stream_t*)Fs, Fs->dir_start) + where;
00324         return 1;
00325 }
00326         
00327 
00328 static int read_file(Stream_t *Stream, char *buf, mt_off_t iwhere, 
00329                                          size_t len)
00330 {
00331         DeclareThis(File_t);
00332         mt_off_t pos;
00333         int err;
00334         off_t where = truncBytes32(iwhere);
00335 
00336         Stream_t *Disk = This->Fs->Next;
00337         
00338         err = This->map(This, where, &len, MT_READ, &pos);
00339         if(err <= 0)
00340                 return err;
00341         return READS(Disk, buf, pos, len);
00342 }
00343 
00344 static int write_file(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len)
00345 {
00346         DeclareThis(File_t);
00347         mt_off_t pos;
00348         int ret;
00349         size_t requestedLen;
00350         Stream_t *Disk = This->Fs->Next;
00351         off_t where = truncBytes32(iwhere);
00352         int err;
00353 
00354         requestedLen = len;
00355         err = This->map(This, where, &len, MT_WRITE, &pos);
00356         if( err <= 0)
00357                 return err;
00358         if(batchmode)
00359                 ret = force_write(Disk, buf, pos, len);
00360         else
00361                 ret = WRITES(Disk, buf, pos, len);
00362         if(ret > requestedLen)
00363                 ret = requestedLen;
00364         if (ret > 0 && where + ret > This->FileSize )
00365                 This->FileSize = where + ret;
00366         recalcPreallocSize(This);
00367         return ret;
00368 }
00369 
00370 
00371 /*
00372  * Convert an MSDOS time & date stamp to the Unix time() format
00373  */
00374 
00375 static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
00376                                           0, 0, 0 };
00377 static inline time_t conv_stamp(struct directory *dir)
00378 {
00379         struct tm *tmbuf;
00380         long tzone, dst;
00381         time_t accum, tmp;
00382 
00383         accum = DOS_YEAR(dir) - 1970; /* years past */
00384 
00385         /* days passed */
00386         accum = accum * 365L + month[DOS_MONTH(dir)-1] + DOS_DAY(dir);
00387 
00388         /* leap years */
00389         accum += (DOS_YEAR(dir) - 1972) / 4L;
00390 
00391         /* back off 1 day if before 29 Feb */
00392         if (!(DOS_YEAR(dir) % 4) && DOS_MONTH(dir) < 3)
00393                 accum--;
00394         accum = accum * 24L + DOS_HOUR(dir); /* hours passed */
00395         accum = accum * 60L + DOS_MINUTE(dir); /* minutes passed */
00396         accum = accum * 60L + DOS_SEC(dir); /* seconds passed */
00397 
00398 #ifndef OS_Minix
00399         /* correct for Time Zone */
00400 #ifdef HAVE_GETTIMEOFDAY
00401         {
00402                 struct timeval tv;
00403                 struct timezone tz;
00404                 
00405                 gettimeofday(&tv, &tz);
00406                 tzone = tz.tz_minuteswest * 60L;
00407         }
00408 #else
00409 #ifdef HAVE_TZSET
00410         {
00411 #ifndef OS_ultrix
00412                 /* Ultrix defines this to be a different type */
00413                 extern long timezone;
00414 #endif
00415                 tzset();
00416                 tzone = (long) timezone;
00417         }
00418 #else
00419         tzone = 0;
00420 #endif /* HAVE_TZSET */
00421 #endif /* HAVE_GETTIMEOFDAY */
00422 
00423         accum += tzone;
00424 #endif /* OS_Minix */
00425 
00426         /* correct for Daylight Saving Time */
00427         tmp = accum;
00428         tmbuf = localtime(&tmp);
00429 #ifndef OS_Minix
00430         dst = (tmbuf->tm_isdst) ? (-60L * 60L) : 0L;
00431         accum += dst;
00432 #endif
00433         
00434         return accum;
00435 }
00436 
00437 
00438 static int get_file_data(Stream_t *Stream, time_t *date, mt_size_t *size,
00439                          int *type, int *address)
00440 {
00441         DeclareThis(File_t);
00442 
00443         if(date)
00444                 *date = conv_stamp(& This->direntry.dir);
00445         if(size)
00446                 *size = (mt_size_t) This->FileSize;
00447         if(type)
00448                 *type = This->direntry.dir.attr & ATTR_DIR;
00449         if(address)
00450                 *address = This->FirstAbsCluNr;
00451         return 0;
00452 }
00453 
00454 
00455 static int free_file(Stream_t *Stream)
00456 {
00457         DeclareThis(File_t);
00458         Fs_t *Fs = This->Fs;
00459         fsPreallocateClusters(Fs, -This->preallocatedClusters);       
00460         FREE(&This->direntry.Dir);
00461         freeDirCache(Stream);
00462         return hash_remove(filehash, (void *) Stream, This->hint);
00463 }
00464 
00465 
00466 static int flush_file(Stream_t *Stream)
00467 {
00468         DeclareThis(File_t);
00469         direntry_t *entry = &This->direntry;
00470 
00471         if(isRootDir(Stream)) {
00472                 return 0;
00473         }
00474 
00475         if(This->FirstAbsCluNr != getStart(entry->Dir, &entry->dir)) {
00476                 set_word(entry->dir.start, This->FirstAbsCluNr & 0xffff);
00477                 set_word(entry->dir.startHi, This->FirstAbsCluNr >> 16);
00478                 dir_write(entry);
00479         }
00480         return 0;
00481 }
00482 
00483 
00484 static int pre_allocate_file(Stream_t *Stream, mt_size_t isize)
00485 {
00486         DeclareThis(File_t);
00487 
00488         size_t size = truncBytes32(isize);
00489 
00490         if(size > This->FileSize &&
00491            size > This->preallocatedSize) {
00492                 This->preallocatedSize = size;
00493                 return recalcPreallocSize(This);
00494         } else
00495                 return 0;
00496 }
00497 
00498 static Class_t FileClass = {
00499         read_file, 
00500         write_file, 
00501         flush_file, /* flush */
00502         free_file, /* free */
00503         0, /* get_geom */
00504         get_file_data,
00505         pre_allocate_file
00506 };
00507 
00508 static unsigned int getAbsCluNr(File_t *This)
00509 {
00510         if(This->FirstAbsCluNr)
00511                 return This->FirstAbsCluNr;
00512         if(isRootDir((Stream_t *) This))
00513                 return 0;
00514         return 1;
00515 }
00516 
00517 static unsigned int func1(void *Stream)
00518 {
00519         DeclareThis(File_t);
00520 
00521         return getAbsCluNr(This) ^ (long) This->Fs;
00522 }
00523 
00524 static unsigned int func2(void *Stream)
00525 {
00526         DeclareThis(File_t);
00527 
00528         return getAbsCluNr(This);
00529 }
00530 
00531 static int comp(void *Stream, void *Stream2)
00532 {
00533         DeclareThis(File_t);
00534 
00535         File_t *This2 = (File_t *) Stream2;
00536 
00537         return This->Fs != This2->Fs ||
00538                 getAbsCluNr(This) != getAbsCluNr(This2);
00539 }
00540 
00541 static void init_hash(void)
00542 {
00543         static int is_initialised=0;
00544         
00545         if(!is_initialised){
00546                 make_ht(func1, func2, comp, 20, &filehash);
00547                 is_initialised = 1;
00548         }
00549 }
00550 
00551 
00552 static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first, 
00553                                    size_t size, direntry_t *entry)
00554 {
00555         Stream_t *Stream = GetFs(Dir);
00556         DeclareThis(Fs_t);
00557         File_t Pattern;
00558         File_t *File;
00559 
00560         init_hash();
00561         This->refs++;
00562 
00563         if(first != 1){
00564                 /* we use the illegal cluster 1 to mark newly created files.
00565                  * do not manage those by hashtable */
00566                 Pattern.Fs = This;
00567                 Pattern.Class = &FileClass;
00568                 if(first || (entry && !IS_DIR(entry)))
00569                         Pattern.map = normal_map;
00570                 else
00571                         Pattern.map = root_map;
00572                 Pattern.FirstAbsCluNr = first;
00573                 Pattern.loopDetectRel = 0;
00574                 Pattern.loopDetectAbs = first;
00575                 if(!hash_lookup(filehash, (T_HashTableEl) &Pattern, 
00576                                 (T_HashTableEl **)&File, 0)){
00577                         File->refs++;
00578                         This->refs--;
00579                         return (Stream_t *) File;
00580                 }
00581         }
00582 
00583         File = New(File_t);
00584         if (!File)
00585                 return NULL;
00586         File->dcp = 0;
00587         File->preallocatedClusters = 0;
00588         File->preallocatedSize = 0;
00589         /* memorize dir for date and attrib */
00590         File->direntry = *entry;
00591         if(entry->entry == -3)
00592                 File->direntry.Dir = (Stream_t *) File; /* root directory */
00593         else
00594                 COPY(File->direntry.Dir);
00595 
00596         File->Class = &FileClass;
00597         File->Fs = This;
00598         if(first || (entry && !IS_DIR(entry)))
00599                 File->map = normal_map;
00600         else
00601                 File->map = root_map; /* FAT 12/16 root directory */
00602         if(first == 1)
00603                 File->FirstAbsCluNr = 0;
00604         else
00605                 File->FirstAbsCluNr = first;
00606 
00607         File->loopDetectRel = 0;
00608         File->loopDetectAbs = 0;
00609 
00610         File->PreviousRelCluNr = 0xffff;
00611         File->FileSize = size;
00612         File->refs = 1;
00613         File->Buffer = 0;
00614         hash_add(filehash, (void *) File, &File->hint);
00615         return (Stream_t *) File;
00616 }
00617 
00618 Stream_t *OpenRoot(Stream_t *Dir)
00619 {
00620         unsigned int num;
00621         direntry_t entry;
00622         size_t size;
00623         Stream_t *file;
00624 
00625         memset(&entry, 0, sizeof(direntry_t));
00626 
00627         num = fat32RootCluster(Dir);
00628 
00629         /* make the directory entry */
00630         entry.entry = -3;
00631         entry.name[0] = '\0';
00632         mk_entry("/", ATTR_DIR, num, 0, 0, &entry.dir);
00633 
00634         if(num)
00635                 size = countBytes(Dir, num);
00636         else {
00637                 Fs_t *Fs = (Fs_t *) GetFs(Dir);
00638                 size = Fs->dir_len * Fs->sector_size;
00639         }
00640         file = _internalFileOpen(Dir, num, size, &entry);
00641         bufferize(&file);
00642         return file;
00643 }
00644 
00645 
00646 Stream_t *OpenFileByDirentry(direntry_t *entry)
00647 {
00648         Stream_t *file;
00649         unsigned int first;
00650         size_t size;
00651 
00652         first = getStart(entry->Dir, &entry->dir);
00653 
00654         if(!first && IS_DIR(entry))
00655                 return OpenRoot(entry->Dir);
00656         if (IS_DIR(entry))
00657                 size = countBytes(entry->Dir, first);
00658         else 
00659                 size = FILE_SIZE(&entry->dir);
00660         file = _internalFileOpen(entry->Dir, first, size, entry);
00661         if(IS_DIR(entry)) {
00662                 bufferize(&file);
00663                 if(first == 1)
00664                         dir_grow(file, 0);
00665         }
00666 
00667         return file;
00668 }
00669 
00670 
00671 int isRootDir(Stream_t *Stream)
00672 {
00673         File_t *This = getUnbufferedFile(Stream);
00674 
00675         return This->map == root_map;
00676 }

Generated on Fri Apr 14 22:56:53 2006 for minix by  doxygen 1.4.6