blob: 8165d4f432046e6989e25da78d5428da49b6db5c [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* Copyright 1996-1999,2001-2003,2007-2009,2011 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "sysincludes.h"
19#include "msdos.h"
20#include "stream.h"
21#include "mtools.h"
22#include "fsP.h"
23#include "file.h"
24#include "htable.h"
25#include "dirCache.h"
Yi Kong39bbd962022-01-09 19:41:38 +080026#include "buffer.h"
Alistair Delvabeaee832021-02-24 11:27:23 -080027
28typedef struct File_t {
Yi Kong39bbd962022-01-09 19:41:38 +080029 struct Stream_t head;
Alistair Delvabeaee832021-02-24 11:27:23 -080030
Yi Kong39bbd962022-01-09 19:41:38 +080031 struct Stream_t *Buffer;
32
33 int (*map)(struct File_t *this, uint32_t where, uint32_t *len, int mode,
Alistair Delvabeaee832021-02-24 11:27:23 -080034 mt_off_t *res);
Yi Kong39bbd962022-01-09 19:41:38 +080035 uint32_t FileSize;
Alistair Delvabeaee832021-02-24 11:27:23 -080036
Yi Kong39bbd962022-01-09 19:41:38 +080037 /* How many bytes do we project to need for this file
38 (includes those already in FileSize) */
39 uint32_t preallocatedSize;
40
41 /* How many clusters we have asked the lower layer to reserve
42 for us (only what we will need in the future, excluding already
43 allocated clusters in FileSize) */
44 uint32_t preallocatedClusters;
Alistair Delvabeaee832021-02-24 11:27:23 -080045
46 /* Absolute position of first cluster of file */
47 unsigned int FirstAbsCluNr;
48
49 /* Absolute position of previous cluster */
50 unsigned int PreviousAbsCluNr;
51
52 /* Relative position of previous cluster */
53 unsigned int PreviousRelCluNr;
54 direntry_t direntry;
55 size_t hint;
56 struct dirCache_t *dcp;
57
58 unsigned int loopDetectRel;
59 unsigned int loopDetectAbs;
Yi Kong39bbd962022-01-09 19:41:38 +080060
61 uint32_t where;
Alistair Delvabeaee832021-02-24 11:27:23 -080062} File_t;
63
64static Class_t FileClass;
65static T_HashTable *filehash;
66
67static File_t *getUnbufferedFile(Stream_t *Stream)
68{
69 while(Stream->Class != &FileClass)
70 Stream = Stream->Next;
71 return (File_t *) Stream;
72}
73
Yi Kong39bbd962022-01-09 19:41:38 +080074static inline Fs_t *_getFs(File_t *File)
75{
76 return (Fs_t *) File->head.Next;
77}
78
Alistair Delvabeaee832021-02-24 11:27:23 -080079Fs_t *getFs(Stream_t *Stream)
80{
Yi Kong39bbd962022-01-09 19:41:38 +080081 return (Fs_t *)getUnbufferedFile(Stream)->head.Next;
Alistair Delvabeaee832021-02-24 11:27:23 -080082}
83
84struct dirCache_t **getDirCacheP(Stream_t *Stream)
85{
86 return &getUnbufferedFile(Stream)->dcp;
87}
88
89direntry_t *getDirentry(Stream_t *Stream)
90{
91 return &getUnbufferedFile(Stream)->direntry;
92}
93
Yi Kong39bbd962022-01-09 19:41:38 +080094/**
95 * Overflow-safe conversion of bytes to cluster
96 */
97static uint32_t filebytesToClusters(uint32_t bytes, uint32_t clus_size) {
98 uint32_t ret = bytes / clus_size;
99 if(bytes % clus_size)
100 ret++;
101 return ret;
102}
Alistair Delvabeaee832021-02-24 11:27:23 -0800103
104static int recalcPreallocSize(File_t *This)
105{
Yi Kong39bbd962022-01-09 19:41:38 +0800106 uint32_t currentClusters, neededClusters;
Alistair Delvabeaee832021-02-24 11:27:23 -0800107 unsigned int clus_size;
Yi Kong39bbd962022-01-09 19:41:38 +0800108 uint32_t neededPrealloc;
109 Fs_t *Fs = _getFs(This);
Alistair Delvabeaee832021-02-24 11:27:23 -0800110
111#if 0
112 if(This->FileSize & 0xc0000000) {
113 fprintf(stderr, "Bad filesize\n");
114 }
115 if(This->preallocatedSize & 0xc0000000) {
116 fprintf(stderr, "Bad preallocated size %x\n",
117 (int) This->preallocatedSize);
118 }
119#endif
120 clus_size = Fs->cluster_size * Fs->sector_size;
Yi Kong39bbd962022-01-09 19:41:38 +0800121 currentClusters = filebytesToClusters(This->FileSize, clus_size);
122 neededClusters = filebytesToClusters(This->preallocatedSize, clus_size);
123 if(neededClusters < currentClusters)
Alistair Delvabeaee832021-02-24 11:27:23 -0800124 neededPrealloc = 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800125 else
126 neededPrealloc = neededClusters - currentClusters;
127 if(neededPrealloc > This->preallocatedClusters) {
128 int r = fsPreallocateClusters(Fs, neededPrealloc-
129 This->preallocatedClusters);
130 if(r)
131 return r;
132 } else {
133 fsReleasePreallocateClusters(Fs, This->preallocatedClusters -
134 neededPrealloc);
135 }
Alistair Delvabeaee832021-02-24 11:27:23 -0800136 This->preallocatedClusters = neededPrealloc;
137 return 0;
138}
139
140static int _loopDetect(unsigned int *oldrel, unsigned int rel,
141 unsigned int *oldabs, unsigned int absol)
142{
143 if(*oldrel && rel > *oldrel && absol == *oldabs) {
144 fprintf(stderr, "loop detected! oldrel=%d newrel=%d abs=%d\n",
145 *oldrel, rel, absol);
146 return -1;
147 }
148
149 if(rel >= 2 * *oldrel + 1) {
150 *oldrel = rel;
151 *oldabs = absol;
152 }
153 return 0;
154}
155
156
157static int loopDetect(File_t *This, unsigned int rel, unsigned int absol)
158{
159 return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, absol);
160}
161
162static unsigned int _countBlocks(Fs_t *This, unsigned int block)
163{
164 unsigned int blocks;
165 unsigned int rel, oldabs, oldrel;
166
167 blocks = 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800168
Alistair Delvabeaee832021-02-24 11:27:23 -0800169 oldabs = oldrel = rel = 0;
170
171 while (block <= This->last_fat && block != 1 && block) {
172 blocks++;
173 block = fatDecode(This, block);
174 rel++;
175 if(_loopDetect(&oldrel, rel, &oldabs, block) < 0)
176 block = 1;
177 }
178 return blocks;
179}
180
181unsigned int countBlocks(Stream_t *Dir, unsigned int block)
182{
183 Stream_t *Stream = GetFs(Dir);
184 DeclareThis(Fs_t);
185
186 return _countBlocks(This, block);
187}
188
189/* returns number of bytes in a directory. Represents a file size, and
190 * can hence be not bigger than 2^32
191 */
Yi Kong39bbd962022-01-09 19:41:38 +0800192static uint32_t countBytes(Stream_t *Dir, unsigned int block)
Alistair Delvabeaee832021-02-24 11:27:23 -0800193{
194 Stream_t *Stream = GetFs(Dir);
195 DeclareThis(Fs_t);
196
197 return _countBlocks(This, block) *
198 This->sector_size * This->cluster_size;
199}
200
201void printFat(Stream_t *Stream)
202{
203 File_t *This = getUnbufferedFile(Stream);
Yi Kong39bbd962022-01-09 19:41:38 +0800204 uint32_t n;
Alistair Delvabeaee832021-02-24 11:27:23 -0800205 unsigned int rel;
206 unsigned long begin, end;
207 int first;
208
209 n = This->FirstAbsCluNr;
210 if(!n) {
211 printf("Root directory or empty file\n");
212 return;
213 }
214
215 rel = 0;
216 first = 1;
217 begin = end = 0;
218 do {
219 if (first || n != end+1) {
220 if (!first) {
221 if (begin != end)
222 printf("-%lu", end);
223 printf("> ");
224 }
225 begin = end = n;
226 printf("<%lu", begin);
227 } else {
228 end++;
229 }
230 first = 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800231 n = fatDecode(_getFs(This), n);
Alistair Delvabeaee832021-02-24 11:27:23 -0800232 rel++;
233 if(loopDetect(This, rel, n) < 0)
234 n = 1;
Yi Kong39bbd962022-01-09 19:41:38 +0800235 } while (n <= _getFs(This)->last_fat && n != 1);
Alistair Delvabeaee832021-02-24 11:27:23 -0800236 if(!first) {
237 if (begin != end)
238 printf("-%lu", end);
239 printf(">");
240 }
241}
242
243void printFatWithOffset(Stream_t *Stream, off_t offset) {
244 File_t *This = getUnbufferedFile(Stream);
Yi Kong39bbd962022-01-09 19:41:38 +0800245 uint32_t n;
246 unsigned int rel;
Alistair Delvabeaee832021-02-24 11:27:23 -0800247 off_t clusSize;
248
249 n = This->FirstAbsCluNr;
250 if(!n) {
251 printf("Root directory or empty file\n");
252 return;
253 }
254
Yi Kong39bbd962022-01-09 19:41:38 +0800255 clusSize = _getFs(This)->cluster_size * _getFs(This)->sector_size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800256
257 rel = 0;
258 while(offset >= clusSize) {
Yi Kong39bbd962022-01-09 19:41:38 +0800259 n = fatDecode(_getFs(This), n);
Alistair Delvabeaee832021-02-24 11:27:23 -0800260 rel++;
261 if(loopDetect(This, rel, n) < 0)
262 return;
Yi Kong39bbd962022-01-09 19:41:38 +0800263 if(n > _getFs(This)->last_fat)
Alistair Delvabeaee832021-02-24 11:27:23 -0800264 return;
265 offset -= clusSize;
266 }
267
Yi Kong39bbd962022-01-09 19:41:38 +0800268 printf("%lu", (unsigned long) n);
Alistair Delvabeaee832021-02-24 11:27:23 -0800269}
270
Yi Kong39bbd962022-01-09 19:41:38 +0800271static int normal_map(File_t *This, uint32_t where, uint32_t *len, int mode,
272 mt_off_t *res)
Alistair Delvabeaee832021-02-24 11:27:23 -0800273{
274 unsigned int offset;
275 size_t end;
Yi Kong39bbd962022-01-09 19:41:38 +0800276 uint32_t NrClu; /* number of clusters to read */
277 uint32_t RelCluNr;
278 uint32_t CurCluNr;
279 uint32_t NewCluNr;
280 uint32_t AbsCluNr;
281 uint32_t clus_size;
282 Fs_t *Fs = _getFs(This);
Alistair Delvabeaee832021-02-24 11:27:23 -0800283
284 *res = 0;
285 clus_size = Fs->cluster_size * Fs->sector_size;
286 offset = where % clus_size;
287
288 if (mode == MT_READ)
289 maximize(*len, This->FileSize - where);
290 if (*len == 0 )
291 return 0;
292
293 if (This->FirstAbsCluNr < 2){
294 if( mode == MT_READ || *len == 0){
295 *len = 0;
296 return 0;
297 }
Yi Kong39bbd962022-01-09 19:41:38 +0800298 NewCluNr = get_next_free_cluster(_getFs(This), 1);
Alistair Delvabeaee832021-02-24 11:27:23 -0800299 if (NewCluNr == 1 ){
300 errno = ENOSPC;
301 return -2;
302 }
303 hash_remove(filehash, (void *) This, This->hint);
304 This->FirstAbsCluNr = NewCluNr;
305 hash_add(filehash, (void *) This, &This->hint);
Yi Kong39bbd962022-01-09 19:41:38 +0800306 fatAllocate(_getFs(This), NewCluNr, Fs->end_fat);
Alistair Delvabeaee832021-02-24 11:27:23 -0800307 }
308
309 RelCluNr = where / clus_size;
Yi Kong39bbd962022-01-09 19:41:38 +0800310
Alistair Delvabeaee832021-02-24 11:27:23 -0800311 if (RelCluNr >= This->PreviousRelCluNr){
312 CurCluNr = This->PreviousRelCluNr;
313 AbsCluNr = This->PreviousAbsCluNr;
314 } else {
315 CurCluNr = 0;
316 AbsCluNr = This->FirstAbsCluNr;
317 }
318
319
320 NrClu = (offset + *len - 1) / clus_size;
321 while (CurCluNr <= RelCluNr + NrClu){
322 if (CurCluNr == RelCluNr){
323 /* we have reached the beginning of our zone. Save
324 * coordinates */
325 This->PreviousRelCluNr = RelCluNr;
326 This->PreviousAbsCluNr = AbsCluNr;
327 }
Yi Kong39bbd962022-01-09 19:41:38 +0800328 NewCluNr = fatDecode(_getFs(This), AbsCluNr);
Alistair Delvabeaee832021-02-24 11:27:23 -0800329 if (NewCluNr == 1 || NewCluNr == 0){
330 fprintf(stderr,"Fat problem while decoding %d %x\n",
331 AbsCluNr, NewCluNr);
332 exit(1);
333 }
Yi Kong39bbd962022-01-09 19:41:38 +0800334 if(CurCluNr == RelCluNr + NrClu)
Alistair Delvabeaee832021-02-24 11:27:23 -0800335 break;
336 if (NewCluNr > Fs->last_fat && mode == MT_WRITE){
337 /* if at end, and writing, extend it */
Yi Kong39bbd962022-01-09 19:41:38 +0800338 NewCluNr = get_next_free_cluster(_getFs(This), AbsCluNr);
Alistair Delvabeaee832021-02-24 11:27:23 -0800339 if (NewCluNr == 1 ){ /* no more space */
340 errno = ENOSPC;
341 return -2;
342 }
Yi Kong39bbd962022-01-09 19:41:38 +0800343 fatAppend(_getFs(This), AbsCluNr, NewCluNr);
Alistair Delvabeaee832021-02-24 11:27:23 -0800344 }
345
346 if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat){
347 *len = 0;
348 return 0;
349 }
350
351 if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
352 break;
353 CurCluNr++;
354 AbsCluNr = NewCluNr;
355 if(loopDetect(This, CurCluNr, AbsCluNr)) {
356 errno = EIO;
357 return -2;
358 }
359 }
360
361 maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
Yi Kong39bbd962022-01-09 19:41:38 +0800362
Alistair Delvabeaee832021-02-24 11:27:23 -0800363 end = where + *len;
364 if(batchmode &&
365 mode == MT_WRITE &&
366 end >= This->FileSize) {
Yi Kong39bbd962022-01-09 19:41:38 +0800367 /* In batch mode, when writing at end of file, "pad"
368 * to nearest cluster boundary so that we don't have
369 * to read that data back from disk. */
Alistair Delvabeaee832021-02-24 11:27:23 -0800370 *len += ROUND_UP(end, clus_size) - end;
371 }
372
373 if((*len + offset) / clus_size + This->PreviousAbsCluNr-2 >
374 Fs->num_clus) {
375 fprintf(stderr, "cluster too big\n");
376 exit(1);
377 }
378
Yi Kong39bbd962022-01-09 19:41:38 +0800379 *res = sectorsToBytes(Fs,
380 (This->PreviousAbsCluNr-2) * Fs->cluster_size +
381 Fs->clus_start) + to_mt_off_t(offset);
Alistair Delvabeaee832021-02-24 11:27:23 -0800382 return 1;
383}
384
385
Yi Kong39bbd962022-01-09 19:41:38 +0800386static int root_map(File_t *This, uint32_t where, uint32_t *len,
387 int mode UNUSEDP, mt_off_t *res)
Alistair Delvabeaee832021-02-24 11:27:23 -0800388{
Yi Kong39bbd962022-01-09 19:41:38 +0800389 Fs_t *Fs = _getFs(This);
Alistair Delvabeaee832021-02-24 11:27:23 -0800390
Yi Kong39bbd962022-01-09 19:41:38 +0800391 if(Fs->dir_len * Fs->sector_size < where) {
Alistair Delvabeaee832021-02-24 11:27:23 -0800392 *len = 0;
393 errno = ENOSPC;
394 return -2;
395 }
396
Yi Kong39bbd962022-01-09 19:41:38 +0800397 maximize(*len, Fs->dir_len * Fs->sector_size - where);
Alistair Delvabeaee832021-02-24 11:27:23 -0800398 if (*len == 0)
399 return 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800400
401 *res = sectorsToBytes(Fs, Fs->dir_start) +
402 to_mt_off_t(where);
Alistair Delvabeaee832021-02-24 11:27:23 -0800403 return 1;
404}
Alistair Delvabeaee832021-02-24 11:27:23 -0800405
Yi Kong39bbd962022-01-09 19:41:38 +0800406static ssize_t read_file(Stream_t *Stream, char *buf, size_t ilen)
Alistair Delvabeaee832021-02-24 11:27:23 -0800407{
408 DeclareThis(File_t);
409 mt_off_t pos;
410 int err;
Yi Kong39bbd962022-01-09 19:41:38 +0800411 uint32_t len = truncSizeTo32u(ilen);
412 ssize_t ret;
Alistair Delvabeaee832021-02-24 11:27:23 -0800413
Yi Kong39bbd962022-01-09 19:41:38 +0800414 Stream_t *Disk = _getFs(This)->head.Next;
415
416 err = This->map(This, This->where, &len, MT_READ, &pos);
Alistair Delvabeaee832021-02-24 11:27:23 -0800417 if(err <= 0)
418 return err;
Yi Kong39bbd962022-01-09 19:41:38 +0800419 ret = PREADS(Disk, buf, pos, len);
420 if(ret < 0)
421 return ret;
422 This->where += (size_t) ret;
Alistair Delvabeaee832021-02-24 11:27:23 -0800423 return ret;
424}
425
Yi Kong39bbd962022-01-09 19:41:38 +0800426static ssize_t write_file(Stream_t *Stream, char *buf, size_t ilen)
427{
428 DeclareThis(File_t);
429 mt_off_t pos;
430 ssize_t ret;
431 uint32_t requestedLen;
432 uint32_t bytesWritten;
433 Stream_t *Disk = _getFs(This)->head.Next;
434 uint32_t maxLen = UINT32_MAX-This->where;
435 uint32_t len;
436 int err;
437
438 if(ilen > maxLen) {
439 len = maxLen;
440 } else
441 len = (uint32_t) ilen;
442 requestedLen = len;
443 err = This->map(This, This->where, &len, MT_WRITE, &pos);
444 if( err <= 0)
445 return err;
446 if(batchmode)
447 ret = force_pwrite(Disk, buf, pos, len);
448 else
449 ret = PWRITES(Disk, buf, pos, len);
450 if(ret < 0)
451 /* Error occured */
452 return ret;
453 if((uint32_t)ret > requestedLen)
454 /* More data than requested may be written to lower
455 * levels if batch mode is active, in order to "pad"
456 * the last cluster of a file, so that we don't have
457 * to read that back from disk */
458 bytesWritten = requestedLen;
459 else
460 bytesWritten = (uint32_t)ret;
461 This->where += bytesWritten;
462 if (This->where > This->FileSize )
463 This->FileSize = This->where;
464 recalcPreallocSize(This);
465 return (ssize_t) bytesWritten;
466}
467
468static ssize_t pread_file(Stream_t *Stream, char *buf, mt_off_t where,
469 size_t ilen) {
470 DeclareThis(File_t);
471 This->where = truncMtOffTo32u(where);
472 return read_file(Stream, buf, ilen);
473}
474
475static ssize_t pwrite_file(Stream_t *Stream, char *buf, mt_off_t where,
476 size_t ilen) {
477 DeclareThis(File_t);
478 This->where = truncMtOffTo32u(where);
479 return write_file(Stream, buf, ilen);
480}
Alistair Delvabeaee832021-02-24 11:27:23 -0800481
482/*
483 * Convert an MSDOS time & date stamp to the Unix time() format
484 */
485
486static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
487 0, 0, 0 };
488static __inline__ time_t conv_stamp(struct directory *dir)
489{
490 struct tm *tmbuf;
491 long tzone, dst;
492 time_t accum, tmp;
493
494 accum = DOS_YEAR(dir) - 1970; /* years past */
495
496 /* days passed */
497 accum = accum * 365L + month[DOS_MONTH(dir)-1] + DOS_DAY(dir);
498
499 /* leap years */
500 accum += (DOS_YEAR(dir) - 1972) / 4L;
501
502 /* back off 1 day if before 29 Feb */
503 if (!(DOS_YEAR(dir) % 4) && DOS_MONTH(dir) < 3)
504 accum--;
505 accum = accum * 24L + DOS_HOUR(dir); /* hours passed */
506 accum = accum * 60L + DOS_MINUTE(dir); /* minutes passed */
507 accum = accum * 60L + DOS_SEC(dir); /* seconds passed */
508
509 /* correct for Time Zone */
510#ifdef HAVE_GETTIMEOFDAY
511 {
512 struct timeval tv;
513 struct timezone tz;
Yi Kong39bbd962022-01-09 19:41:38 +0800514
Alistair Delvabeaee832021-02-24 11:27:23 -0800515 gettimeofday(&tv, &tz);
516 tzone = tz.tz_minuteswest * 60L;
517 }
518#else
519#if defined HAVE_TZSET && !defined OS_mingw32msvc
520 {
521#if !defined OS_ultrix && !defined OS_cygwin
522 /* Ultrix defines this to be a different type */
523 extern long timezone;
524#endif
525 tzset();
526 tzone = (long) timezone;
527 }
528#else
529 tzone = 0;
530#endif /* HAVE_TZSET */
531#endif /* HAVE_GETTIMEOFDAY */
532
533 accum += tzone;
534
535 /* correct for Daylight Saving Time */
536 tmp = accum;
537 tmbuf = localtime(&tmp);
538 if(tmbuf) {
539 dst = (tmbuf->tm_isdst) ? (-60L * 60L) : 0L;
540 accum += dst;
541 }
542 return accum;
543}
544
545
Yi Kong39bbd962022-01-09 19:41:38 +0800546static int get_file_data(Stream_t *Stream, time_t *date, mt_off_t *size,
547 int *type, uint32_t *address)
Alistair Delvabeaee832021-02-24 11:27:23 -0800548{
549 DeclareThis(File_t);
550
551 if(date)
552 *date = conv_stamp(& This->direntry.dir);
553 if(size)
Yi Kong39bbd962022-01-09 19:41:38 +0800554 *size = to_mt_off_t(This->FileSize);
Alistair Delvabeaee832021-02-24 11:27:23 -0800555 if(type)
556 *type = This->direntry.dir.attr & ATTR_DIR;
557 if(address)
558 *address = This->FirstAbsCluNr;
559 return 0;
560}
561
562
563static int free_file(Stream_t *Stream)
564{
565 DeclareThis(File_t);
Yi Kong39bbd962022-01-09 19:41:38 +0800566 Fs_t *Fs = _getFs(This);
567 fsReleasePreallocateClusters(Fs, This->preallocatedClusters);
Alistair Delvabeaee832021-02-24 11:27:23 -0800568 FREE(&This->direntry.Dir);
569 freeDirCache(Stream);
570 return hash_remove(filehash, (void *) Stream, This->hint);
571}
572
573
574static int flush_file(Stream_t *Stream)
575{
576 DeclareThis(File_t);
577 direntry_t *entry = &This->direntry;
578
579 if(isRootDir(Stream)) {
580 return 0;
581 }
582
583 if(This->FirstAbsCluNr != getStart(entry->Dir, &entry->dir)) {
584 set_word(entry->dir.start, This->FirstAbsCluNr & 0xffff);
585 set_word(entry->dir.startHi, This->FirstAbsCluNr >> 16);
586 dir_write(entry);
587 }
588 return 0;
589}
590
591
Yi Kong39bbd962022-01-09 19:41:38 +0800592static int pre_allocate_file(Stream_t *Stream, mt_off_t isize)
Alistair Delvabeaee832021-02-24 11:27:23 -0800593{
594 DeclareThis(File_t);
595
Yi Kong39bbd962022-01-09 19:41:38 +0800596 uint32_t size = truncMtOffTo32u(isize);
Alistair Delvabeaee832021-02-24 11:27:23 -0800597
598 if(size > This->FileSize &&
599 size > This->preallocatedSize) {
600 This->preallocatedSize = size;
601 return recalcPreallocSize(This);
602 } else
603 return 0;
604}
605
606static Class_t FileClass = {
607 read_file,
608 write_file,
Yi Kong39bbd962022-01-09 19:41:38 +0800609 pread_file,
610 pwrite_file,
Alistair Delvabeaee832021-02-24 11:27:23 -0800611 flush_file, /* flush */
612 free_file, /* free */
613 0, /* get_geom */
614 get_file_data,
615 pre_allocate_file,
616 get_dosConvert_pass_through,
617 0 /* discard */
618};
619
620static unsigned int getAbsCluNr(File_t *This)
621{
622 if(This->FirstAbsCluNr)
623 return This->FirstAbsCluNr;
624 if(isRootDir((Stream_t *) This))
625 return 0;
626 return 1;
627}
628
Yi Kong39bbd962022-01-09 19:41:38 +0800629static uint32_t func1(void *Stream)
Alistair Delvabeaee832021-02-24 11:27:23 -0800630{
631 DeclareThis(File_t);
632
Yi Kong39bbd962022-01-09 19:41:38 +0800633 return getAbsCluNr(This) ^ (uint32_t) (unsigned long) This->head.Next;
Alistair Delvabeaee832021-02-24 11:27:23 -0800634}
635
Yi Kong39bbd962022-01-09 19:41:38 +0800636static uint32_t func2(void *Stream)
Alistair Delvabeaee832021-02-24 11:27:23 -0800637{
638 DeclareThis(File_t);
639
640 return getAbsCluNr(This);
641}
642
643static int comp(void *Stream, void *Stream2)
644{
645 DeclareThis(File_t);
646
647 File_t *This2 = (File_t *) Stream2;
648
Yi Kong39bbd962022-01-09 19:41:38 +0800649 return _getFs(This) != _getFs(This2) ||
Alistair Delvabeaee832021-02-24 11:27:23 -0800650 getAbsCluNr(This) != getAbsCluNr(This2);
651}
652
653static void init_hash(void)
654{
655 static int is_initialised=0;
Yi Kong39bbd962022-01-09 19:41:38 +0800656
Alistair Delvabeaee832021-02-24 11:27:23 -0800657 if(!is_initialised){
658 make_ht(func1, func2, comp, 20, &filehash);
659 is_initialised = 1;
660 }
661}
662
663
664static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
Yi Kong39bbd962022-01-09 19:41:38 +0800665 uint32_t size, direntry_t *entry)
Alistair Delvabeaee832021-02-24 11:27:23 -0800666{
667 Stream_t *Stream = GetFs(Dir);
668 DeclareThis(Fs_t);
669 File_t Pattern;
670 File_t *File;
671
672 init_hash();
Yi Kong39bbd962022-01-09 19:41:38 +0800673 This->head.refs++;
Alistair Delvabeaee832021-02-24 11:27:23 -0800674
675 if(first != 1){
676 /* we use the illegal cluster 1 to mark newly created files.
677 * do not manage those by hashtable */
Yi Kong39bbd962022-01-09 19:41:38 +0800678 init_head(&Pattern.head, &FileClass, &This->head);
Alistair Delvabeaee832021-02-24 11:27:23 -0800679 if(first || (entry && !IS_DIR(entry)))
680 Pattern.map = normal_map;
681 else
682 Pattern.map = root_map;
683 Pattern.FirstAbsCluNr = first;
684 Pattern.loopDetectRel = 0;
685 Pattern.loopDetectAbs = first;
686 if(!hash_lookup(filehash, (T_HashTableEl) &Pattern,
687 (T_HashTableEl **)&File, 0)){
Yi Kong39bbd962022-01-09 19:41:38 +0800688 File->head.refs++;
689 This->head.refs--;
Alistair Delvabeaee832021-02-24 11:27:23 -0800690 return (Stream_t *) File;
691 }
692 }
693
694 File = New(File_t);
695 if (!File)
696 return NULL;
Yi Kong39bbd962022-01-09 19:41:38 +0800697 init_head(&File->head, &FileClass, &This->head);
698 File->Buffer = NULL;
Alistair Delvabeaee832021-02-24 11:27:23 -0800699 File->dcp = 0;
700 File->preallocatedClusters = 0;
701 File->preallocatedSize = 0;
702 /* memorize dir for date and attrib */
703 File->direntry = *entry;
704 if(entry->entry == -3)
705 File->direntry.Dir = (Stream_t *) File; /* root directory */
706 else
707 COPY(File->direntry.Dir);
Yi Kong39bbd962022-01-09 19:41:38 +0800708 File->where = 0;
Alistair Delvabeaee832021-02-24 11:27:23 -0800709 if(first || (entry && !IS_DIR(entry)))
710 File->map = normal_map;
711 else
712 File->map = root_map; /* FAT 12/16 root directory */
713 if(first == 1)
714 File->FirstAbsCluNr = 0;
715 else
716 File->FirstAbsCluNr = first;
717
718 File->loopDetectRel = 0;
719 File->loopDetectAbs = 0;
720
721 File->PreviousRelCluNr = 0xffff;
722 File->FileSize = size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800723 hash_add(filehash, (void *) File, &File->hint);
724 return (Stream_t *) File;
725}
726
Yi Kong39bbd962022-01-09 19:41:38 +0800727static void bufferize(Stream_t **Dir)
728{
729 Stream_t *BDir;
730 File_t *file = (File_t *) *Dir;
731
732 if(!*Dir)
733 return;
734
735 if(file->Buffer){
736 (*Dir)->refs--;
737 file->Buffer->refs++;
738 *Dir = file->Buffer;
739 return;
740 }
741
742 BDir = buf_init(*Dir, 64*16384, 512, MDIR_SIZE);
743 if(!BDir){
744 FREE(Dir);
745 *Dir = NULL;
746 } else {
747 file->Buffer = BDir;
748 *Dir = BDir;
749 }
750}
751
752
Alistair Delvabeaee832021-02-24 11:27:23 -0800753Stream_t *OpenRoot(Stream_t *Dir)
754{
755 unsigned int num;
756 direntry_t entry;
Yi Kong39bbd962022-01-09 19:41:38 +0800757 uint32_t size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800758 Stream_t *file;
759
760 memset(&entry, 0, sizeof(direntry_t));
761
762 num = fat32RootCluster(Dir);
763
764 /* make the directory entry */
765 entry.entry = -3;
766 entry.name[0] = '\0';
767 mk_entry_from_base("/", ATTR_DIR, num, 0, 0, &entry.dir);
768
769 if(num)
770 size = countBytes(Dir, num);
771 else {
772 Fs_t *Fs = (Fs_t *) GetFs(Dir);
773 size = Fs->dir_len * Fs->sector_size;
774 }
775 file = _internalFileOpen(Dir, num, size, &entry);
776 bufferize(&file);
777 return file;
778}
779
780
781Stream_t *OpenFileByDirentry(direntry_t *entry)
782{
783 Stream_t *file;
784 unsigned int first;
Yi Kong39bbd962022-01-09 19:41:38 +0800785 uint32_t size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800786
787 first = getStart(entry->Dir, &entry->dir);
788
789 if(!first && IS_DIR(entry))
790 return OpenRoot(entry->Dir);
791 if (IS_DIR(entry))
792 size = countBytes(entry->Dir, first);
793 else
794 size = FILE_SIZE(&entry->dir);
795 file = _internalFileOpen(entry->Dir, first, size, entry);
796 if(IS_DIR(entry)) {
797 bufferize(&file);
798 if(first == 1)
799 dir_grow(file, 0);
800 }
801
802 return file;
803}
804
805
806int isRootDir(Stream_t *Stream)
807{
808 File_t *This = getUnbufferedFile(Stream);
809
810 return This->map == root_map;
811}