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