source: trunk/minix/commands/i386/mtools-3.9.7/file.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 13.9 KB
Line 
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
10typedef 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
39static Class_t FileClass;
40T_HashTable *filehash;
41
42static File_t *getUnbufferedFile(Stream_t *Stream)
43{
44 while(Stream->Class != &FileClass)
45 Stream = Stream->Next;
46 return (File_t *) Stream;
47}
48
49Fs_t *getFs(Stream_t *Stream)
50{
51 return getUnbufferedFile(Stream)->Fs;
52}
53
54struct dirCache_t **getDirCacheP(Stream_t *Stream)
55{
56 return &getUnbufferedFile(Stream)->dcp;
57}
58
59direntry_t *getDirentry(Stream_t *Stream)
60{
61 return &getUnbufferedFile(Stream)->direntry;
62}
63
64
65static 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
95static 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
112static int loopDetect(File_t *This, unsigned int rel, unsigned int abs)
113{
114 return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, abs);
115}
116
117static 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
136unsigned 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 */
147static 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
156void 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
198static 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
308static 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
328static 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
344static 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
375static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
376 0, 0, 0 };
377static 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
438static 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
455static 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
466static 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
484static 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
498static 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
508static 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
517static unsigned int func1(void *Stream)
518{
519 DeclareThis(File_t);
520
521 return getAbsCluNr(This) ^ (long) This->Fs;
522}
523
524static unsigned int func2(void *Stream)
525{
526 DeclareThis(File_t);
527
528 return getAbsCluNr(This);
529}
530
531static 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
541static 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
552static 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
618Stream_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
646Stream_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
671int isRootDir(Stream_t *Stream)
672{
673 File_t *This = getUnbufferedFile(Stream);
674
675 return This->map == root_map;
676}
Note: See TracBrowser for help on using the repository browser.