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;
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
00024 unsigned int FirstAbsCluNr;
00025
00026
00027 unsigned int PreviousAbsCluNr;
00028
00029
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
00145
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;
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
00251
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
00265 NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr);
00266 if (NewCluNr == 1 ){
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
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;
00384
00385
00386 accum = accum * 365L + month[DOS_MONTH(dir)-1] + DOS_DAY(dir);
00387
00388
00389 accum += (DOS_YEAR(dir) - 1972) / 4L;
00390
00391
00392 if (!(DOS_YEAR(dir) % 4) && DOS_MONTH(dir) < 3)
00393 accum--;
00394 accum = accum * 24L + DOS_HOUR(dir);
00395 accum = accum * 60L + DOS_MINUTE(dir);
00396 accum = accum * 60L + DOS_SEC(dir);
00397
00398 #ifndef OS_Minix
00399
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
00413 extern long timezone;
00414 #endif
00415 tzset();
00416 tzone = (long) timezone;
00417 }
00418 #else
00419 tzone = 0;
00420 #endif
00421 #endif
00422
00423 accum += tzone;
00424 #endif
00425
00426
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,
00502 free_file,
00503 0,
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
00565
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
00590 File->direntry = *entry;
00591 if(entry->entry == -3)
00592 File->direntry.Dir = (Stream_t *) File;
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;
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
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 }