[9] | 1 | #include "sysincludes.h"
|
---|
| 2 | #include "msdos.h"
|
---|
| 3 | #include "stream.h"
|
---|
| 4 | #include "mtools.h"
|
---|
| 5 | #include "fsP.h"
|
---|
| 6 | #include "file.h"
|
---|
| 7 | #include "htable.h"
|
---|
| 8 | #include "dirCache.h"
|
---|
| 9 |
|
---|
| 10 | typedef struct File_t {
|
---|
| 11 | Class_t *Class;
|
---|
| 12 | int refs;
|
---|
| 13 | struct Fs_t *Fs; /* Filesystem that this fat file belongs to */
|
---|
| 14 | Stream_t *Buffer;
|
---|
| 15 |
|
---|
| 16 | int (*map)(struct File_t *this, off_t where, size_t *len, int mode,
|
---|
| 17 | mt_off_t *res);
|
---|
| 18 | size_t FileSize;
|
---|
| 19 |
|
---|
| 20 | size_t preallocatedSize;
|
---|
| 21 | int preallocatedClusters;
|
---|
| 22 |
|
---|
| 23 | /* Absolute position of first cluster of file */
|
---|
| 24 | unsigned int FirstAbsCluNr;
|
---|
| 25 |
|
---|
| 26 | /* Absolute position of previous cluster */
|
---|
| 27 | unsigned int PreviousAbsCluNr;
|
---|
| 28 |
|
---|
| 29 | /* Relative position of previous cluster */
|
---|
| 30 | unsigned int PreviousRelCluNr;
|
---|
| 31 | direntry_t direntry;
|
---|
| 32 | int hint;
|
---|
| 33 | struct dirCache_t *dcp;
|
---|
| 34 |
|
---|
| 35 | unsigned int loopDetectRel;
|
---|
| 36 | unsigned int loopDetectAbs;
|
---|
| 37 | } File_t;
|
---|
| 38 |
|
---|
| 39 | static Class_t FileClass;
|
---|
| 40 | T_HashTable *filehash;
|
---|
| 41 |
|
---|
| 42 | static File_t *getUnbufferedFile(Stream_t *Stream)
|
---|
| 43 | {
|
---|
| 44 | while(Stream->Class != &FileClass)
|
---|
| 45 | Stream = Stream->Next;
|
---|
| 46 | return (File_t *) Stream;
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | Fs_t *getFs(Stream_t *Stream)
|
---|
| 50 | {
|
---|
| 51 | return getUnbufferedFile(Stream)->Fs;
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | struct dirCache_t **getDirCacheP(Stream_t *Stream)
|
---|
| 55 | {
|
---|
| 56 | return &getUnbufferedFile(Stream)->dcp;
|
---|
| 57 | }
|
---|
| 58 |
|
---|
| 59 | direntry_t *getDirentry(Stream_t *Stream)
|
---|
| 60 | {
|
---|
| 61 | return &getUnbufferedFile(Stream)->direntry;
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 |
|
---|
| 65 | static int recalcPreallocSize(File_t *This)
|
---|
| 66 | {
|
---|
| 67 | size_t currentClusters, neededClusters;
|
---|
| 68 | int clus_size;
|
---|
| 69 | int neededPrealloc;
|
---|
| 70 | Fs_t *Fs = This->Fs;
|
---|
| 71 | int r;
|
---|
| 72 |
|
---|
| 73 | if(This->FileSize & 0xc0000000) {
|
---|
| 74 | fprintf(stderr, "Bad filesize\n");
|
---|
| 75 | }
|
---|
| 76 | if(This->preallocatedSize & 0xc0000000) {
|
---|
| 77 | fprintf(stderr, "Bad preallocated size %x\n",
|
---|
| 78 | (int) This->preallocatedSize);
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | clus_size = Fs->cluster_size * Fs->sector_size;
|
---|
| 82 |
|
---|
| 83 | currentClusters = (This->FileSize + clus_size - 1) / clus_size;
|
---|
| 84 | neededClusters = (This->preallocatedSize + clus_size - 1) / clus_size;
|
---|
| 85 | neededPrealloc = neededClusters - currentClusters;
|
---|
| 86 | if(neededPrealloc < 0)
|
---|
| 87 | neededPrealloc = 0;
|
---|
| 88 | r = fsPreallocateClusters(Fs, neededPrealloc - This->preallocatedClusters);
|
---|
| 89 | if(r)
|
---|
| 90 | return r;
|
---|
| 91 | This->preallocatedClusters = neededPrealloc;
|
---|
| 92 | return 0;
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | static int _loopDetect(unsigned int *oldrel, unsigned int rel,
|
---|
| 96 | unsigned int *oldabs, unsigned int abs)
|
---|
| 97 | {
|
---|
| 98 | if(*oldrel && rel > *oldrel && abs == *oldabs) {
|
---|
| 99 | fprintf(stderr, "loop detected! oldrel=%d newrel=%d abs=%d\n",
|
---|
| 100 | *oldrel, rel, abs);
|
---|
| 101 | return -1;
|
---|
| 102 | }
|
---|
| 103 |
|
---|
| 104 | if(rel >= 2 * *oldrel + 1) {
|
---|
| 105 | *oldrel = rel;
|
---|
| 106 | *oldabs = abs;
|
---|
| 107 | }
|
---|
| 108 | return 0;
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 |
|
---|
| 112 | static int loopDetect(File_t *This, unsigned int rel, unsigned int abs)
|
---|
| 113 | {
|
---|
| 114 | return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, abs);
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | static unsigned int _countBlocks(Fs_t *This, unsigned int block)
|
---|
| 118 | {
|
---|
| 119 | unsigned int blocks;
|
---|
| 120 | unsigned int rel, oldabs, oldrel;
|
---|
| 121 |
|
---|
| 122 | blocks = 0;
|
---|
| 123 |
|
---|
| 124 | oldabs = oldrel = rel = 0;
|
---|
| 125 |
|
---|
| 126 | while (block <= This->last_fat && block != 1 && block) {
|
---|
| 127 | blocks++;
|
---|
| 128 | block = fatDecode(This, block);
|
---|
| 129 | rel++;
|
---|
| 130 | if(_loopDetect(&oldrel, rel, &oldabs, block) < 0)
|
---|
| 131 | block = -1;
|
---|
| 132 | }
|
---|
| 133 | return blocks;
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | unsigned int countBlocks(Stream_t *Dir, unsigned int block)
|
---|
| 137 | {
|
---|
| 138 | Stream_t *Stream = GetFs(Dir);
|
---|
| 139 | DeclareThis(Fs_t);
|
---|
| 140 |
|
---|
| 141 | return _countBlocks(This, block);
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | /* returns number of bytes in a directory. Represents a file size, and
|
---|
| 145 | * can hence be not bigger than 2^32
|
---|
| 146 | */
|
---|
| 147 | static size_t countBytes(Stream_t *Dir, unsigned int block)
|
---|
| 148 | {
|
---|
| 149 | Stream_t *Stream = GetFs(Dir);
|
---|
| 150 | DeclareThis(Fs_t);
|
---|
| 151 |
|
---|
| 152 | return _countBlocks(This, block) *
|
---|
| 153 | This->sector_size * This->cluster_size;
|
---|
| 154 | }
|
---|
| 155 |
|
---|
| 156 | void printFat(Stream_t *Stream)
|
---|
| 157 | {
|
---|
| 158 | File_t *This = getUnbufferedFile(Stream);
|
---|
| 159 | unsigned long n;
|
---|
| 160 | int rel;
|
---|
| 161 | unsigned long begin, end;
|
---|
| 162 | int first;
|
---|
| 163 |
|
---|
| 164 | n = This->FirstAbsCluNr;
|
---|
| 165 | if(!n) {
|
---|
| 166 | printf("Root directory or empty file\n");
|
---|
| 167 | return;
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | rel = 0;
|
---|
| 171 | first = 1;
|
---|
| 172 | begin = end = 0;
|
---|
| 173 | do {
|
---|
| 174 | if (first || n != end+1) {
|
---|
| 175 | if (!first) {
|
---|
| 176 | if (begin != end)
|
---|
| 177 | printf("-%lu", end);
|
---|
| 178 | printf("> ");
|
---|
| 179 | }
|
---|
| 180 | begin = end = n;
|
---|
| 181 | printf("<%lu", begin);
|
---|
| 182 | } else {
|
---|
| 183 | end++;
|
---|
| 184 | }
|
---|
| 185 | first = 0;
|
---|
| 186 | n = fatDecode(This->Fs, n);
|
---|
| 187 | rel++;
|
---|
| 188 | if(loopDetect(This, rel, n) < 0)
|
---|
| 189 | n = 1;
|
---|
| 190 | } while (n <= This->Fs->last_fat && n != 1);
|
---|
| 191 | if(!first) {
|
---|
| 192 | if (begin != end)
|
---|
| 193 | printf("-%lu", end);
|
---|
| 194 | printf(">");
|
---|
| 195 | }
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | static int normal_map(File_t *This, off_t where, size_t *len, int mode,
|
---|
| 199 | mt_off_t *res)
|
---|
| 200 | {
|
---|
| 201 | int offset;
|
---|
| 202 | off_t end;
|
---|
| 203 | int NrClu; /* number of clusters to read */
|
---|
| 204 | unsigned int RelCluNr;
|
---|
| 205 | unsigned int CurCluNr;
|
---|
| 206 | unsigned int NewCluNr;
|
---|
| 207 | unsigned int AbsCluNr;
|
---|
| 208 | int clus_size;
|
---|
| 209 | Fs_t *Fs = This->Fs;
|
---|
| 210 |
|
---|
| 211 | *res = 0;
|
---|
| 212 | clus_size = Fs->cluster_size * Fs->sector_size;
|
---|
| 213 | offset = where % clus_size;
|
---|
| 214 |
|
---|
| 215 | if (mode == MT_READ)
|
---|
| 216 | maximize(*len, This->FileSize - where);
|
---|
| 217 | if (*len == 0 )
|
---|
| 218 | return 0;
|
---|
| 219 |
|
---|
| 220 | if (This->FirstAbsCluNr < 2){
|
---|
| 221 | if( mode == MT_READ || *len == 0){
|
---|
| 222 | *len = 0;
|
---|
| 223 | return 0;
|
---|
| 224 | }
|
---|
| 225 | NewCluNr = get_next_free_cluster(This->Fs, 1);
|
---|
| 226 | if (NewCluNr == 1 ){
|
---|
| 227 | errno = ENOSPC;
|
---|
| 228 | return -2;
|
---|
| 229 | }
|
---|
| 230 | hash_remove(filehash, (void *) This, This->hint);
|
---|
| 231 | This->FirstAbsCluNr = NewCluNr;
|
---|
| 232 | hash_add(filehash, (void *) This, &This->hint);
|
---|
| 233 | fatAllocate(This->Fs, NewCluNr, Fs->end_fat);
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | RelCluNr = where / clus_size;
|
---|
| 237 |
|
---|
| 238 | if (RelCluNr >= This->PreviousRelCluNr){
|
---|
| 239 | CurCluNr = This->PreviousRelCluNr;
|
---|
| 240 | AbsCluNr = This->PreviousAbsCluNr;
|
---|
| 241 | } else {
|
---|
| 242 | CurCluNr = 0;
|
---|
| 243 | AbsCluNr = This->FirstAbsCluNr;
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 |
|
---|
| 247 | NrClu = (offset + *len - 1) / clus_size;
|
---|
| 248 | while (CurCluNr <= RelCluNr + NrClu){
|
---|
| 249 | if (CurCluNr == RelCluNr){
|
---|
| 250 | /* we have reached the beginning of our zone. Save
|
---|
| 251 | * coordinates */
|
---|
| 252 | This->PreviousRelCluNr = RelCluNr;
|
---|
| 253 | This->PreviousAbsCluNr = AbsCluNr;
|
---|
| 254 | }
|
---|
| 255 | NewCluNr = fatDecode(This->Fs, AbsCluNr);
|
---|
| 256 | if (NewCluNr == 1 || NewCluNr == 0){
|
---|
| 257 | fprintf(stderr,"Fat problem while decoding %d %x\n",
|
---|
| 258 | AbsCluNr, NewCluNr);
|
---|
| 259 | exit(1);
|
---|
| 260 | }
|
---|
| 261 | if(CurCluNr == RelCluNr + NrClu)
|
---|
| 262 | break;
|
---|
| 263 | if (NewCluNr > Fs->last_fat && mode == MT_WRITE){
|
---|
| 264 | /* if at end, and writing, extend it */
|
---|
| 265 | NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr);
|
---|
| 266 | if (NewCluNr == 1 ){ /* no more space */
|
---|
| 267 | errno = ENOSPC;
|
---|
| 268 | return -2;
|
---|
| 269 | }
|
---|
| 270 | fatAppend(This->Fs, AbsCluNr, NewCluNr);
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat){
|
---|
| 274 | *len = 0;
|
---|
| 275 | return 0;
|
---|
| 276 | }
|
---|
| 277 |
|
---|
| 278 | if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
|
---|
| 279 | break;
|
---|
| 280 | CurCluNr++;
|
---|
| 281 | AbsCluNr = NewCluNr;
|
---|
| 282 | if(loopDetect(This, CurCluNr, AbsCluNr)) {
|
---|
| 283 | errno = EIO;
|
---|
| 284 | return -2;
|
---|
| 285 | }
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
|
---|
| 289 |
|
---|
| 290 | end = where + *len;
|
---|
| 291 | if(batchmode && mode == MT_WRITE && end >= This->FileSize) {
|
---|
| 292 | *len += ROUND_UP(end, clus_size) - end;
|
---|
| 293 | }
|
---|
| 294 |
|
---|
| 295 | if((*len + offset) / clus_size + This->PreviousAbsCluNr-2 >
|
---|
| 296 | Fs->num_clus) {
|
---|
| 297 | fprintf(stderr, "cluster too big\n");
|
---|
| 298 | exit(1);
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | *res = sectorsToBytes((Stream_t*)Fs,
|
---|
| 302 | (This->PreviousAbsCluNr-2) * Fs->cluster_size +
|
---|
| 303 | Fs->clus_start) + offset;
|
---|
| 304 | return 1;
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 |
|
---|
| 308 | static int root_map(File_t *This, off_t where, size_t *len, int mode,
|
---|
| 309 | mt_off_t *res)
|
---|
| 310 | {
|
---|
| 311 | Fs_t *Fs = This->Fs;
|
---|
| 312 |
|
---|
| 313 | if(Fs->dir_len * Fs->sector_size < where) {
|
---|
| 314 | *len = 0;
|
---|
| 315 | errno = ENOSPC;
|
---|
| 316 | return -2;
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | maximize(*len, Fs->dir_len * Fs->sector_size - where);
|
---|
| 320 | if (*len == 0)
|
---|
| 321 | return 0;
|
---|
| 322 |
|
---|
| 323 | *res = sectorsToBytes((Stream_t*)Fs, Fs->dir_start) + where;
|
---|
| 324 | return 1;
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 |
|
---|
| 328 | static int read_file(Stream_t *Stream, char *buf, mt_off_t iwhere,
|
---|
| 329 | size_t len)
|
---|
| 330 | {
|
---|
| 331 | DeclareThis(File_t);
|
---|
| 332 | mt_off_t pos;
|
---|
| 333 | int err;
|
---|
| 334 | off_t where = truncBytes32(iwhere);
|
---|
| 335 |
|
---|
| 336 | Stream_t *Disk = This->Fs->Next;
|
---|
| 337 |
|
---|
| 338 | err = This->map(This, where, &len, MT_READ, &pos);
|
---|
| 339 | if(err <= 0)
|
---|
| 340 | return err;
|
---|
| 341 | return READS(Disk, buf, pos, len);
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | static int write_file(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len)
|
---|
| 345 | {
|
---|
| 346 | DeclareThis(File_t);
|
---|
| 347 | mt_off_t pos;
|
---|
| 348 | int ret;
|
---|
| 349 | size_t requestedLen;
|
---|
| 350 | Stream_t *Disk = This->Fs->Next;
|
---|
| 351 | off_t where = truncBytes32(iwhere);
|
---|
| 352 | int err;
|
---|
| 353 |
|
---|
| 354 | requestedLen = len;
|
---|
| 355 | err = This->map(This, where, &len, MT_WRITE, &pos);
|
---|
| 356 | if( err <= 0)
|
---|
| 357 | return err;
|
---|
| 358 | if(batchmode)
|
---|
| 359 | ret = force_write(Disk, buf, pos, len);
|
---|
| 360 | else
|
---|
| 361 | ret = WRITES(Disk, buf, pos, len);
|
---|
| 362 | if(ret > requestedLen)
|
---|
| 363 | ret = requestedLen;
|
---|
| 364 | if (ret > 0 && where + ret > This->FileSize )
|
---|
| 365 | This->FileSize = where + ret;
|
---|
| 366 | recalcPreallocSize(This);
|
---|
| 367 | return ret;
|
---|
| 368 | }
|
---|
| 369 |
|
---|
| 370 |
|
---|
| 371 | /*
|
---|
| 372 | * Convert an MSDOS time & date stamp to the Unix time() format
|
---|
| 373 | */
|
---|
| 374 |
|
---|
| 375 | static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
|
---|
| 376 | 0, 0, 0 };
|
---|
| 377 | static inline time_t conv_stamp(struct directory *dir)
|
---|
| 378 | {
|
---|
| 379 | struct tm *tmbuf;
|
---|
| 380 | long tzone, dst;
|
---|
| 381 | time_t accum, tmp;
|
---|
| 382 |
|
---|
| 383 | accum = DOS_YEAR(dir) - 1970; /* years past */
|
---|
| 384 |
|
---|
| 385 | /* days passed */
|
---|
| 386 | accum = accum * 365L + month[DOS_MONTH(dir)-1] + DOS_DAY(dir);
|
---|
| 387 |
|
---|
| 388 | /* leap years */
|
---|
| 389 | accum += (DOS_YEAR(dir) - 1972) / 4L;
|
---|
| 390 |
|
---|
| 391 | /* back off 1 day if before 29 Feb */
|
---|
| 392 | if (!(DOS_YEAR(dir) % 4) && DOS_MONTH(dir) < 3)
|
---|
| 393 | accum--;
|
---|
| 394 | accum = accum * 24L + DOS_HOUR(dir); /* hours passed */
|
---|
| 395 | accum = accum * 60L + DOS_MINUTE(dir); /* minutes passed */
|
---|
| 396 | accum = accum * 60L + DOS_SEC(dir); /* seconds passed */
|
---|
| 397 |
|
---|
| 398 | #ifndef OS_Minix
|
---|
| 399 | /* correct for Time Zone */
|
---|
| 400 | #ifdef HAVE_GETTIMEOFDAY
|
---|
| 401 | {
|
---|
| 402 | struct timeval tv;
|
---|
| 403 | struct timezone tz;
|
---|
| 404 |
|
---|
| 405 | gettimeofday(&tv, &tz);
|
---|
| 406 | tzone = tz.tz_minuteswest * 60L;
|
---|
| 407 | }
|
---|
| 408 | #else
|
---|
| 409 | #ifdef HAVE_TZSET
|
---|
| 410 | {
|
---|
| 411 | #ifndef OS_ultrix
|
---|
| 412 | /* Ultrix defines this to be a different type */
|
---|
| 413 | extern long timezone;
|
---|
| 414 | #endif
|
---|
| 415 | tzset();
|
---|
| 416 | tzone = (long) timezone;
|
---|
| 417 | }
|
---|
| 418 | #else
|
---|
| 419 | tzone = 0;
|
---|
| 420 | #endif /* HAVE_TZSET */
|
---|
| 421 | #endif /* HAVE_GETTIMEOFDAY */
|
---|
| 422 |
|
---|
| 423 | accum += tzone;
|
---|
| 424 | #endif /* OS_Minix */
|
---|
| 425 |
|
---|
| 426 | /* correct for Daylight Saving Time */
|
---|
| 427 | tmp = accum;
|
---|
| 428 | tmbuf = localtime(&tmp);
|
---|
| 429 | #ifndef OS_Minix
|
---|
| 430 | dst = (tmbuf->tm_isdst) ? (-60L * 60L) : 0L;
|
---|
| 431 | accum += dst;
|
---|
| 432 | #endif
|
---|
| 433 |
|
---|
| 434 | return accum;
|
---|
| 435 | }
|
---|
| 436 |
|
---|
| 437 |
|
---|
| 438 | static int get_file_data(Stream_t *Stream, time_t *date, mt_size_t *size,
|
---|
| 439 | int *type, int *address)
|
---|
| 440 | {
|
---|
| 441 | DeclareThis(File_t);
|
---|
| 442 |
|
---|
| 443 | if(date)
|
---|
| 444 | *date = conv_stamp(& This->direntry.dir);
|
---|
| 445 | if(size)
|
---|
| 446 | *size = (mt_size_t) This->FileSize;
|
---|
| 447 | if(type)
|
---|
| 448 | *type = This->direntry.dir.attr & ATTR_DIR;
|
---|
| 449 | if(address)
|
---|
| 450 | *address = This->FirstAbsCluNr;
|
---|
| 451 | return 0;
|
---|
| 452 | }
|
---|
| 453 |
|
---|
| 454 |
|
---|
| 455 | static int free_file(Stream_t *Stream)
|
---|
| 456 | {
|
---|
| 457 | DeclareThis(File_t);
|
---|
| 458 | Fs_t *Fs = This->Fs;
|
---|
| 459 | fsPreallocateClusters(Fs, -This->preallocatedClusters);
|
---|
| 460 | FREE(&This->direntry.Dir);
|
---|
| 461 | freeDirCache(Stream);
|
---|
| 462 | return hash_remove(filehash, (void *) Stream, This->hint);
|
---|
| 463 | }
|
---|
| 464 |
|
---|
| 465 |
|
---|
| 466 | static int flush_file(Stream_t *Stream)
|
---|
| 467 | {
|
---|
| 468 | DeclareThis(File_t);
|
---|
| 469 | direntry_t *entry = &This->direntry;
|
---|
| 470 |
|
---|
| 471 | if(isRootDir(Stream)) {
|
---|
| 472 | return 0;
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | if(This->FirstAbsCluNr != getStart(entry->Dir, &entry->dir)) {
|
---|
| 476 | set_word(entry->dir.start, This->FirstAbsCluNr & 0xffff);
|
---|
| 477 | set_word(entry->dir.startHi, This->FirstAbsCluNr >> 16);
|
---|
| 478 | dir_write(entry);
|
---|
| 479 | }
|
---|
| 480 | return 0;
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 |
|
---|
| 484 | static int pre_allocate_file(Stream_t *Stream, mt_size_t isize)
|
---|
| 485 | {
|
---|
| 486 | DeclareThis(File_t);
|
---|
| 487 |
|
---|
| 488 | size_t size = truncBytes32(isize);
|
---|
| 489 |
|
---|
| 490 | if(size > This->FileSize &&
|
---|
| 491 | size > This->preallocatedSize) {
|
---|
| 492 | This->preallocatedSize = size;
|
---|
| 493 | return recalcPreallocSize(This);
|
---|
| 494 | } else
|
---|
| 495 | return 0;
|
---|
| 496 | }
|
---|
| 497 |
|
---|
| 498 | static Class_t FileClass = {
|
---|
| 499 | read_file,
|
---|
| 500 | write_file,
|
---|
| 501 | flush_file, /* flush */
|
---|
| 502 | free_file, /* free */
|
---|
| 503 | 0, /* get_geom */
|
---|
| 504 | get_file_data,
|
---|
| 505 | pre_allocate_file
|
---|
| 506 | };
|
---|
| 507 |
|
---|
| 508 | static unsigned int getAbsCluNr(File_t *This)
|
---|
| 509 | {
|
---|
| 510 | if(This->FirstAbsCluNr)
|
---|
| 511 | return This->FirstAbsCluNr;
|
---|
| 512 | if(isRootDir((Stream_t *) This))
|
---|
| 513 | return 0;
|
---|
| 514 | return 1;
|
---|
| 515 | }
|
---|
| 516 |
|
---|
| 517 | static unsigned int func1(void *Stream)
|
---|
| 518 | {
|
---|
| 519 | DeclareThis(File_t);
|
---|
| 520 |
|
---|
| 521 | return getAbsCluNr(This) ^ (long) This->Fs;
|
---|
| 522 | }
|
---|
| 523 |
|
---|
| 524 | static unsigned int func2(void *Stream)
|
---|
| 525 | {
|
---|
| 526 | DeclareThis(File_t);
|
---|
| 527 |
|
---|
| 528 | return getAbsCluNr(This);
|
---|
| 529 | }
|
---|
| 530 |
|
---|
| 531 | static int comp(void *Stream, void *Stream2)
|
---|
| 532 | {
|
---|
| 533 | DeclareThis(File_t);
|
---|
| 534 |
|
---|
| 535 | File_t *This2 = (File_t *) Stream2;
|
---|
| 536 |
|
---|
| 537 | return This->Fs != This2->Fs ||
|
---|
| 538 | getAbsCluNr(This) != getAbsCluNr(This2);
|
---|
| 539 | }
|
---|
| 540 |
|
---|
| 541 | static void init_hash(void)
|
---|
| 542 | {
|
---|
| 543 | static int is_initialised=0;
|
---|
| 544 |
|
---|
| 545 | if(!is_initialised){
|
---|
| 546 | make_ht(func1, func2, comp, 20, &filehash);
|
---|
| 547 | is_initialised = 1;
|
---|
| 548 | }
|
---|
| 549 | }
|
---|
| 550 |
|
---|
| 551 |
|
---|
| 552 | static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
|
---|
| 553 | size_t size, direntry_t *entry)
|
---|
| 554 | {
|
---|
| 555 | Stream_t *Stream = GetFs(Dir);
|
---|
| 556 | DeclareThis(Fs_t);
|
---|
| 557 | File_t Pattern;
|
---|
| 558 | File_t *File;
|
---|
| 559 |
|
---|
| 560 | init_hash();
|
---|
| 561 | This->refs++;
|
---|
| 562 |
|
---|
| 563 | if(first != 1){
|
---|
| 564 | /* we use the illegal cluster 1 to mark newly created files.
|
---|
| 565 | * do not manage those by hashtable */
|
---|
| 566 | Pattern.Fs = This;
|
---|
| 567 | Pattern.Class = &FileClass;
|
---|
| 568 | if(first || (entry && !IS_DIR(entry)))
|
---|
| 569 | Pattern.map = normal_map;
|
---|
| 570 | else
|
---|
| 571 | Pattern.map = root_map;
|
---|
| 572 | Pattern.FirstAbsCluNr = first;
|
---|
| 573 | Pattern.loopDetectRel = 0;
|
---|
| 574 | Pattern.loopDetectAbs = first;
|
---|
| 575 | if(!hash_lookup(filehash, (T_HashTableEl) &Pattern,
|
---|
| 576 | (T_HashTableEl **)&File, 0)){
|
---|
| 577 | File->refs++;
|
---|
| 578 | This->refs--;
|
---|
| 579 | return (Stream_t *) File;
|
---|
| 580 | }
|
---|
| 581 | }
|
---|
| 582 |
|
---|
| 583 | File = New(File_t);
|
---|
| 584 | if (!File)
|
---|
| 585 | return NULL;
|
---|
| 586 | File->dcp = 0;
|
---|
| 587 | File->preallocatedClusters = 0;
|
---|
| 588 | File->preallocatedSize = 0;
|
---|
| 589 | /* memorize dir for date and attrib */
|
---|
| 590 | File->direntry = *entry;
|
---|
| 591 | if(entry->entry == -3)
|
---|
| 592 | File->direntry.Dir = (Stream_t *) File; /* root directory */
|
---|
| 593 | else
|
---|
| 594 | COPY(File->direntry.Dir);
|
---|
| 595 |
|
---|
| 596 | File->Class = &FileClass;
|
---|
| 597 | File->Fs = This;
|
---|
| 598 | if(first || (entry && !IS_DIR(entry)))
|
---|
| 599 | File->map = normal_map;
|
---|
| 600 | else
|
---|
| 601 | File->map = root_map; /* FAT 12/16 root directory */
|
---|
| 602 | if(first == 1)
|
---|
| 603 | File->FirstAbsCluNr = 0;
|
---|
| 604 | else
|
---|
| 605 | File->FirstAbsCluNr = first;
|
---|
| 606 |
|
---|
| 607 | File->loopDetectRel = 0;
|
---|
| 608 | File->loopDetectAbs = 0;
|
---|
| 609 |
|
---|
| 610 | File->PreviousRelCluNr = 0xffff;
|
---|
| 611 | File->FileSize = size;
|
---|
| 612 | File->refs = 1;
|
---|
| 613 | File->Buffer = 0;
|
---|
| 614 | hash_add(filehash, (void *) File, &File->hint);
|
---|
| 615 | return (Stream_t *) File;
|
---|
| 616 | }
|
---|
| 617 |
|
---|
| 618 | Stream_t *OpenRoot(Stream_t *Dir)
|
---|
| 619 | {
|
---|
| 620 | unsigned int num;
|
---|
| 621 | direntry_t entry;
|
---|
| 622 | size_t size;
|
---|
| 623 | Stream_t *file;
|
---|
| 624 |
|
---|
| 625 | memset(&entry, 0, sizeof(direntry_t));
|
---|
| 626 |
|
---|
| 627 | num = fat32RootCluster(Dir);
|
---|
| 628 |
|
---|
| 629 | /* make the directory entry */
|
---|
| 630 | entry.entry = -3;
|
---|
| 631 | entry.name[0] = '\0';
|
---|
| 632 | mk_entry("/", ATTR_DIR, num, 0, 0, &entry.dir);
|
---|
| 633 |
|
---|
| 634 | if(num)
|
---|
| 635 | size = countBytes(Dir, num);
|
---|
| 636 | else {
|
---|
| 637 | Fs_t *Fs = (Fs_t *) GetFs(Dir);
|
---|
| 638 | size = Fs->dir_len * Fs->sector_size;
|
---|
| 639 | }
|
---|
| 640 | file = _internalFileOpen(Dir, num, size, &entry);
|
---|
| 641 | bufferize(&file);
|
---|
| 642 | return file;
|
---|
| 643 | }
|
---|
| 644 |
|
---|
| 645 |
|
---|
| 646 | Stream_t *OpenFileByDirentry(direntry_t *entry)
|
---|
| 647 | {
|
---|
| 648 | Stream_t *file;
|
---|
| 649 | unsigned int first;
|
---|
| 650 | size_t size;
|
---|
| 651 |
|
---|
| 652 | first = getStart(entry->Dir, &entry->dir);
|
---|
| 653 |
|
---|
| 654 | if(!first && IS_DIR(entry))
|
---|
| 655 | return OpenRoot(entry->Dir);
|
---|
| 656 | if (IS_DIR(entry))
|
---|
| 657 | size = countBytes(entry->Dir, first);
|
---|
| 658 | else
|
---|
| 659 | size = FILE_SIZE(&entry->dir);
|
---|
| 660 | file = _internalFileOpen(entry->Dir, first, size, entry);
|
---|
| 661 | if(IS_DIR(entry)) {
|
---|
| 662 | bufferize(&file);
|
---|
| 663 | if(first == 1)
|
---|
| 664 | dir_grow(file, 0);
|
---|
| 665 | }
|
---|
| 666 |
|
---|
| 667 | return file;
|
---|
| 668 | }
|
---|
| 669 |
|
---|
| 670 |
|
---|
| 671 | int isRootDir(Stream_t *Stream)
|
---|
| 672 | {
|
---|
| 673 | File_t *This = getUnbufferedFile(Stream);
|
---|
| 674 |
|
---|
| 675 | return This->map == root_map;
|
---|
| 676 | }
|
---|