Tizen 2.1 base
[platform/upstream/hplip.git] / pcard / fat.c
1 /*****************************************************************************\
2
3   fat.c - FAT12/FAT16 file system
4
5   (c) 2004 Copyright Hewlett-Packard Development Company, LP
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20
21   Author: David Suffield
22
23   Caveats:
24   1. Global variables are used, therefore this library is not thread safe.
25   2. Current implementation has not been tested on a big-edian system. 
26
27 \*****************************************************************************/
28
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <netinet/in.h>
35 #include "fat.h"
36
37 /*
38  * Private data structures.
39  */
40
41 typedef struct
42 {
43    int FatStartSector;
44    int DataStartSector;
45    int RootDirStartSector;
46    int RootDirNumSectors;
47    uint8_t *Fat;            /* cached FAT16 data */
48    int FatSize;             /* in bytes */
49    uint8_t *Fat12;         /* FAT12 backup */
50    int Fat12Size;         /* in bytes */
51    uint8_t *Fat16;         /* FAT16 backup */
52    int WriteProtect;     /* 0=false, 1=true */
53 } DISK_ATTRIBUTES;
54
55 typedef struct
56 {
57    char Name[16];
58    int StartCluster;    /* equals zero if root */
59    int StartSector;
60    int CurrSector;
61    int NumEntries;
62 } CURRENT_WORKING_DIRECTORY;
63
64 typedef struct
65 {
66    char Name[16];
67    char Attr;
68    int StartCluster;
69    int CurrCluster;
70    int Size;
71    int CurrSector;
72    int CurrSectorNumInCluster;
73    int CurrByte;
74    int DirSectorNum;
75    int DirEntryNum;           /* number in current directory */
76 } CURRENT_FILE_ATTRIBUTES;
77
78 typedef struct
79 {
80    char JumpInstruction[3];      /* offset = 0 */
81    char OEMID[8];                   /* 3 */
82    uint16_t BytesPerSector;         /* 11 */
83    uint8_t SectorsPerCluster;       /* 13 */
84    uint16_t ReservedSectors;        /* 14 */
85    uint8_t Fats;          /* number of copies of the fat, 16 */
86    uint16_t RootEntries;           /* 17 */
87    uint16_t SmallSectors;          /* 19 */
88    uint8_t Media;                  /* 21 */
89    uint16_t SectorsPerFat;         /* 22 */
90    uint16_t SectorsPerTrack;       /* 24 */
91    uint16_t Heads;                 /* 26 */
92    uint32_t HiddenSectors;         /* 28 */
93    uint32_t LargeSectors;  /* number of sector if SmallSectors == 0, 32 */
94    uint8_t DriveNumber;     /* 36 */
95    uint8_t CurrentHead;     /* 37 */
96    uint8_t Signature;       /* 38 */
97    uint32_t ID;             /* random serial number, 39 */
98    uint8_t VolumeLabel[11]; /* 43 */
99    uint8_t SystemID[8];     /* 54 */
100    uint8_t LoadInstructions[512-64];
101    uint16_t EndSignature; /*=AA55h*/
102 } __attribute__((packed)) FAT_BOOT_SECTOR;       /* 512 bytes total */
103
104 typedef struct
105 {
106    char Name[8],Ext[3];    /* name and extension */
107    uint8_t Attributes;     /* attribute bits */
108    uint8_t Reserved[10];
109    uint16_t Time; 
110    uint16_t Date; 
111    uint16_t StartCluster;       
112    uint32_t Size;      /* size of the file in bytes */ 
113 } __attribute__((packed)) FAT_DIRECTORY;      /* 32 bytes total */
114
115 #define FAT_IS_DIR 0x10
116 #define FAT_END_OF_DIR 0x2
117 #define FAT_FILE_DELETED 0xe5
118 #define FAT_LONG_FILENAME 0x3
119
120 /* 
121  * Private data objects. Note, static variables are initialized to zero.
122  */
123 static FAT_BOOT_SECTOR bpb;   /* bios parameter block */
124 static DISK_ATTRIBUTES da;   
125 static CURRENT_WORKING_DIRECTORY cwd;
126 static CURRENT_FILE_ATTRIBUTES fa;
127
128 /* Convert 12-bit FAT to 16-bit FAT. */
129 int ConvertFat12to16(uint8_t *dest, uint8_t *src, int maxentry)
130 {
131    uint8_t *p12 = src;
132    uint16_t *p16 = (uint16_t *)dest;
133    int i;
134
135    for (i=0; i<maxentry; i++)
136    {
137       if (i&1)
138       {
139          *p16++ = lit2hs(*(uint16_t *)p12 >> 4);  /* odd fat entry */
140          p12+=2;      
141       }
142       else
143       {
144          *p16++ = lit2hs(*(uint16_t *)p12 & 0xfff);  /* even fat entry */
145          p12++;      
146       }
147    }
148    return 0;
149 }
150
151 /* Convert 16-bit FAT to 12-bit FAT. */
152 int ConvertFat16to12(uint8_t *dest, uint8_t *src, int maxentry)
153 {
154    uint8_t *p12 = dest;
155    uint16_t *p16 = (uint16_t *)src;
156    int i;
157
158    for (i=0; i<maxentry; i++, p16++)
159    {
160       if (i&1)
161       {
162          *p12 = (uint8_t)h2lits(*p16 >> 4);  /* odd fat entry */
163          p12++;      
164       }
165       else
166       {
167          *(uint16_t *)p12 = h2lits(*p16 | (*(p16+1) << 12));  /* even fat entry */
168          p12+=2;
169       }
170    }
171    return 0;
172 }
173
174 int readsect(int sector, int nsector, void *buf, int size)
175 {
176    int len=nsector, total=0;
177    int i, n, stat=1;
178
179    /* Read 1-blksize sectors. */
180    for (i=0; i<nsector; i+=n, len-=n)
181    {
182       n = len > FAT_BLKSIZE ? FAT_BLKSIZE : len;
183       if (ReadSector(sector+i, n, buf+total, size-total) != 0)
184          goto bugout;
185       total += n*FAT_HARDSECT;
186    }
187
188    stat = 0;
189
190 bugout:
191   return stat;
192 };
193
194 int writesect(int sector, int nsector, void *buf, int size)
195 {
196    int len=nsector, total=0;
197    int i, n, stat=1;
198
199    /* Write 1-blksize sectors. */
200    for (i=0; i<nsector; i+=n, len-=n)
201    {
202       n = len > FAT_BLKSIZE ? FAT_BLKSIZE : len;
203       if (WriteSector(sector+i, n, buf+total, size-total) != 0)
204          goto bugout;
205       total += n*FAT_HARDSECT;
206    }
207
208    stat = 0;
209
210 bugout:
211   return stat;
212 };
213
214 int UpdateFat(void)
215 {
216    int stat=1;
217    uint8_t *p12=NULL;
218    int i, total=0;
219
220    if (strcmp((char *)bpb.SystemID, "FAT12") == 0)
221    {
222       if ((p12 = malloc(da.Fat12Size)) == NULL)
223          goto bugout;
224       ConvertFat16to12(p12, da.Fat, da.Fat12Size/1.5);
225       for (i=0; i<bpb.SectorsPerFat; i++)
226       {
227          if (memcmp(p12+total, da.Fat12+total, FAT_HARDSECT) != 0)
228             if (writesect(da.FatStartSector+i, 1, p12+total, FAT_HARDSECT) != 0)
229                goto bugout;
230          total += FAT_HARDSECT;
231       }
232    }
233    else
234    {  /* Assume FAT16 */
235       for (i=0; i<bpb.SectorsPerFat; i++)
236       {
237          if (memcmp(da.Fat+total, da.Fat16+total, FAT_HARDSECT) != 0)
238             if (writesect(da.FatStartSector+i, 1, da.Fat+total, FAT_HARDSECT) != 0)
239                goto bugout;
240          total += FAT_HARDSECT;
241       }
242    }
243
244    stat = 0;
245
246 bugout:
247
248    if (p12 != NULL)
249       free(p12);
250
251    return stat;
252 };
253
254 int RootSetCWD(void)
255 {
256    /* Set CWD to the root directory. */
257    cwd.Name[0] = '/'; cwd.Name[1] = 0;
258    cwd.StartSector = da.RootDirStartSector;
259    cwd.CurrSector = cwd.StartSector;
260    cwd.NumEntries = bpb.RootEntries;
261    cwd.StartCluster = 0;
262     
263     return 0;
264 };
265
266 /* Returns number of free clusters (ie: those with 0x0 as the value in FAT). */
267 int FindFreeClusters(void)
268 {
269    int16_t *pfat = (int16_t *)da.Fat;
270    int max_entry = da.FatSize/2;
271    int i, freeclusters=0;
272
273    for (i=0; i<max_entry; i++)
274    {
275       if (*pfat++ == 0x0)
276          freeclusters++;
277    }
278    return freeclusters;
279 }
280
281 int ConvertClusterToSector(int cluster) 
282 {
283    int sector;
284
285    sector = cluster - 2;            /* skip first two FAT entries */
286    sector *= bpb.SectorsPerCluster; /* find the sector number relative to data start */
287    sector += da.DataStartSector;    /* find absolute sector number */
288
289    return sector;
290 }
291
292 /* Get next cluster from FAT. */
293 int GetNextCluster(int cluster) 
294 {
295    uint16_t *pfat = (uint16_t *)da.Fat;
296    return *(pfat+cluster);
297 }
298
299 /* Tries to load the directory entry specified by filenumber. */
300 int LoadFileInCWD(int filenumber) 
301 {
302    uint8_t buf[FAT_HARDSECT];
303    FAT_DIRECTORY *pde = (FAT_DIRECTORY *)buf;
304    int i, j, fn, sector, cluster, cluster_cnt;
305    int de_per_sector = FAT_HARDSECT/sizeof(FAT_DIRECTORY);
306    uint8_t c;
307
308    sector = filenumber / de_per_sector;        /* determine sector offset */
309    fn = filenumber - (sector * de_per_sector);   /* determine file number in sector */
310
311    if (cwd.StartCluster)
312    {  /* CWD = subdirectory */
313
314       /* Determine cluster */
315       cluster = cwd.StartCluster;
316       cluster_cnt = sector / bpb.SectorsPerCluster;
317       for (i=0; i<cluster_cnt && cluster<0xfff7 && cluster; i++)
318          cluster = GetNextCluster(cluster);
319       if (cluster >= 0xfff7 || cluster == 0)
320          return FAT_END_OF_DIR;
321       cwd.CurrSector = ConvertClusterToSector(cluster);
322       sector -= cluster_cnt * bpb.SectorsPerCluster;
323    }
324    else
325    {  /* CWD = root */
326       cwd.CurrSector = cwd.StartSector;
327
328       /* Check for max entry. */
329       if (filenumber > (da.RootDirNumSectors * de_per_sector))
330          return FAT_END_OF_DIR;
331    }
332
333    cwd.CurrSector += sector;
334
335    fa.DirEntryNum = fn;
336    fa.DirSectorNum = cwd.CurrSector;
337
338    /* Read the directory sector. */
339    pde += fn;
340    pde->Name[0] = 0;
341    readsect(fa.DirSectorNum, 1, buf, sizeof(buf));
342
343    c = pde->Name[0];
344    if (c == 0x0)
345       return FAT_END_OF_DIR;
346    if (c == FAT_FILE_DELETED)
347       return FAT_FILE_DELETED;
348
349    /* Read file information from directory and convert to 8.3 format. */
350    for (i=0; (i<sizeof(pde->Name)) && pde->Name[i] && (pde->Name[i] != ' '); i++)  /* copy charactors up to any space */
351       fa.Name[i] = pde->Name[i];
352    if (pde->Ext[0] && (pde->Ext[0] != ' '))
353    {
354       fa.Name[i++] = '.';
355       for (j=0; (pde->Ext[j] != ' ') && (j<sizeof(pde->Ext)); j++, i++)  /* copy charactors up to space */
356          fa.Name[i] = pde->Ext[j];
357    }
358    
359    fa.Name[i] = 0;  /* zero terminate */
360
361    fa.Attr =  pde->Attributes;
362
363    if (pde->Attributes == 0xf) 
364    { 
365       return FAT_LONG_FILENAME;   /* ignor long filename (slot) directory entries */ 
366    }
367
368    fa.StartCluster = pde->StartCluster;
369    fa.CurrCluster = fa.StartCluster;
370    fa.Size = pde->Size;
371    fa.CurrSectorNumInCluster = 0;
372
373    return 0;
374 }
375
376 /* Look in CWD for file with given name. */
377 int LoadFileWithName(char *filename)
378 {
379    int filenum;
380    int ret, stat=1;
381
382    filenum = 0;
383
384    while (1)
385    {
386       ret = LoadFileInCWD(filenum);
387       if (ret == FAT_END_OF_DIR) 
388          break;
389
390       if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
391       {
392          if (strcasecmp(fa.Name, filename) == 0)
393          {
394             stat = 0; /* found file */
395             break;            
396          }
397       }
398       filenum++;
399    }
400    return stat;
401 }
402
403 void PrintCurrFileInfo(void)
404 {
405    fprintf(stdout, "%s   %d bytes (cluster %d, sector %d)", fa.Name, fa.Size,
406                 fa.StartCluster, ConvertClusterToSector(fa.StartCluster));
407    if (fa.Attr & FAT_IS_DIR)
408       fputs(" <DIR>\n", stdout);
409    else
410       fputc('\n', stdout);
411 }
412
413 /* Get the FAT boot sector and root directory. */
414 int FatInit(void)
415 {
416    int bootsector_startsector, stat=1, fatsize;
417    char dummy[FAT_HARDSECT];
418
419    if (da.Fat != NULL)
420       free(da.Fat);
421    if (da.Fat12 != NULL)
422       free(da.Fat12);
423    da.Fat = NULL;
424    da.Fat12 = NULL;
425
426    /* Assume no MBR and boot sector starts at first sector. */
427    bootsector_startsector = 0;
428
429    /* Read boot sector. */
430    /*fprintf( stdout, "start=%d", bootsector_startsector );*/
431    if (readsect(bootsector_startsector, 1, &bpb, sizeof(bpb)) != 0)
432       goto bugout;
433
434    /* TODO: take care big-endian byte ordering in bpb. */
435
436    if (bpb.BytesPerSector != FAT_HARDSECT)
437       goto bugout;
438    
439    bpb.SystemID[5] = 0;
440
441    if (verbose > 0)
442    {
443       fprintf(stderr, "bytes/sectors=%d\n", bpb.BytesPerSector);
444       fprintf(stderr, "sectors/cluster=%d\n", bpb.SectorsPerCluster);
445       fprintf(stderr, "reserved sectors=%d\n", bpb.ReservedSectors);
446       fprintf(stderr, "sectors/FAT=%d\n", bpb.SectorsPerFat);
447       fprintf(stderr, "root entries=%d\n", bpb.RootEntries);
448       fprintf(stderr, "small sectors=%d\n", bpb.SmallSectors);
449       fprintf(stderr, "large sectors=%d\n", bpb.LargeSectors);
450       fprintf(stderr, "system id=%s\n", bpb.SystemID);   
451    }
452
453    /* Calculate where the fat and root directory are. */
454    da.FatStartSector = bootsector_startsector + bpb.ReservedSectors;
455    da.RootDirNumSectors = ((bpb.RootEntries * 32) + (bpb.BytesPerSector - 1)) / bpb.BytesPerSector;
456    da.RootDirStartSector = da.FatStartSector + ((int16_t)bpb.Fats * (int16_t)bpb.SectorsPerFat);
457    da.DataStartSector = da.RootDirStartSector + da.RootDirNumSectors;
458
459    RootSetCWD();
460    fatsize = bpb.SectorsPerFat * FAT_HARDSECT;
461    
462    if (strcmp((char *)bpb.SystemID, "FAT12") == 0)
463    {
464       da.Fat12Size = fatsize;
465       if ((da.Fat12 = (uint8_t *)malloc(da.Fat12Size)) == NULL)
466          goto bugout;
467       if (readsect(da.FatStartSector, bpb.SectorsPerFat, da.Fat12, da.Fat12Size) != 0)
468          goto bugout;
469       da.FatSize = da.Fat12Size/1.5*2;
470       if ((da.Fat = (uint8_t *)malloc(da.FatSize)) == NULL)
471          goto bugout; 
472       ConvertFat12to16(da.Fat, da.Fat12, da.Fat12Size/1.5);
473    }
474    else
475    {
476       da.FatSize = fatsize;
477       if ((da.Fat16 = (uint8_t *)malloc(da.FatSize)) == NULL)
478          goto bugout; 
479       if (readsect(da.FatStartSector, bpb.SectorsPerFat, da.Fat16, da.FatSize) != 0)
480          goto bugout;
481       if ((da.Fat = (uint8_t *)malloc(da.FatSize)) == NULL)
482          goto bugout; 
483       memcpy(da.Fat, da.Fat16, da.FatSize);
484    }
485
486    if (verbose > 0)
487    {
488       fprintf(stderr, "FAT start sector=%d\n", da.FatStartSector);
489       fprintf(stderr, "root start sector=%d\n", da.RootDirStartSector);
490       fprintf(stderr, "root number of sectors=%d\n", da.RootDirNumSectors);
491       fprintf(stderr, "data start sector=%d\n", da.DataStartSector);
492    }
493
494    /* Check for write protected disk. Try read/write to last sector in root directory. */
495    da.WriteProtect = 1;
496    if (readsect(da.RootDirStartSector+da.RootDirNumSectors-1, 1, dummy, sizeof(dummy)) == 0)
497       if (writesect(da.RootDirStartSector+da.RootDirNumSectors-1, 1, dummy, sizeof(dummy)) == 0)
498          da.WriteProtect = 0;
499
500    stat = 0;
501
502 bugout:
503    if (stat != 0)
504    {
505       if (da.Fat != NULL)
506          free(da.Fat);
507       if (da.Fat12 != NULL)
508          free(da.Fat12);
509       if (da.Fat16 != NULL)
510          free(da.Fat16);
511    }
512    return stat;
513 }
514
515 int FatFreeSpace(void)
516 {
517    return(FindFreeClusters() * bpb.SectorsPerCluster * FAT_HARDSECT);
518 }
519
520 int FatDiskAttributes( PHOTO_CARD_ATTRIBUTES * pa )
521 {
522     strncpy( pa->OEMID, bpb.OEMID, 8 );
523     pa->BytesPerSector = bpb.BytesPerSector;
524     pa->SectorsPerCluster = bpb.SectorsPerCluster;
525     pa->ReservedSectors = bpb.ReservedSectors;
526     pa->SectorsPerFat = bpb.SectorsPerFat;
527     pa->RootEntries = bpb.RootEntries;
528     strncpy( pa->SystemID, (char *)bpb.SystemID, 8 );
529     strncpy( pa->VolumeLabel, (char *)bpb.VolumeLabel, 11 );
530     pa->WriteProtect = da.WriteProtect;
531     
532     return 0;
533 }
534
535 /*  Prints out all entries in the current directory to stdout. */
536 int FatListDir(void) 
537 {
538    int ret, filenum;
539    int freespace;
540
541    if (verbose > 0)
542    {
543       freespace = FatFreeSpace();
544       fprintf(stdout, "Free Space=%d bytes\n", freespace);
545    }
546
547    filenum = 0;
548    while (1) 
549    {
550       ret = LoadFileInCWD(filenum);
551       if (ret == FAT_END_OF_DIR) 
552       {
553          fputs("<EOD>\n", stdout);
554          break;
555       }
556       if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
557          PrintCurrFileInfo();
558       filenum++;
559    }
560    return 0;
561 }
562
563 /*   Directory List "Iterator": Begin,  Next... */
564 static int fatdir_filenum = 0;
565
566 int FatDirBegin(FILE_ATTRIBUTES *a)
567 {
568     fatdir_filenum = 0;
569     return FatDirNext( a );
570 }
571
572 int FatDirNext(FILE_ATTRIBUTES *a)
573 {
574     int ret;
575     ret = LoadFileInCWD( fatdir_filenum );
576
577     if( ret == FAT_END_OF_DIR )
578         return 0;
579     
580     if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
581     {
582         strcpy( a->Name, fa.Name );
583         
584         if( fa.Attr == FAT_IS_DIR )
585             a->Attr = 'd';
586         else
587             a->Attr = ' ';
588         
589         a->Size = fa.Size;
590     }
591     else
592     {
593         strcpy( a->Name, "" );
594         a->Attr = 'x';
595         a->Size = 0;    
596     }
597     
598     fatdir_filenum++;
599     return 1;
600 }
601
602 /* Dump FAT file to the output file (fd). */
603 int FatReadFile(char *name, int fd)
604 {
605    uint8_t *buf=NULL;
606    int cluster, sector, old;
607    int block = bpb.SectorsPerCluster * FAT_HARDSECT;
608    int i, n, total = 0;
609
610    if (LoadFileWithName(name) != 0)
611       goto bugout;   /* file not found */
612
613    cluster = fa.StartCluster;
614    sector = ConvertClusterToSector(cluster);
615
616    if ((buf = malloc(block)) == NULL)
617    {
618        goto bugout;
619    }
620
621    for (i=0; i<fa.Size; i+=n)
622    {
623       if (readsect(sector, bpb.SectorsPerCluster, buf, block) != 0)   /* read full cluster */
624       {
625          total = -1;
626          goto bugout;
627       }
628
629       n = fa.Size-i > block ? block : fa.Size-i;
630       write(fd, buf, n);
631       
632       total += n;
633       old = cluster;
634       cluster = GetNextCluster(cluster);
635       if (cluster >= 0xfff7 || cluster == 0)
636          break;
637
638       sector = ConvertClusterToSector(cluster);
639    }
640
641 bugout:
642    if (buf != NULL)
643       free(buf);
644
645    return total;
646 }
647
648 /* Dump FAT file, given the "offset" in bytes and the "len" in bytes, to the output buffer. */
649 int FatReadFileExt(char *name, int offset, int len, void *outbuf)
650 {
651    uint8_t *buf=NULL;
652    int cluster, sector, old;
653    int block = bpb.SectorsPerCluster * FAT_HARDSECT;  /* cluster size in bytes */
654    int i, n, total = 0, btotal = 0;
655    int bn, boff, blen; 
656    int b1 = offset / block;   /* first cluster to read */ 
657    int b2 = (offset+len) / block;   /* last cluster to read */
658
659    if (LoadFileWithName(name) != 0)
660       goto bugout;   /* file not found */
661
662    cluster = fa.StartCluster;
663    sector = ConvertClusterToSector(cluster);
664
665    if ((buf = malloc(block)) == NULL)
666    {
667        goto bugout;
668    }
669
670    for (i=0, bn=0; i<fa.Size; i+=n, bn++)
671    {
672       /* Determine size in bytes to write for this cluster */
673       n = fa.Size-i > block ? block : fa.Size-i;  
674
675       /* Read/write data if it falls within "offset" and "len". */ 
676       if (bn >= b1)
677       {
678          if (readsect(sector, bpb.SectorsPerCluster, buf, block) != 0)   /* read full cluster */
679          {
680             total = -1;
681             goto bugout;
682          }
683
684          if (bn == b1)
685             boff = offset-total;    /* cluster overlaps "offset" */
686          else
687             boff = 0;         /* cluster is past "offset" */
688
689          if (bn <= b2)
690          {
691             if (bn == b2)
692                blen = (offset+len)-total-boff;    /* cluster overlaps "len" */
693             else
694                blen = n-boff;         /* cluster is within "len" */
695
696             memcpy(outbuf+btotal, buf+boff,  blen);
697             btotal += blen;
698          }       
699          else
700             break; /* done writing data */  
701       }
702
703       total += n;
704       old = cluster;
705       cluster = GetNextCluster(cluster);
706       if (cluster >= 0xfff7 || cluster == 0)
707          break;
708
709       sector = ConvertClusterToSector(cluster);
710    }
711
712 bugout:
713    if (buf != NULL)
714       free(buf);
715
716    return btotal;
717 }
718
719 /* Make dir current working directory. */
720 int FatSetCWD(char *dir) 
721 {
722    int ret;
723
724    if (dir[0] == '.')
725       return 0;
726
727    if (dir[0] == '/')
728    {
729       RootSetCWD();
730       return 0;
731    }
732
733    if (strcmp(cwd.Name, dir) == 0)
734       return 0;
735  
736    ret = LoadFileWithName(dir);
737    if (ret != 0)
738       return ret;
739
740    if (!(fa.Attr & FAT_IS_DIR))
741       return 1;
742
743    strncpy(cwd.Name, fa.Name, sizeof(cwd.Name));
744    cwd.StartSector = ConvertClusterToSector(fa.StartCluster);
745    cwd.CurrSector = cwd.StartSector;
746    cwd.StartCluster = fa.StartCluster;
747    return 0;
748 }
749
750 int FatDeleteFile(char *name) 
751 {
752    uint8_t buf[FAT_HARDSECT];
753    uint16_t *pfat = (uint16_t *)da.Fat;
754    int cluster, next_cluster, filenum, stat=1;
755
756    if (LoadFileWithName(name) != 0)
757       goto bugout;
758
759    cluster = fa.StartCluster;
760
761    /* Free all fat cluster entries for specified file. */
762    while ((cluster <= 0xFFF8) && (cluster != 0x0000)) 
763    {
764       next_cluster = *(pfat+cluster);   /* get next cluster */
765       *(pfat+cluster) = 0;         /* free current cluster */
766       cluster = next_cluster;
767    }
768
769    /* Remove directory entry for specified file. */
770    readsect(fa.DirSectorNum, 1, buf, sizeof(buf));
771    filenum = fa.DirEntryNum & 0x000F;
772    filenum <<= 4;
773    buf[filenum * 2] = FAT_FILE_DELETED;
774    if (writesect(fa.DirSectorNum, 1, buf, sizeof(buf)) != 0)
775       goto bugout;
776
777    /* Write updated fat to disk. */
778    if (UpdateFat() != 0)
779       goto bugout;
780
781    stat = 0;
782
783 bugout:
784    return stat;
785 }
786
787
788
789