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