xref: /aosp_15_r20/external/mtools/file.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
1 /*  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"
26 #include "buffer.h"
27 
28 typedef struct File_t {
29 	struct Stream_t head;
30 
31 	struct Stream_t *Buffer;
32 
33 	int (*map)(struct File_t *this, uint32_t where, uint32_t *len, int mode,
34 			   mt_off_t *res);
35 	uint32_t FileSize;
36 
37 	/* 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;
45 
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;
60 
61 	uint32_t where;
62 } File_t;
63 
64 static Class_t FileClass;
65 static T_HashTable *filehash;
66 
getUnbufferedFile(Stream_t * Stream)67 static File_t *getUnbufferedFile(Stream_t *Stream)
68 {
69 	while(Stream->Class != &FileClass)
70 		Stream = Stream->Next;
71 	return (File_t *) Stream;
72 }
73 
_getFs(File_t * File)74 static inline Fs_t *_getFs(File_t *File)
75 {
76 	return (Fs_t *) File->head.Next;
77 }
78 
getFs(Stream_t * Stream)79 Fs_t *getFs(Stream_t *Stream)
80 {
81 	return (Fs_t *)getUnbufferedFile(Stream)->head.Next;
82 }
83 
getDirCacheP(Stream_t * Stream)84 struct dirCache_t **getDirCacheP(Stream_t *Stream)
85 {
86 	return &getUnbufferedFile(Stream)->dcp;
87 }
88 
getDirentry(Stream_t * Stream)89 direntry_t *getDirentry(Stream_t *Stream)
90 {
91 	return &getUnbufferedFile(Stream)->direntry;
92 }
93 
94 /**
95  * Overflow-safe conversion of bytes to cluster
96  */
filebytesToClusters(uint32_t bytes,uint32_t clus_size)97 static 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 }
103 
recalcPreallocSize(File_t * This)104 static int recalcPreallocSize(File_t *This)
105 {
106 	uint32_t currentClusters, neededClusters;
107 	unsigned int clus_size;
108 	uint32_t neededPrealloc;
109 	Fs_t *Fs = _getFs(This);
110 
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;
121 	currentClusters = filebytesToClusters(This->FileSize, clus_size);
122 	neededClusters = filebytesToClusters(This->preallocatedSize, clus_size);
123 	if(neededClusters < currentClusters)
124 		neededPrealloc = 0;
125 	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 	}
136 	This->preallocatedClusters = neededPrealloc;
137 	return 0;
138 }
139 
_loopDetect(unsigned int * oldrel,unsigned int rel,unsigned int * oldabs,unsigned int absol)140 static 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 
loopDetect(File_t * This,unsigned int rel,unsigned int absol)157 static int loopDetect(File_t *This, unsigned int rel, unsigned int absol)
158 {
159 	return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, absol);
160 }
161 
_countBlocks(Fs_t * This,unsigned int block)162 static unsigned int _countBlocks(Fs_t *This, unsigned int block)
163 {
164 	unsigned int blocks;
165 	unsigned int rel, oldabs, oldrel;
166 
167 	blocks = 0;
168 
169 	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 
countBlocks(Stream_t * Dir,unsigned int block)181 unsigned 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  */
countBytes(Stream_t * Dir,unsigned int block)192 static uint32_t countBytes(Stream_t *Dir, unsigned int block)
193 {
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 
printFat(Stream_t * Stream)201 void printFat(Stream_t *Stream)
202 {
203 	File_t *This = getUnbufferedFile(Stream);
204 	uint32_t n;
205 	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;
231 		n = fatDecode(_getFs(This), n);
232 		rel++;
233 		if(loopDetect(This, rel, n) < 0)
234 			n = 1;
235 	} while (n <= _getFs(This)->last_fat && n != 1);
236 	if(!first) {
237 		if (begin != end)
238 			printf("-%lu", end);
239 		printf(">");
240 	}
241 }
242 
printFatWithOffset(Stream_t * Stream,off_t offset)243 void printFatWithOffset(Stream_t *Stream, off_t offset) {
244 	File_t *This = getUnbufferedFile(Stream);
245 	uint32_t n;
246 	unsigned int rel;
247 	off_t clusSize;
248 
249 	n = This->FirstAbsCluNr;
250 	if(!n) {
251 		printf("Root directory or empty file\n");
252 		return;
253 	}
254 
255 	clusSize = _getFs(This)->cluster_size * _getFs(This)->sector_size;
256 
257 	rel = 0;
258 	while(offset >= clusSize) {
259 		n = fatDecode(_getFs(This), n);
260 		rel++;
261 		if(loopDetect(This, rel, n) < 0)
262 			return;
263 		if(n > _getFs(This)->last_fat)
264 			return;
265 		offset -= clusSize;
266 	}
267 
268 	printf("%lu", (unsigned long) n);
269 }
270 
normal_map(File_t * This,uint32_t where,uint32_t * len,int mode,mt_off_t * res)271 static int normal_map(File_t *This, uint32_t where, uint32_t *len, int mode,
272 		      mt_off_t *res)
273 {
274 	unsigned int offset;
275 	size_t end;
276 	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);
283 
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 		}
298 		NewCluNr = get_next_free_cluster(_getFs(This), 1);
299 		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);
306 		fatAllocate(_getFs(This), NewCluNr, Fs->end_fat);
307 	}
308 
309 	RelCluNr = where / clus_size;
310 
311 	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 		}
328 		NewCluNr = fatDecode(_getFs(This), AbsCluNr);
329 		if (NewCluNr == 1 || NewCluNr == 0){
330 			fprintf(stderr,"Fat problem while decoding %d %x\n",
331 				AbsCluNr, NewCluNr);
332 			exit(1);
333 		}
334 		if(CurCluNr == RelCluNr + NrClu)
335 			break;
336 		if (NewCluNr > Fs->last_fat && mode == MT_WRITE){
337 			/* if at end, and writing, extend it */
338 			NewCluNr = get_next_free_cluster(_getFs(This), AbsCluNr);
339 			if (NewCluNr == 1 ){ /* no more space */
340 				errno = ENOSPC;
341 				return -2;
342 			}
343 			fatAppend(_getFs(This), AbsCluNr, NewCluNr);
344 		}
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);
362 
363 	end = where + *len;
364 	if(batchmode &&
365 	   mode == MT_WRITE &&
366 	   end >= This->FileSize) {
367 		/* 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. */
370 		*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 
379 	*res = sectorsToBytes(Fs,
380 			      (This->PreviousAbsCluNr-2) * Fs->cluster_size +
381 			      Fs->clus_start) + to_mt_off_t(offset);
382 	return 1;
383 }
384 
385 
root_map(File_t * This,uint32_t where,uint32_t * len,int mode UNUSEDP,mt_off_t * res)386 static int root_map(File_t *This, uint32_t where, uint32_t *len,
387 		    int mode UNUSEDP,  mt_off_t *res)
388 {
389 	Fs_t *Fs = _getFs(This);
390 
391 	if(Fs->dir_len * Fs->sector_size < where) {
392 		*len = 0;
393 		errno = ENOSPC;
394 		return -2;
395 	}
396 
397 	maximize(*len, Fs->dir_len * Fs->sector_size - where);
398         if (*len == 0)
399             return 0;
400 
401 	*res = sectorsToBytes(Fs, Fs->dir_start) +
402 		to_mt_off_t(where);
403 	return 1;
404 }
405 
read_file(Stream_t * Stream,char * buf,size_t ilen)406 static ssize_t read_file(Stream_t *Stream, char *buf, size_t ilen)
407 {
408 	DeclareThis(File_t);
409 	mt_off_t pos;
410 	int err;
411 	uint32_t len = truncSizeTo32u(ilen);
412 	ssize_t ret;
413 
414 	Stream_t *Disk = _getFs(This)->head.Next;
415 
416 	err = This->map(This, This->where, &len, MT_READ, &pos);
417 	if(err <= 0)
418 		return err;
419 	ret = PREADS(Disk, buf, pos, len);
420 	if(ret < 0)
421 		return ret;
422 	This->where += (size_t) ret;
423 	return ret;
424 }
425 
write_file(Stream_t * Stream,char * buf,size_t ilen)426 static 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 
pread_file(Stream_t * Stream,char * buf,mt_off_t where,size_t ilen)468 static 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 
pwrite_file(Stream_t * Stream,char * buf,mt_off_t where,size_t ilen)475 static 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 }
481 
482 /*
483  * Convert an MSDOS time & date stamp to the Unix time() format
484  */
485 
486 static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
487 					  0, 0, 0 };
conv_stamp(struct directory * dir)488 static __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;
514 
515 		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 
get_file_data(Stream_t * Stream,time_t * date,mt_off_t * size,int * type,uint32_t * address)546 static int get_file_data(Stream_t *Stream, time_t *date, mt_off_t *size,
547 			 int *type, uint32_t *address)
548 {
549 	DeclareThis(File_t);
550 
551 	if(date)
552 		*date = conv_stamp(& This->direntry.dir);
553 	if(size)
554 		*size = to_mt_off_t(This->FileSize);
555 	if(type)
556 		*type = This->direntry.dir.attr & ATTR_DIR;
557 	if(address)
558 		*address = This->FirstAbsCluNr;
559 	return 0;
560 }
561 
562 
free_file(Stream_t * Stream)563 static int free_file(Stream_t *Stream)
564 {
565 	DeclareThis(File_t);
566 	Fs_t *Fs = _getFs(This);
567 	fsReleasePreallocateClusters(Fs, This->preallocatedClusters);
568 	FREE(&This->direntry.Dir);
569 	freeDirCache(Stream);
570 	return hash_remove(filehash, (void *) Stream, This->hint);
571 }
572 
573 
flush_file(Stream_t * Stream)574 static 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 
pre_allocate_file(Stream_t * Stream,mt_off_t isize)592 static int pre_allocate_file(Stream_t *Stream, mt_off_t isize)
593 {
594 	DeclareThis(File_t);
595 
596 	uint32_t size = truncMtOffTo32u(isize);
597 
598 	if(size > This->FileSize &&
599 	   size > This->preallocatedSize) {
600 		This->preallocatedSize = size;
601 		return recalcPreallocSize(This);
602 	} else
603 		return 0;
604 }
605 
606 static Class_t FileClass = {
607 	read_file,
608 	write_file,
609 	pread_file,
610 	pwrite_file,
611 	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 
getAbsCluNr(File_t * This)620 static 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 
func1(void * Stream)629 static uint32_t func1(void *Stream)
630 {
631 	DeclareThis(File_t);
632 
633 	return getAbsCluNr(This) ^ (uint32_t) (unsigned long) This->head.Next;
634 }
635 
func2(void * Stream)636 static uint32_t func2(void *Stream)
637 {
638 	DeclareThis(File_t);
639 
640 	return getAbsCluNr(This);
641 }
642 
comp(void * Stream,void * Stream2)643 static int comp(void *Stream, void *Stream2)
644 {
645 	DeclareThis(File_t);
646 
647 	File_t *This2 = (File_t *) Stream2;
648 
649 	return _getFs(This) != _getFs(This2) ||
650 		getAbsCluNr(This) != getAbsCluNr(This2);
651 }
652 
init_hash(void)653 static void init_hash(void)
654 {
655 	static int is_initialised=0;
656 
657 	if(!is_initialised){
658 		make_ht(func1, func2, comp, 20, &filehash);
659 		is_initialised = 1;
660 	}
661 }
662 
663 
_internalFileOpen(Stream_t * Dir,unsigned int first,uint32_t size,direntry_t * entry)664 static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first,
665 				   uint32_t size, direntry_t *entry)
666 {
667 	Stream_t *Stream = GetFs(Dir);
668 	DeclareThis(Fs_t);
669 	File_t Pattern;
670 	File_t *File;
671 
672 	init_hash();
673 	This->head.refs++;
674 
675 	if(first != 1){
676 		/* we use the illegal cluster 1 to mark newly created files.
677 		 * do not manage those by hashtable */
678 		init_head(&Pattern.head, &FileClass, &This->head);
679 		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)){
688 			File->head.refs++;
689 			This->head.refs--;
690 			return (Stream_t *) File;
691 		}
692 	}
693 
694 	File = New(File_t);
695 	if (!File)
696 		return NULL;
697 	init_head(&File->head, &FileClass, &This->head);
698 	File->Buffer = NULL;
699 	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);
708 	File->where = 0;
709 	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;
723 	hash_add(filehash, (void *) File, &File->hint);
724 	return (Stream_t *) File;
725 }
726 
bufferize(Stream_t ** Dir)727 static 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 
OpenRoot(Stream_t * Dir)753 Stream_t *OpenRoot(Stream_t *Dir)
754 {
755 	unsigned int num;
756 	direntry_t entry;
757 	uint32_t size;
758 	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 
OpenFileByDirentry(direntry_t * entry)781 Stream_t *OpenFileByDirentry(direntry_t *entry)
782 {
783 	Stream_t *file;
784 	unsigned int first;
785 	uint32_t size;
786 
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 
isRootDir(Stream_t * Stream)806 int isRootDir(Stream_t *Stream)
807 {
808 	File_t *This = getUnbufferedFile(Stream);
809 
810 	return This->map == root_map;
811 }
812