LPCOpen Platform
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dosfs.c
Go to the documentation of this file.
1 /*
2  DOSFS Embedded FAT-Compatible Filesystem
3  (C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
4 
5  You are permitted to modify and/or use this code in your own projects without
6  payment of royalty, regardless of the license(s) you choose for those projects.
7 
8  You cannot re-copyright or restrict use of the code as released by Lewin Edwards.
9 */
10 
11 #include <string.h>
12 #include <stdlib.h>
13 
14 #include "dosfs.h"
15 
16 /*
17  Get starting sector# of specified partition on drive #unit
18  NOTE: This code ASSUMES an MBR on the disk.
19  scratchsector should point to a SECTOR_SIZE scratch area
20  Returns 0xffffffff for any error.
21  If pactive is non-NULL, this function also returns the partition active flag.
22  If pptype is non-NULL, this function also returns the partition type.
23  If psize is non-NULL, this function also returns the partition size.
24 */
25 uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize)
26 {
27  uint32_t result;
28  PMBR mbr = (PMBR) scratchsector;
29 
30  // DOS ptable supports maximum 4 partitions
31  if (pnum > 3)
32  return DFS_ERRMISC;
33 
34  // Read MBR from target media
35  if (DFS_ReadSector(unit,scratchsector,0,1)) {
36  return DFS_ERRMISC;
37  }
38 
39  result = (uint32_t) mbr->ptable[pnum].start_0 |
40  (((uint32_t) mbr->ptable[pnum].start_1) << 8) |
41  (((uint32_t) mbr->ptable[pnum].start_2) << 16) |
42  (((uint32_t) mbr->ptable[pnum].start_3) << 24);
43 
44  if (pactive)
45  *pactive = mbr->ptable[pnum].active;
46 
47  if (pptype)
48  *pptype = mbr->ptable[pnum].type;
49 
50  if (psize)
51  *psize = (uint32_t) mbr->ptable[pnum].size_0 |
52  (((uint32_t) mbr->ptable[pnum].size_1) << 8) |
53  (((uint32_t) mbr->ptable[pnum].size_2) << 16) |
54  (((uint32_t) mbr->ptable[pnum].size_3) << 24);
55 
56  return result;
57 }
58 
59 
60 /*
61  Retrieve volume info from BPB and store it in a VOLINFO structure
62  You must provide the unit and starting sector of the filesystem, and
63  a pointer to a sector buffer for scratch
64  Attempts to read BPB and glean information about the FS from that.
65  Returns 0 OK, nonzero for any error.
66 */
67 uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo)
68 {
69  PLBR lbr = (PLBR) scratchsector;
70  volinfo->unit = unit;
71  volinfo->startsector = startsector;
72 
73  if(DFS_ReadSector(unit,scratchsector,startsector,1))
74  return DFS_ERRMISC;
75 
76  /* Check for valid signature */
77  if (lbr->sig_55 != 0x55 || lbr->sig_aa != 0xaa) {
78  return DFS_ERRMISC;
79  }
80 // tag: OEMID, refer dosfs.h
81 // strncpy(volinfo->oemid, lbr->oemid, 8);
82 // volinfo->oemid[8] = 0;
83 
84  volinfo->secperclus = lbr->bpb.secperclus;
85  volinfo->reservedsecs = (uint16_t) lbr->bpb.reserved_l |
86  (((uint16_t) lbr->bpb.reserved_h) << 8);
87 
88  volinfo->numsecs = (uint16_t) lbr->bpb.sectors_s_l |
89  (((uint16_t) lbr->bpb.sectors_s_h) << 8);
90 
91  if (!volinfo->numsecs)
92  volinfo->numsecs = (uint32_t) lbr->bpb.sectors_l_0 |
93  (((uint32_t) lbr->bpb.sectors_l_1) << 8) |
94  (((uint32_t) lbr->bpb.sectors_l_2) << 16) |
95  (((uint32_t) lbr->bpb.sectors_l_3) << 24);
96 
97  // If secperfat is 0, we must be in a FAT32 volume; get secperfat
98  // from the FAT32 EBPB. The volume label and system ID string are also
99  // in different locations for FAT12/16 vs FAT32.
100  volinfo->secperfat = (uint16_t) lbr->bpb.secperfat_l |
101  (((uint16_t) lbr->bpb.secperfat_h) << 8);
102  if (!volinfo->secperfat) {
103  volinfo->secperfat = (uint32_t) lbr->ebpb.ebpb32.fatsize_0 |
104  (((uint32_t) lbr->ebpb.ebpb32.fatsize_1) << 8) |
105  (((uint32_t) lbr->ebpb.ebpb32.fatsize_2) << 16) |
106  (((uint32_t) lbr->ebpb.ebpb32.fatsize_3) << 24);
107 
108  memcpy(volinfo->label, lbr->ebpb.ebpb32.label, 11);
109  volinfo->label[11] = 0;
110 
111 // tag: OEMID, refer dosfs.h
112 // memcpy(volinfo->system, lbr->ebpb.ebpb32.system, 8);
113 // volinfo->system[8] = 0;
114  }
115  else {
116  memcpy(volinfo->label, lbr->ebpb.ebpb.label, 11);
117  volinfo->label[11] = 0;
118 
119 // tag: OEMID, refer dosfs.h
120 // memcpy(volinfo->system, lbr->ebpb.ebpb.system, 8);
121 // volinfo->system[8] = 0;
122  }
123 
124  // note: if rootentries is 0, we must be in a FAT32 volume.
125  volinfo->rootentries = (uint16_t) lbr->bpb.rootentries_l |
126  (((uint16_t) lbr->bpb.rootentries_h) << 8);
127 
128  // after extracting raw info we perform some useful precalculations
129  volinfo->fat1 = startsector + volinfo->reservedsecs;
130 
131  // The calculation below is designed to round up the root directory size for FAT12/16
132  // and to simply ignore the root directory for FAT32, since it's a normal, expandable
133  // file in that situation.
134  if (volinfo->rootentries) {
135  volinfo->rootdir = volinfo->fat1 + (volinfo->secperfat * lbr->bpb.numfats);
136  volinfo->dataarea = volinfo->rootdir + (((volinfo->rootentries * 32) + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
137  }
138  else {
139  volinfo->dataarea = volinfo->fat1 + (volinfo->secperfat * lbr->bpb.numfats);
140  volinfo->rootdir = (uint32_t) lbr->ebpb.ebpb32.root_0 |
141  (((uint32_t) lbr->ebpb.ebpb32.root_1) << 8) |
142  (((uint32_t) lbr->ebpb.ebpb32.root_2) << 16) |
143  (((uint32_t) lbr->ebpb.ebpb32.root_3) << 24);
144  }
145 
146  // Calculate number of clusters in data area and infer FAT type from this information.
147  volinfo->numclusters = (volinfo->numsecs - volinfo->dataarea) / volinfo->secperclus;
148  if (volinfo->numclusters < 4085)
149  volinfo->filesystem = FAT12;
150  else if (volinfo->numclusters < 65525)
151  volinfo->filesystem = FAT16;
152  else
153  volinfo->filesystem = FAT32;
154 
155  return DFS_OK;
156 }
157 
158 /*
159  Fetch FAT entry for specified cluster number
160  You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
161  Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
162  FAT entry.
163  scratchcache should point to a UINT32. This variable caches the physical sector number
164  last read into the scratch buffer for performance enhancement reasons.
165 */
166 uint32_t DFS_GetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster)
167 {
168  uint32_t offset, sector, result;
169 
170  if (volinfo->filesystem == FAT12) {
171  offset = cluster + (cluster / 2);
172  }
173  else if (volinfo->filesystem == FAT16) {
174  offset = cluster * 2;
175  }
176  else if (volinfo->filesystem == FAT32) {
177  offset = cluster * 4;
178  }
179  else
180  return 0x0ffffff7; // FAT32 bad cluster
181 
182  // at this point, offset is the BYTE offset of the desired sector from the start
183  // of the FAT. Calculate the physical sector containing this FAT entry.
184  sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
185 
186  // If this is not the same sector we last read, then read it into RAM
187  if (sector != *scratchcache) {
188  if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
189  // avoid anyone assuming that this cache value is still valid, which
190  // might cause disk corruption
191  *scratchcache = 0;
192  return 0x0ffffff7; // FAT32 bad cluster
193  }
194  *scratchcache = sector;
195  }
196 
197  // At this point, we "merely" need to extract the relevant entry.
198  // This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
199  // may span a sector boundary. The normal way around this is always to read two
200  // FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
201  offset = ldiv(offset, SECTOR_SIZE).rem;
202 
203  if (volinfo->filesystem == FAT12) {
204  // Special case for sector boundary - Store last byte of current sector.
205  // Then read in the next sector and put the first byte of that sector into
206  // the high byte of result.
207  if (offset == SECTOR_SIZE - 1) {
208  result = (uint32_t) scratch[offset];
209  sector++;
210  if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
211  // avoid anyone assuming that this cache value is still valid, which
212  // might cause disk corruption
213  *scratchcache = 0;
214  return 0x0ffffff7; // FAT32 bad cluster
215  }
216  *scratchcache = sector;
217  // Thanks to Claudio Leonel for pointing out this missing line.
218  result |= ((uint32_t) scratch[0]) << 8;
219  }
220  else {
221  result = (uint32_t) scratch[offset] |
222  ((uint32_t) scratch[offset+1]) << 8;
223  }
224  if (cluster & 1)
225  result = result >> 4;
226  else
227  result = result & 0xfff;
228  }
229  else if (volinfo->filesystem == FAT16) {
230  result = (uint32_t) scratch[offset] |
231  ((uint32_t) scratch[offset+1]) << 8;
232  }
233  else if (volinfo->filesystem == FAT32) {
234  result = ((uint32_t) scratch[offset] |
235  ((uint32_t) scratch[offset+1]) << 8 |
236  ((uint32_t) scratch[offset+2]) << 16 |
237  ((uint32_t) scratch[offset+3]) << 24) & 0x0fffffff;
238  }
239  else
240  result = 0x0ffffff7; // FAT32 bad cluster
241  return result;
242 }
243 
244 
245 /*
246  Set FAT entry for specified cluster number
247  You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
248  Returns DFS_ERRMISC for any error, otherwise DFS_OK
249  scratchcache should point to a UINT32. This variable caches the physical sector number
250  last read into the scratch buffer for performance enhancement reasons.
251 
252  NOTE: This code is HIGHLY WRITE-INEFFICIENT, particularly for flash media. Considerable
253  performance gains can be realized by caching the sector. However this is difficult to
254  achieve on FAT12 without requiring 2 sector buffers of scratch space, and it is a design
255  requirement of this code to operate on a single 512-byte scratch.
256 
257  If you are operating DOSFS over flash, you are strongly advised to implement a writeback
258  cache in your physical I/O driver. This will speed up your code significantly and will
259  also conserve power and flash write life.
260 */
261 uint32_t DFS_SetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster, uint32_t new_contents)
262 {
263  uint32_t offset, sector, result;
264  if (volinfo->filesystem == FAT12) {
265  offset = cluster + (cluster / 2);
266  new_contents &=0xfff;
267  }
268  else if (volinfo->filesystem == FAT16) {
269  offset = cluster * 2;
270  new_contents &=0xffff;
271  }
272  else if (volinfo->filesystem == FAT32) {
273  offset = cluster * 4;
274  new_contents &=0x0fffffff; // FAT32 is really "FAT28"
275  }
276  else
277  return DFS_ERRMISC;
278 
279  // at this point, offset is the BYTE offset of the desired sector from the start
280  // of the FAT. Calculate the physical sector containing this FAT entry.
281  sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
282 
283  // If this is not the same sector we last read, then read it into RAM
284  if (sector != *scratchcache) {
285  if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
286  // avoid anyone assuming that this cache value is still valid, which
287  // might cause disk corruption
288  *scratchcache = 0;
289  return DFS_ERRMISC;
290  }
291  *scratchcache = sector;
292  }
293 
294  // At this point, we "merely" need to extract the relevant entry.
295  // This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
296  // may span a sector boundary. The normal way around this is always to read two
297  // FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
298  offset = ldiv(offset, SECTOR_SIZE).rem;
299 
300  if (volinfo->filesystem == FAT12) {
301 
302  // If this is an odd cluster, pre-shift the desired new contents 4 bits to
303  // make the calculations below simpler
304  if (cluster & 1)
305  new_contents = new_contents << 4;
306 
307  // Special case for sector boundary
308  if (offset == SECTOR_SIZE - 1) {
309 
310  // Odd cluster: High 12 bits being set
311  if (cluster & 1) {
312  scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
313  }
314  // Even cluster: Low 12 bits being set
315  else {
316  scratch[offset] = new_contents & 0xff;
317  }
318  result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
319  // mirror the FAT into copy 2
320  if (DFS_OK == result)
321  result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
322 
323  // If we wrote that sector OK, then read in the subsequent sector
324  // and poke the first byte with the remainder of this FAT entry.
325  if (DFS_OK == result) {
326  *scratchcache++;
327  result = DFS_ReadSector(volinfo->unit, scratch, *scratchcache, 1);
328  if (DFS_OK == result) {
329  // Odd cluster: High 12 bits being set
330  if (cluster & 1) {
331  scratch[0] = new_contents & 0xff00;
332  }
333  // Even cluster: Low 12 bits being set
334  else {
335  scratch[0] = (scratch[0] & 0xf0) | (new_contents & 0x0f);
336  }
337  result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
338  // mirror the FAT into copy 2
339  if (DFS_OK == result)
340  result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
341  }
342  else {
343  // avoid anyone assuming that this cache value is still valid, which
344  // might cause disk corruption
345  *scratchcache = 0;
346  }
347  }
348  } // if (offset == SECTOR_SIZE - 1)
349 
350  // Not a sector boundary. But we still have to worry about if it's an odd
351  // or even cluster number.
352  else {
353  // Odd cluster: High 12 bits being set
354  if (cluster & 1) {
355  scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
356  scratch[offset+1] = new_contents & 0xff00;
357  }
358  // Even cluster: Low 12 bits being set
359  else {
360  scratch[offset] = new_contents & 0xff;
361  scratch[offset+1] = (scratch[offset+1] & 0xf0) | (new_contents & 0x0f);
362  }
363  result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
364  // mirror the FAT into copy 2
365  if (DFS_OK == result)
366  result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
367  }
368  }
369  else if (volinfo->filesystem == FAT16) {
370  scratch[offset] = (new_contents & 0xff);
371  scratch[offset+1] = (new_contents & 0xff00) >> 8;
372  result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
373  // mirror the FAT into copy 2
374  if (DFS_OK == result)
375  result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
376  }
377  else if (volinfo->filesystem == FAT32) {
378  scratch[offset] = (new_contents & 0xff);
379  scratch[offset+1] = (new_contents & 0xff00) >> 8;
380  scratch[offset+2] = (new_contents & 0xff0000) >> 16;
381  scratch[offset+3] = (scratch[offset+3] & 0xf0) | ((new_contents & 0x0f000000) >> 24);
382  // Note well from the above: Per Microsoft's guidelines we preserve the upper
383  // 4 bits of the FAT32 cluster value. It's unclear what these bits will be used
384  // for; in every example I've encountered they are always zero.
385  result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
386  // mirror the FAT into copy 2
387  if (DFS_OK == result)
388  result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
389  }
390  else
391  result = DFS_ERRMISC;
392 
393  return result;
394 }
395 
396 /*
397  Convert a filename element from canonical (8.3) to directory entry (11) form
398  src must point to the first non-separator character.
399  dest must point to a 12-byte buffer.
400 */
401 uint8_t *DFS_CanonicalToDir(uint8_t *dest, uint8_t *src)
402 {
403  uint8_t *destptr = dest;
404 
405  memset(dest, ' ', 11);
406  dest[11] = 0;
407 
408  while (*src && (*src != DIR_SEPARATOR) && (destptr - dest < 11)) {
409  if (*src >= 'a' && *src <='z') {
410  *destptr++ = (*src - 'a') + 'A';
411  src++;
412  }
413  else if (*src == '.') {
414  src++;
415  destptr = dest + 8;
416  }
417  else {
418  *destptr++ = *src++;
419  }
420  }
421 
422  return dest;
423 }
424 
425 /*
426  Find the first unused FAT entry
427  You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
428  Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
429  FAT entry.
430  Returns FAT32 bad_sector (0x0ffffff7) if there is no free cluster available
431 */
432 uint32_t DFS_GetFreeFAT(PVOLINFO volinfo, uint8_t *scratch)
433 {
434  uint32_t i, result = 0xffffffff, scratchcache = 0;
435 
436  // Search starts at cluster 2, which is the first usable cluster
437  // NOTE: This search can't terminate at a bad cluster, because there might
438  // legitimately be bad clusters on the disk.
439  for (i=2; i < volinfo->numclusters; i++) {
440  result = DFS_GetFAT(volinfo, scratch, &scratchcache, i);
441  if (!result) {
442  return i;
443  }
444  }
445  return 0x0ffffff7; // Can't find a free cluster
446 }
447 
448 
449 /*
450  Open a directory for enumeration by DFS_GetNextDirEnt
451  You must supply a populated VOLINFO (see DFS_GetVolInfo)
452  The empty string or a string containing only the directory separator are
453  considered to be the root directory.
454  Returns 0 OK, nonzero for any error.
455 */
456 uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo)
457 {
458  // Default behavior is a regular search for existing entries
459  dirinfo->flags = 0;
460 
461  if (!strlen((char *) dirname) || (strlen((char *) dirname) == 1 && dirname[0] == DIR_SEPARATOR)) {
462  if (volinfo->filesystem == FAT32) {
463  dirinfo->currentcluster = volinfo->rootdir;
464  dirinfo->currentsector = 0;
465  dirinfo->currententry = 0;
466 
467  // read first sector of directory
468  return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1);
469  }
470  else {
471  dirinfo->currentcluster = 0;
472  dirinfo->currentsector = 0;
473  dirinfo->currententry = 0;
474 
475  // read first sector of directory
476  return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1);
477  }
478  }
479 
480  // This is not the root directory. We need to find the start of this subdirectory.
481  // We do this by devious means, using our own companion function DFS_GetNext.
482  else {
483  uint8_t tmpfn[12];
484  uint8_t *ptr = dirname;
485  uint32_t result;
486  DIRENT de;
487 
488  if (volinfo->filesystem == FAT32) {
489  dirinfo->currentcluster = volinfo->rootdir;
490  dirinfo->currentsector = 0;
491  dirinfo->currententry = 0;
492 
493  // read first sector of directory
494  if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1))
495  return DFS_ERRMISC;
496  }
497  else {
498  dirinfo->currentcluster = 0;
499  dirinfo->currentsector = 0;
500  dirinfo->currententry = 0;
501 
502  // read first sector of directory
503  if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1))
504  return DFS_ERRMISC;
505  }
506 
507  // skip leading path separators
508  while (*ptr == DIR_SEPARATOR && *ptr)
509  ptr++;
510 
511  // Scan the path from left to right, finding the start cluster of each entry
512  // Observe that this code is inelegant, but obviates the need for recursion.
513  while (*ptr) {
514  DFS_CanonicalToDir(tmpfn, ptr);
515 
516  de.name[0] = 0;
517 
518  do {
519  result = DFS_GetNext(volinfo, dirinfo, &de);
520  } while (!result && memcmp(de.name, tmpfn, 11));
521 
522  if (!memcmp(de.name, tmpfn, 11) && ((de.attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)) {
523  if (volinfo->filesystem == FAT32) {
524  dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
525  ((uint32_t) de.startclus_l_h) << 8 |
526  ((uint32_t) de.startclus_h_l) << 16 |
527  ((uint32_t) de.startclus_h_h) << 24;
528  }
529  else {
530  dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
531  ((uint32_t) de.startclus_l_h) << 8;
532  }
533  dirinfo->currentsector = 0;
534  dirinfo->currententry = 0;
535 
536  if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus), 1))
537  return DFS_ERRMISC;
538  }
539  else if (!memcmp(de.name, tmpfn, 11) && !(de.attr & ATTR_DIRECTORY))
540  return DFS_NOTFOUND;
541 
542  // seek to next item in list
543  while (*ptr != DIR_SEPARATOR && *ptr)
544  ptr++;
545  if (*ptr == DIR_SEPARATOR)
546  ptr++;
547  }
548 
549  if (!dirinfo->currentcluster)
550  return DFS_NOTFOUND;
551  }
552  return DFS_OK;
553 }
554 
555 /*
556  Get next entry in opened directory structure. Copies fields into the dirent
557  structure, updates dirinfo. Note that it is the _caller's_ responsibility to
558  handle the '.' and '..' entries.
559  A deleted file will be returned as a NULL entry (first char of filename=0)
560  by this code. Filenames beginning with 0x05 will be translated to 0xE5
561  automatically. Long file name entries will be returned as NULL.
562  returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
563  or DFS_ERRMISC for a media error
564 */
565 uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent)
566 {
567  uint32_t tempint; // required by DFS_GetFAT
568 
569  // Do we need to read the next sector of the directory?
570  if (dirinfo->currententry >= SECTOR_SIZE / sizeof(DIRENT)) {
571  dirinfo->currententry = 0;
572  dirinfo->currentsector++;
573 
574  // Root directory; special case handling
575  // Note that currentcluster will only ever be zero if both:
576  // (a) this is the root directory, and
577  // (b) we are on a FAT12/16 volume, where the root dir can't be expanded
578  if (dirinfo->currentcluster == 0) {
579  // Trying to read past end of root directory?
580  if (dirinfo->currentsector * (SECTOR_SIZE / sizeof(DIRENT)) >= volinfo->rootentries)
581  return DFS_EOF;
582 
583  // Otherwise try to read the next sector
584  if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir + dirinfo->currentsector, 1))
585  return DFS_ERRMISC;
586  }
587 
588  // Normal handling
589  else {
590  if (dirinfo->currentsector >= volinfo->secperclus) {
591  dirinfo->currentsector = 0;
592  if ((dirinfo->currentcluster >= 0xff7 && volinfo->filesystem == FAT12) ||
593  (dirinfo->currentcluster >= 0xfff7 && volinfo->filesystem == FAT16) ||
594  (dirinfo->currentcluster >= 0x0ffffff7 && volinfo->filesystem == FAT32)) {
595 
596  // We are at the end of the directory chain. If this is a normal
597  // find operation, we should indicate that there is nothing more
598  // to see.
599  if (!(dirinfo->flags & DFS_DI_BLANKENT))
600  return DFS_EOF;
601 
602  // On the other hand, if this is a "find free entry" search,
603  // we need to tell the caller to allocate a new cluster
604  else
605  return DFS_ALLOCNEW;
606  }
607  dirinfo->currentcluster = DFS_GetFAT(volinfo, dirinfo->scratch, &tempint, dirinfo->currentcluster);
608  }
609  if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus) + dirinfo->currentsector, 1))
610  return DFS_ERRMISC;
611  }
612  }
613 
614  memcpy(dirent, &(((PDIRENT) dirinfo->scratch)[dirinfo->currententry]), sizeof(DIRENT));
615 
616  if (dirent->name[0] == 0) { // no more files in this directory
617  // If this is a "find blank" then we can reuse this name.
618  if (dirinfo->flags & DFS_DI_BLANKENT)
619  return DFS_OK;
620  else
621  return DFS_EOF;
622  }
623 
624  if (dirent->name[0] == 0xe5) // handle deleted file entries
625  dirent->name[0] = 0;
626  else if ((dirent->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)
627  dirent->name[0] = 0;
628  else if (dirent->name[0] == 0x05) // handle kanji filenames beginning with 0xE5
629  dirent->name[0] = 0xe5;
630 
631  dirinfo->currententry++;
632 
633  return DFS_OK;
634 }
635 
636 /*
637  INTERNAL
638  Find a free directory entry in the directory specified by path
639  This function MAY cause a disk write if it is necessary to extend the directory
640  size.
641  Note - di.scratch must be preinitialized to point to a sector scratch buffer
642  de is a scratch structure
643  Returns DFS_ERRMISC if a new entry could not be located or created
644  de is updated with the same return information you would expect from DFS_GetNext
645 */
646 uint32_t DFS_GetFreeDirEnt(PVOLINFO volinfo, uint8_t *path, PDIRINFO di, PDIRENT de)
647 {
648  uint32_t tempclus,i;
649 
650  if (DFS_OpenDir(volinfo, path, di))
651  return DFS_NOTFOUND;
652 
653  // Set "search for empty" flag so DFS_GetNext knows what we're doing
654  di->flags |= DFS_DI_BLANKENT;
655 
656  // We seek through the directory looking for an empty entry
657  // Note we are reusing tempclus as a temporary result holder.
658  tempclus = 0;
659  do {
660  tempclus = DFS_GetNext(volinfo, di, de);
661 
662  // Empty entry found
663  if (tempclus == DFS_OK && (!de->name[0])) {
664  return DFS_OK;
665  }
666 
667  // End of root directory reached
668  else if (tempclus == DFS_EOF)
669  return DFS_ERRMISC;
670 
671  else if (tempclus == DFS_ALLOCNEW) {
672  tempclus = DFS_GetFreeFAT(volinfo, di->scratch);
673  if (tempclus == 0x0ffffff7)
674  return DFS_ERRMISC;
675 
676  // write out zeroed sectors to the new cluster
677  memset(di->scratch, 0, SECTOR_SIZE);
678  for (i=0;i<volinfo->secperclus;i++) {
679  if (DFS_WriteSector(volinfo->unit, di->scratch, volinfo->dataarea + ((tempclus - 2) * volinfo->secperclus) + i, 1))
680  return DFS_ERRMISC;
681  }
682  // Point old end cluster to newly allocated cluster
683  i = 0;
684  DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
685 
686  // Update DIRINFO so caller knows where to place the new file
687  di->currentcluster = tempclus;
688  di->currentsector = 0;
689  di->currententry = 1; // since the code coming after this expects to subtract 1
690 
691  // Mark newly allocated cluster as end of chain
692  switch(volinfo->filesystem) {
693  case FAT12: tempclus = 0xff8; break;
694  case FAT16: tempclus = 0xfff8; break;
695  case FAT32: tempclus = 0x0ffffff8; break;
696  default: return DFS_ERRMISC;
697  }
698  DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
699  }
700  } while (!tempclus);
701 
702  // We shouldn't get here
703  return DFS_ERRMISC;
704 }
705 
706 /*
707  Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
708  mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
709  provide a pointer to a sector-sized scratch buffer.
710  Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
711  to access the file from this point on.
712 */
713 uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo)
714 {
715  uint8_t tmppath[MAX_PATH];
716  uint8_t filename[12];
717  uint8_t *p;
718  DIRINFO di;
719  DIRENT de;
720 
721  // larwe 2006-09-16 +1 zero out file structure
722  memset(fileinfo, 0, sizeof(FILEINFO));
723 
724  // save access mode
725  fileinfo->mode = mode;
726 
727  // Get a local copy of the path. If it's longer than MAX_PATH, abort.
728  strncpy((char *) tmppath, (char *) path, MAX_PATH);
729  tmppath[MAX_PATH - 1] = 0;
730  if (strcmp((char *) path,(char *) tmppath)) {
731  return DFS_PATHLEN;
732  }
733 
734  // strip leading path separators
735  while (tmppath[0] == DIR_SEPARATOR)
736  strcpy((char *) tmppath, (char *) tmppath + 1);
737 
738  // Parse filename off the end of the supplied path
739  p = tmppath;
740  while (*(p++));
741 
742  p--;
743  while (p > tmppath && *p != DIR_SEPARATOR) // larwe 9/16/06 ">=" to ">" bugfix
744  p--;
745  if (*p == DIR_SEPARATOR)
746  p++;
747 
748  DFS_CanonicalToDir(filename, p);
749 
750  if (p > tmppath)
751  p--;
752  if (*p == DIR_SEPARATOR || p == tmppath) // larwe 9/16/06 +"|| p == tmppath" bugfix
753  *p = 0;
754 
755  // At this point, if our path was MYDIR/MYDIR2/FILE.EXT, filename = "FILE EXT" and
756  // tmppath = "MYDIR/MYDIR2".
757  di.scratch = scratch;
758  if (DFS_OpenDir(volinfo, tmppath, &di))
759  return DFS_NOTFOUND;
760 
761  while (!DFS_GetNext(volinfo, &di, &de)) {
762  if (!memcmp(de.name, filename, 11)) {
763  // You can't use this function call to open a directory.
764  if (de.attr & ATTR_DIRECTORY)
765  return DFS_NOTFOUND;
766 
767  fileinfo->volinfo = volinfo;
768  fileinfo->pointer = 0;
769  // The reason we store this extra info about the file is so that we can
770  // speedily update the file size, modification date, etc. on a file that is
771  // opened for writing.
772  if (di.currentcluster == 0)
773  fileinfo->dirsector = volinfo->rootdir + di.currentsector;
774  else
775  fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
776  fileinfo->diroffset = di.currententry - 1;
777  if (volinfo->filesystem == FAT32) {
778  fileinfo->cluster = (uint32_t) de.startclus_l_l |
779  ((uint32_t) de.startclus_l_h) << 8 |
780  ((uint32_t) de.startclus_h_l) << 16 |
781  ((uint32_t) de.startclus_h_h) << 24;
782  }
783  else {
784  fileinfo->cluster = (uint32_t) de.startclus_l_l |
785  ((uint32_t) de.startclus_l_h) << 8;
786  }
787  fileinfo->firstcluster = fileinfo->cluster;
788  fileinfo->filelen = (uint32_t) de.filesize_0 |
789  ((uint32_t) de.filesize_1) << 8 |
790  ((uint32_t) de.filesize_2) << 16 |
791  ((uint32_t) de.filesize_3) << 24;
792 
793  return DFS_OK;
794  }
795  }
796 
797  // At this point, we KNOW the file does not exist. If the file was opened
798  // with write access, we can create it.
799  if (mode & DFS_WRITE) {
800  uint32_t cluster, temp;
801 
802  // Locate or create a directory entry for this file
803  if (DFS_OK != DFS_GetFreeDirEnt(volinfo, tmppath, &di, &de))
804  return DFS_ERRMISC;
805 
806  // put sane values in the directory entry
807  memset(&de, 0, sizeof(de));
808  memcpy(de.name, filename, 11);
809  de.crttime_l = 0x20; // 01:01:00am, Jan 1, 2006.
810  de.crttime_h = 0x08;
811  de.crtdate_l = 0x11;
812  de.crtdate_h = 0x34;
813  de.lstaccdate_l = 0x11;
814  de.lstaccdate_h = 0x34;
815  de.wrttime_l = 0x20;
816  de.wrttime_h = 0x08;
817  de.wrtdate_l = 0x11;
818  de.wrtdate_h = 0x34;
819 
820  // allocate a starting cluster for the directory entry
821  cluster = DFS_GetFreeFAT(volinfo, scratch);
822 
823  de.startclus_l_l = cluster & 0xff;
824  de.startclus_l_h = (cluster & 0xff00) >> 8;
825  de.startclus_h_l = (cluster & 0xff0000) >> 16;
826  de.startclus_h_h = (cluster & 0xff000000) >> 24;
827 
828  // update FILEINFO for our caller's sake
829  fileinfo->volinfo = volinfo;
830  fileinfo->pointer = 0;
831  // The reason we store this extra info about the file is so that we can
832  // speedily update the file size, modification date, etc. on a file that is
833  // opened for writing.
834  if (di.currentcluster == 0)
835  fileinfo->dirsector = volinfo->rootdir + di.currentsector;
836  else
837  fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
838  fileinfo->diroffset = di.currententry - 1;
839  fileinfo->cluster = cluster;
840  fileinfo->firstcluster = cluster;
841  fileinfo->filelen = 0;
842 
843  // write the directory entry
844  // note that we no longer have the sector containing the directory entry,
845  // tragically, so we have to re-read it
846  if (DFS_ReadSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
847  return DFS_ERRMISC;
848  memcpy(&(((PDIRENT) scratch)[di.currententry-1]), &de, sizeof(DIRENT));
849  if (DFS_WriteSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
850  return DFS_ERRMISC;
851 
852  // Mark newly allocated cluster as end of chain
853  switch(volinfo->filesystem) {
854  case FAT12: cluster = 0xff8; break;
855  case FAT16: cluster = 0xfff8; break;
856  case FAT32: cluster = 0x0ffffff8; break;
857  default: return DFS_ERRMISC;
858  }
859  temp = 0;
860  DFS_SetFAT(volinfo, scratch, &temp, fileinfo->cluster, cluster);
861 
862  return DFS_OK;
863  }
864 
865  return DFS_NOTFOUND;
866 }
867 
868 /*
869  Read an open file
870  You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
871  pointer to a SECTOR_SIZE scratch buffer.
872  Note that returning DFS_EOF is not an error condition. This function updates the
873  successcount field with the number of bytes actually read.
874 */
875 uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
876 {
877  uint32_t remain;
878  uint32_t result = DFS_OK;
879  uint32_t sector;
880  uint32_t bytesread;
881 
882  // Don't try to read past EOF
883  if (len > fileinfo->filelen - fileinfo->pointer)
884  len = fileinfo->filelen - fileinfo->pointer;
885 
886  remain = len;
887  *successcount = 0;
888 
889  while (remain && result == DFS_OK) {
890  // This is a bit complicated. The sector we want to read is addressed at a cluster
891  // granularity by the fileinfo->cluster member. The file pointer tells us how many
892  // extra sectors to add to that number.
893  sector = fileinfo->volinfo->dataarea +
894  ((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
895  div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
896 
897  // Case 1 - File pointer is not on a sector boundary
898  if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
899  uint16_t tempreadsize;
900 
901  // We always have to go through scratch in this case
902  result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
903 
904  // This is the number of bytes that we actually care about in the sector
905  // just read.
906  tempreadsize = SECTOR_SIZE - (div(fileinfo->pointer, SECTOR_SIZE).rem);
907 
908  // Case 1A - We want the entire remainder of the sector. After this
909  // point, all passes through the read loop will be aligned on a sector
910  // boundary, which allows us to go through the optimal path 2A below.
911  if (remain >= tempreadsize) {
912  memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), tempreadsize);
913  bytesread = tempreadsize;
914  buffer += tempreadsize;
915  fileinfo->pointer += tempreadsize;
916  remain -= tempreadsize;
917  }
918  // Case 1B - This read concludes the file read operation
919  else {
920  memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), remain);
921 
922  buffer += remain;
923  fileinfo->pointer += remain;
924  bytesread = remain;
925  remain = 0;
926  }
927  }
928  // Case 2 - File pointer is on sector boundary
929  else {
930  // Case 2A - We have at least one more full sector to read and don't have
931  // to go through the scratch buffer. You could insert optimizations here to
932  // read multiple sectors at a time, if you were thus inclined (note that
933  // the maximum multi-read you could perform is a single cluster, so it would
934  // be advantageous to have code similar to case 1A above that would round the
935  // pointer to a cluster boundary the first pass through, so all subsequent
936  // [large] read requests would be able to go a cluster at a time).
937  if (remain >= SECTOR_SIZE) {
938  result = DFS_ReadSector(fileinfo->volinfo->unit, buffer, sector, 1);
939  remain -= SECTOR_SIZE;
940  buffer += SECTOR_SIZE;
941  fileinfo->pointer += SECTOR_SIZE;
942  bytesread = SECTOR_SIZE;
943  }
944  // Case 2B - We are only reading a partial sector
945  else {
946  result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
947  memcpy(buffer, scratch, remain);
948  buffer += remain;
949  fileinfo->pointer += remain;
950  bytesread = remain;
951  remain = 0;
952  }
953  }
954 
955  *successcount += bytesread;
956 
957  // check to see if we stepped over a cluster boundary
958  if (div(fileinfo->pointer - bytesread, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
959  div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
960  // An act of minor evil - we use bytesread as a scratch integer, knowing that
961  // its value is not used after updating *successcount above
962  bytesread = 0;
963  if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
964  ((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
965  ((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8)))
966  result = DFS_EOF;
967  else
968  fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &bytesread, fileinfo->cluster);
969  }
970  }
971 
972  return result;
973 }
974 
975 /*
976  Seek file pointer to a given position
977  This function does not return status - refer to the fileinfo->pointer value
978  to see where the pointer wound up.
979  Requires a SECTOR_SIZE scratch buffer
980 */
981 void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch)
982 {
983  uint32_t tempint;
984 
985  // larwe 9/16/06 bugfix split case 0a/0b and changed fallthrough handling
986  // Case 0a - Return immediately for degenerate case
987  if (offset == fileinfo->pointer) {
988  return;
989  }
990 
991  // Case 0b - Don't allow the user to seek past the end of the file
992  if (offset > fileinfo->filelen) {
993  offset = fileinfo->filelen;
994  // NOTE NO RETURN HERE!
995  }
996 
997  // Case 1 - Simple rewind to start
998  // Note _intentional_ fallthrough from Case 0b above
999  if (offset == 0) {
1000  fileinfo->cluster = fileinfo->firstcluster;
1001  fileinfo->pointer = 0;
1002  return; // larwe 9/16/06 +1 bugfix
1003  }
1004  // Case 2 - Seeking backwards. Need to reset and seek forwards
1005  else if (offset < fileinfo->pointer) {
1006  fileinfo->cluster = fileinfo->firstcluster;
1007  fileinfo->pointer = 0;
1008  // NOTE NO RETURN HERE!
1009  }
1010 
1011  // Case 3 - Seeking forwards
1012  // Note _intentional_ fallthrough from Case 2 above
1013 
1014  // Case 3a - Seek size does not cross cluster boundary -
1015  // very simple case
1016  // larwe 9/16/06 changed .rem to .quot in both div calls, bugfix
1017  if (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot ==
1018  div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1019  fileinfo->pointer = offset;
1020  }
1021  // Case 3b - Seeking across cluster boundary(ies)
1022  else {
1023  // round file pointer down to cluster boundary
1024  fileinfo->pointer = div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot *
1025  fileinfo->volinfo->secperclus * SECTOR_SIZE;
1026 
1027  // seek by clusters
1028  // larwe 9/30/06 bugfix changed .rem to .quot in both div calls
1029  while (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
1030  div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1031 
1032  fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &tempint, fileinfo->cluster);
1033  // Abort if there was an error
1034  if (fileinfo->cluster == 0x0ffffff7) {
1035  fileinfo->pointer = 0;
1036  fileinfo->cluster = fileinfo->firstcluster;
1037  return;
1038  }
1039  fileinfo->pointer += SECTOR_SIZE * fileinfo->volinfo->secperclus;
1040  }
1041 
1042  // since we know the cluster is right, we have no more work to do
1043  fileinfo->pointer = offset;
1044  }
1045 }
1046 
1047 /*
1048  Delete a file
1049  scratch must point to a sector-sized buffer
1050 */
1051 uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch)
1052 {
1053  /* PDIRENT de = (PDIRENT) scratch; */
1054  FILEINFO fi;
1055  uint32_t cache = 0;
1056  uint32_t tempclus;
1057 
1058  // DFS_OpenFile gives us all the information we need to delete it
1059  if (DFS_OK != DFS_OpenFile(volinfo, path, DFS_READ, scratch, &fi))
1060  return DFS_NOTFOUND;
1061 
1062  // First, read the directory sector and delete that entry
1063  if (DFS_ReadSector(volinfo->unit, scratch, fi.dirsector, 1))
1064  return DFS_ERRMISC;
1065  ((PDIRENT) scratch)[fi.diroffset].name[0] = 0xe5;
1066  if (DFS_WriteSector(volinfo->unit, scratch, fi.dirsector, 1))
1067  return DFS_ERRMISC;
1068 
1069  // Now follow the cluster chain to free the file space
1070  while (!((volinfo->filesystem == FAT12 && fi.firstcluster >= 0x0ff7) ||
1071  (volinfo->filesystem == FAT16 && fi.firstcluster >= 0xfff7) ||
1072  (volinfo->filesystem == FAT32 && fi.firstcluster >= 0x0ffffff7))) {
1073  tempclus = fi.firstcluster;
1074 
1075  fi.firstcluster = DFS_GetFAT(volinfo, scratch, &cache, fi.firstcluster);
1076  DFS_SetFAT(volinfo, scratch, &cache, tempclus, 0);
1077 
1078  }
1079  return DFS_OK;
1080 }
1081 
1082 
1083 /*
1084  Write an open file
1085  You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
1086  pointer to a SECTOR_SIZE scratch buffer.
1087  This function updates the successcount field with the number of bytes actually written.
1088 */
1089 uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
1090 {
1091  uint32_t remain;
1092  uint32_t result = DFS_OK;
1093  uint32_t sector;
1094  uint32_t byteswritten;
1095 
1096  // Don't allow writes to a file that's open as readonly
1097  if (!(fileinfo->mode & DFS_WRITE))
1098  return DFS_ERRMISC;
1099 
1100  remain = len;
1101  *successcount = 0;
1102 
1103  while (remain && result == DFS_OK) {
1104  // This is a bit complicated. The sector we want to read is addressed at a cluster
1105  // granularity by the fileinfo->cluster member. The file pointer tells us how many
1106  // extra sectors to add to that number.
1107  sector = fileinfo->volinfo->dataarea +
1108  ((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
1109  div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
1110 
1111  // Case 1 - File pointer is not on a sector boundary
1112  if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
1113  uint16_t tempsize;
1114 
1115  // We always have to go through scratch in this case
1116  result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
1117 
1118  // This is the number of bytes that we don't want to molest in the
1119  // scratch sector just read.
1120  tempsize = div(fileinfo->pointer, SECTOR_SIZE).rem;
1121 
1122  // Case 1A - We are writing the entire remainder of the sector. After
1123  // this point, all passes through the read loop will be aligned on a
1124  // sector boundary, which allows us to go through the optimal path
1125  // 2A below.
1126  if (remain >= SECTOR_SIZE - tempsize) {
1127  memcpy(scratch + tempsize, buffer, SECTOR_SIZE - tempsize);
1128  if (!result)
1129  result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1130 
1131  byteswritten = SECTOR_SIZE - tempsize;
1132  buffer += SECTOR_SIZE - tempsize;
1133  fileinfo->pointer += SECTOR_SIZE - tempsize;
1134  if (fileinfo->filelen < fileinfo->pointer) {
1135  fileinfo->filelen = fileinfo->pointer;
1136  }
1137  remain -= SECTOR_SIZE - tempsize;
1138  }
1139  // Case 1B - This concludes the file write operation
1140  else {
1141  memcpy(scratch + tempsize, buffer, remain);
1142  if (!result)
1143  result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1144 
1145  buffer += remain;
1146  fileinfo->pointer += remain;
1147  if (fileinfo->filelen < fileinfo->pointer) {
1148  fileinfo->filelen = fileinfo->pointer;
1149  }
1150  byteswritten = remain;
1151  remain = 0;
1152  }
1153  } // case 1
1154  // Case 2 - File pointer is on sector boundary
1155  else {
1156  // Case 2A - We have at least one more full sector to write and don't have
1157  // to go through the scratch buffer. You could insert optimizations here to
1158  // write multiple sectors at a time, if you were thus inclined. Refer to
1159  // similar notes in DFS_ReadFile.
1160  if (remain >= SECTOR_SIZE) {
1161  result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
1162  remain -= SECTOR_SIZE;
1163  buffer += SECTOR_SIZE;
1164  fileinfo->pointer += SECTOR_SIZE;
1165  if (fileinfo->filelen < fileinfo->pointer) {
1166  fileinfo->filelen = fileinfo->pointer;
1167  }
1168  byteswritten = SECTOR_SIZE;
1169  }
1170  // Case 2B - We are only writing a partial sector and potentially need to
1171  // go through the scratch buffer.
1172  else {
1173  // If the current file pointer is not yet at or beyond the file
1174  // length, we are writing somewhere in the middle of the file and
1175  // need to load the original sector to do a read-modify-write.
1176  if (fileinfo->pointer < fileinfo->filelen) {
1177  result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
1178  if (!result) {
1179  memcpy(scratch, buffer, remain);
1180  result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1181  }
1182  }
1183  else {
1184  result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
1185  }
1186 
1187  buffer += remain;
1188  fileinfo->pointer += remain;
1189  if (fileinfo->filelen < fileinfo->pointer) {
1190  fileinfo->filelen = fileinfo->pointer;
1191  }
1192  byteswritten = remain;
1193  remain = 0;
1194  }
1195  }
1196 
1197  *successcount += byteswritten;
1198 
1199  // check to see if we stepped over a cluster boundary
1200  if (div(fileinfo->pointer - byteswritten, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
1201  div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1202  uint32_t lastcluster;
1203 
1204  // We've transgressed into another cluster. If we were already at EOF,
1205  // we need to allocate a new cluster.
1206  // An act of minor evil - we use byteswritten as a scratch integer, knowing
1207  // that its value is not used after updating *successcount above
1208  byteswritten = 0;
1209 
1210  lastcluster = fileinfo->cluster;
1211  fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster);
1212 
1213  // Allocate a new cluster?
1214  if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
1215  ((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
1216  ((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8))) {
1217  uint32_t tempclus;
1218 
1219  tempclus = DFS_GetFreeFAT(fileinfo->volinfo, scratch);
1220  byteswritten = 0; // invalidate cache
1221  if (tempclus == 0x0ffffff7)
1222  return DFS_ERRMISC;
1223 
1224  // Link new cluster onto file
1225  DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, lastcluster, tempclus);
1226  fileinfo->cluster = tempclus;
1227 
1228  // Mark newly allocated cluster as end of chain
1229  switch(fileinfo->volinfo->filesystem) {
1230  case FAT12: tempclus = 0xff8; break;
1231  case FAT16: tempclus = 0xfff8; break;
1232  case FAT32: tempclus = 0x0ffffff8; break;
1233  default: return DFS_ERRMISC;
1234  }
1235  DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster, tempclus);
1236 
1237  result = DFS_OK;
1238  }
1239  // No else clause is required.
1240  }
1241  }
1242 
1243  // Update directory entry
1244  if (DFS_ReadSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
1245  return DFS_ERRMISC;
1246  ((PDIRENT) scratch)[fileinfo->diroffset].filesize_0 = fileinfo->filelen & 0xff;
1247  ((PDIRENT) scratch)[fileinfo->diroffset].filesize_1 = (fileinfo->filelen & 0xff00) >> 8;
1248  ((PDIRENT) scratch)[fileinfo->diroffset].filesize_2 = (fileinfo->filelen & 0xff0000) >> 16;
1249  ((PDIRENT) scratch)[fileinfo->diroffset].filesize_3 = (fileinfo->filelen & 0xff000000) >> 24;
1250  if (DFS_WriteSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
1251  return DFS_ERRMISC;
1252  return result;
1253 }