CircleOS  1
ff.c
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------/
2 / FatFs - FAT file system module R0.08a (C)ChaN, 2010
3 /-----------------------------------------------------------------------------/
4 / FatFs module is a generic FAT file system module for small embedded systems.
5 / This is a free software that opened for education, research and commercial
6 / developments under license policy of following terms.
7 /
8 / Copyright (C) 2010, ChaN, all right reserved.
9 /
10 / * The FatFs module is a free software and there is NO WARRANTY.
11 / * No restriction on use. You can use, modify and redistribute it for
12 / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
13 / * Redistributions of source code must retain the above copyright notice.
14 /
15 /-----------------------------------------------------------------------------/
16 / Feb 26,'06 R0.00 Prototype.
17 /
18 / Apr 29,'06 R0.01 First stable version.
19 /
20 / Jun 01,'06 R0.02 Added FAT12 support.
21 / Removed unbuffered mode.
22 / Fixed a problem on small (<32M) partition.
23 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
24 /
25 / Sep 22,'06 R0.03 Added f_rename().
26 / Changed option _FS_MINIMUM to _FS_MINIMIZE.
27 / Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
28 / Fixed f_mkdir() creates incorrect directory on FAT32.
29 /
30 / Feb 04,'07 R0.04 Supported multiple drive system.
31 / Changed some interfaces for multiple drive system.
32 / Changed f_mountdrv() to f_mount().
33 / Added f_mkfs().
34 / Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
35 / Added a capability of extending file size to f_lseek().
36 / Added minimization level 3.
37 / Fixed an endian sensitive code in f_mkfs().
38 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
39 / Added FSInfo support.
40 / Fixed DBCS name can result FR_INVALID_NAME.
41 / Fixed short seek (<= csize) collapses the file object.
42 /
43 / Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs().
44 / Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
45 / Fixed f_mkdir() on FAT32 creates incorrect directory.
46 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
47 / Fixed off by one error at FAT sub-type determination.
48 / Fixed btr in f_read() can be mistruncated.
49 / Fixed cached sector is not flushed when create and close without write.
50 /
51 / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets().
52 / Improved performance of f_lseek() on moving to the same or following cluster.
53 /
54 / Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. (_FS_TINY)
55 / Added long file name support.
56 / Added multiple code page support.
57 / Added re-entrancy for multitask operation.
58 / Added auto cluster size selection to f_mkfs().
59 / Added rewind option to f_readdir().
60 / Changed result code of critical errors.
61 / Renamed string functions to avoid name collision.
62 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
63 / Added multiple sector size support.
64 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
65 / Fixed wrong cache control in f_lseek().
66 / Added relative path feature.
67 / Added f_chdir() and f_chdrive().
68 / Added proper case conversion to extended char.
69 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
70 / Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
71 / Fixed name matching error on the 13 char boundary.
72 / Added a configuration option, _LFN_UNICODE.
73 / Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
74 /
75 / May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3)
76 / Added file lock feature. (_FS_SHARE)
77 / Added fast seek feature. (_USE_FASTSEEK)
78 / Changed some types on the API, XCHAR->TCHAR.
79 / Changed fname member in the FILINFO structure on Unicode cfg.
80 / String functions support UTF-8 encoding files on Unicode cfg.
81 / Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2)
82 / Added sector erase feature. (_USE_ERASE)
83 / Moved file lock semaphore table from fs object to the bss.
84 / Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
85 / Fixed f_mkfs() creates wrong FAT32 volume.
86 /---------------------------------------------------------------------------*/
87 #include "circle.h"
88 #include "ff.h" /* FatFs configurations and declarations */
89 #include "diskio.h" /* Declarations of low level disk I/O functions */
90 
91 
92 /*--------------------------------------------------------------------------
93 
94  Module Private Definitions
95 
96 ---------------------------------------------------------------------------*/
97 
98 #if _FATFS != 8255
99 #error Wrong include file (ff.h).
100 #endif
101 
102 
103 /* Definitions on sector size */
104 #if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096
105 #error Wrong sector size.
106 #endif
107 #if _MAX_SS != 512
108 #define SS(fs) ((fs)->ssize) /* Multiple sector size */
109 #else
110 #define SS(fs) 512U /* Fixed sector size */
111 #endif
112 
113 
114 /* Reentrancy related */
115 #if _FS_REENTRANT
116 #if _USE_LFN == 1
117 #error Static LFN work area must not be used in re-entrant configuration.
118 #endif
119 #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }
120 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
121 #else
122 #define ENTER_FF(fs)
123 #define LEAVE_FF(fs, res) return res
124 #endif
125 
126 #define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
127 
128 
129 /* File shareing feature */
130 #if _FS_SHARE
131 #if _FS_READONLY
132 #error _FS_SHARE must be 0 on read-only cfg.
133 #endif
134 typedef struct
135 {
136  FATFS* fs; /* File ID 1, volume (NULL:blank entry) */
137  DWORD clu; /* File ID 2, directory */
138  WORD idx; /* File ID 3, directory index */
139  WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */
140 } FILESEM;
141 #endif
142 
143 
144 /* Misc definitions */
145 #define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO))
146 #define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);}
147 
148 
149 /* Character code support macros */
150 #define IsUpper(c) (((c)>='A')&&((c)<='Z'))
151 #define IsLower(c) (((c)>='a')&&((c)<='z'))
152 #define IsDigit(c) (((c)>='0')&&((c)<='9'))
153 
154 #if _DF1S /* Code page is DBCS */
155 
156 #ifdef _DF2S /* Two 1st byte areas */
157 #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
158 #else /* One 1st byte area */
159 #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
160 #endif
161 
162 #ifdef _DS3S /* Three 2nd byte areas */
163 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
164 #else /* Two 2nd byte areas */
165 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
166 #endif
167 
168 #else /* Code page is SBCS */
169 
170 #define IsDBCS1(c) 0
171 #define IsDBCS2(c) 0
172 
173 #endif /* _DF1S */
174 
175 
176 /* Name status flags */
177 #define NS 11 /* Offset of name status byte */
178 #define NS_LOSS 0x01 /* Out of 8.3 format */
179 #define NS_LFN 0x02 /* Force to create LFN entry */
180 #define NS_LAST 0x04 /* Last segment */
181 #define NS_BODY 0x08 /* Lower case flag (body) */
182 #define NS_EXT 0x10 /* Lower case flag (ext) */
183 #define NS_DOT 0x20 /* Dot entry */
184 
185 
186 /* FAT sub-type boundaries */
187 /* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
188 #define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */
189 #define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */
190 
191 
192 /* FatFs refers the members in the FAT structures as byte array instead of
193 / structure member because there are incompatibility of the packing option
194 / between compilers. */
195 
196 #define BS_jmpBoot 0
197 #define BS_OEMName 3
198 #define BPB_BytsPerSec 11
199 #define BPB_SecPerClus 13
200 #define BPB_RsvdSecCnt 14
201 #define BPB_NumFATs 16
202 #define BPB_RootEntCnt 17
203 #define BPB_TotSec16 19
204 #define BPB_Media 21
205 #define BPB_FATSz16 22
206 #define BPB_SecPerTrk 24
207 #define BPB_NumHeads 26
208 #define BPB_HiddSec 28
209 #define BPB_TotSec32 32
210 #define BS_DrvNum 36
211 #define BS_BootSig 38
212 #define BS_VolID 39
213 #define BS_VolLab 43
214 #define BS_FilSysType 54
215 #define BPB_FATSz32 36
216 #define BPB_ExtFlags 40
217 #define BPB_FSVer 42
218 #define BPB_RootClus 44
219 #define BPB_FSInfo 48
220 #define BPB_BkBootSec 50
221 #define BS_DrvNum32 64
222 #define BS_BootSig32 66
223 #define BS_VolID32 67
224 #define BS_VolLab32 71
225 #define BS_FilSysType32 82
226 #define FSI_LeadSig 0
227 #define FSI_StrucSig 484
228 #define FSI_Free_Count 488
229 #define FSI_Nxt_Free 492
230 #define MBR_Table 446
231 #define BS_55AA 510
232 
233 #define DIR_Name 0
234 #define DIR_Attr 11
235 #define DIR_NTres 12
236 #define DIR_CrtTime 14
237 #define DIR_CrtDate 16
238 #define DIR_FstClusHI 20
239 #define DIR_WrtTime 22
240 #define DIR_WrtDate 24
241 #define DIR_FstClusLO 26
242 #define DIR_FileSize 28
243 #define LDIR_Ord 0
244 #define LDIR_Attr 11
245 #define LDIR_Type 12
246 #define LDIR_Chksum 13
247 #define LDIR_FstClusLO 26
248 
249 
250 
251 /*------------------------------------------------------------*/
252 /* Work area */
253 
254 #if _VOLUMES
255 FATFS* FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */
256 #else
257 #error Number of drives must not be 0.
258 #endif
259 
260 static
261 WORD Fsid; /* File system mount ID */
262 
263 #if _FS_RPATH
264 static
265 BYTE CurrVol; /* Current drive */
266 #endif
267 
268 #if _FS_SHARE
269 static
270 FILESEM Files[_FS_SHARE]; /* File lock semaphores */
271 #endif
272 
273 #if _USE_LFN == 0 /* No LFN */
274 #define DEF_NAMEBUF BYTE sfn[12]
275 #define INIT_BUF(dobj) (dobj).fn = sfn
276 #define FREE_BUF()
277 
278 #elif _USE_LFN == 1 /* LFN with static LFN working buffer */
279 WCHAR LfnBuf[_MAX_LFN + 1];
280 #define DEF_NAMEBUF BYTE sfn[12]
281 #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
282 #define FREE_BUF()
283 
284 #elif _USE_LFN == 2 /* LFN with dynamic LFN working buffer on the stack */
285 #define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
286 #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; }
287 #define FREE_BUF()
288 
289 #elif _USE_LFN == 3 /* LFN with dynamic LFN working buffer on the heap */
290 #define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn
291 #define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
292  if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
293  (dobj).lfn = lfn; (dobj).fn = sfn; }
294 #define FREE_BUF() ff_memfree(lfn)
295 
296 #else
297 #error Wrong LFN configuration.
298 #endif
299 
300 
301 
302 
303 /*--------------------------------------------------------------------------
304 
305  Module Private Functions
306 
307 ---------------------------------------------------------------------------*/
308 
309 
310 /*-----------------------------------------------------------------------*/
311 /* String functions */
312 /*-----------------------------------------------------------------------*/
313 
314 /* Copy memory to memory */
315 NODEBUG void mem_cpy( void* dst, const void* src, UINT cnt )
316 {
317  BYTE* d = ( BYTE* )dst;
318  const BYTE* s = ( const BYTE* )src;
319 
320 #if _WORD_ACCESS == 1
321  while ( cnt >= sizeof( int ) )
322  {
323  *( int* )d = *( int* )s;
324  d += sizeof( int ); s += sizeof( int );
325  cnt -= sizeof( int );
326  }
327 #endif
328  while ( cnt-- )
329  *d++ = *s++;
330 }
331 
332 
333 NODEBUG void str_cpy( char* Dest , const char* Src )
334 {
335  while ( *Src )
336  {
337  *Dest++ = *Src++;
338  }
339  *Dest = 0;
340 }
341 
342 
343 
344 /* Fill memory */
345 NODEBUG void mem_set( void* dst, int val, UINT cnt )
346 {
347  BYTE* d = ( BYTE* )dst;
348 
349  while ( cnt-- )
350  *d++ = ( BYTE )val;
351 }
352 
353 /* Compare memory to memory */
354 NODEBUG int mem_cmp( const void* dst, const void* src, UINT cnt )
355 {
356  const BYTE* d = ( const BYTE* )dst, *s = ( const BYTE* )src;
357  int r = 0;
358 
359  while ( cnt-- && ( r = *d++ - *s++ ) == 0 ) ;
360  return r;
361 }
362 
363 /* Check if chr is contained in the string */
364 NODEBUG int chk_chr( const char* str, int chr )
365 {
366  while ( *str && *str != chr ) str++;
367  return *str;
368 }
369 
370 
371 
372 /*-----------------------------------------------------------------------*/
373 /* Request/Release grant to access the volume */
374 /*-----------------------------------------------------------------------*/
375 #if _FS_REENTRANT
376 
377 NODEBUG static
378 int lock_fs(
379  FATFS* fs /* File system object */
380 )
381 {
382  return ff_req_grant( fs->sobj );
383 }
384 
385 
386 NODEBUG static
387 void unlock_fs(
388  FATFS* fs, /* File system object */
389  FRESULT res /* Result code to be returned */
390 )
391 {
392  if ( res != FR_NOT_ENABLED &&
393  res != FR_INVALID_DRIVE &&
394  res != FR_INVALID_OBJECT &&
395  res != FR_TIMEOUT )
396  {
397  ff_rel_grant( fs->sobj );
398  }
399 }
400 #endif
401 
402 
403 
404 /*-----------------------------------------------------------------------*/
405 /* File shareing control functions */
406 /*-----------------------------------------------------------------------*/
407 #if _FS_SHARE
408 
409 NODEBUG static
410 FRESULT chk_lock( /* Check if the file can be accessed */
411  DIR* dj, /* Directory object pointing the file to be checked */
412  int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
413 )
414 {
415  UINT i, be;
416 
417  /* Search file semaphore table */
418  for ( i = be = 0; i < _FS_SHARE; i++ )
419  {
420  if ( Files[i].fs ) /* Existing entry */
421  {
422  if ( Files[i].fs == dj->fs && /* Check if the file matched with an open file */
423  Files[i].clu == dj->sclust &&
424  Files[i].idx == dj->index ) break;
425  }
426  else /* Blank entry */
427  {
428  be++;
429  }
430  }
431  if ( i == _FS_SHARE ) /* The file is not opened */
432  return ( be || acc == 2 ) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */
433 
434  /* The file has been opened. Reject any open against writing file and all write mode open */
435  return ( acc || Files[i].ctr == 0x100 ) ? FR_LOCKED : FR_OK;
436 }
437 
438 
439 NODEBUG static
440 int enq_lock( /* Check if an entry is available for a new file */
441  FATFS* fs /* File system object */
442 )
443 {
444  UINT i;
445 
446  for ( i = 0; i < _FS_SHARE && Files[i].fs; i++ ) ;
447  return ( i == _FS_SHARE ) ? 0 : 1;
448 }
449 
450 
451 NODEBUG static
452 UINT inc_lock( /* Increment file open counter and returns its index (0:int error) */
453  DIR* dj, /* Directory object pointing the file to register or increment */
454  int acc /* Desired access mode (0:Read, !0:Write) */
455 )
456 {
457  UINT i;
458 
459 
460  for ( i = 0; i < _FS_SHARE; i++ ) /* Find the file */
461  {
462  if ( Files[i].fs == dj->fs &&
463  Files[i].clu == dj->sclust &&
464  Files[i].idx == dj->index ) break;
465  }
466 
467  if ( i == _FS_SHARE ) /* Not opened. Register it as new. */
468  {
469  for ( i = 0; i < _FS_SHARE && Files[i].fs; i++ ) ;
470  if ( i == _FS_SHARE ) return 0; /* No space to register (int err) */
471  Files[i].fs = dj->fs;
472  Files[i].clu = dj->sclust;
473  Files[i].idx = dj->index;
474  Files[i].ctr = 0;
475  }
476 
477  if ( acc && Files[i].ctr ) return 0; /* Access violation (int err) */
478 
479  Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */
480 
481  return i + 1;
482 }
483 
484 
485 NODEBUG static
486 FRESULT dec_lock( /* Decrement file open counter */
487  UINT i /* Semaphore index */
488 )
489 {
490  WORD n;
491  FRESULT res;
492 
493 
494  if ( --i < _FS_SHARE )
495  {
496  n = Files[i].ctr;
497  if ( n == 0x100 ) n = 0;
498  if ( n ) n--;
499  Files[i].ctr = n;
500  if ( !n ) Files[i].fs = 0;
501  res = FR_OK;
502  }
503  else
504  {
505  res = FR_INT_ERR;
506  }
507  return res;
508 }
509 
510 
511 NODEBUG static
512 void clear_lock( /* Clear lock entries of the volume */
513  FATFS* fs
514 )
515 {
516  UINT i;
517 
518  for ( i = 0; i < _FS_SHARE; i++ )
519  {
520  if ( Files[i].fs == fs ) Files[i].fs = 0;
521  }
522 }
523 #endif
524 
525 
526 
527 /*-----------------------------------------------------------------------*/
528 /* Change window offset */
529 /*-----------------------------------------------------------------------*/
530 
531 NODEBUG static
532 FRESULT move_window(
533  FATFS* fs, /* File system object */
534  DWORD sector /* Sector number to make appearance in the fs->win[] */
535 ) /* Move to zero only writes back dirty window */
536 {
537  DWORD wsect;
538 
539 
540  wsect = fs->winsect;
541  if ( wsect != sector ) /* Changed current window */
542  {
543 #if !_FS_READONLY
544  if ( fs->wflag ) /* Write back dirty window if needed */
545  {
546  if ( disk_write( fs->drv, fs->win, wsect, 1 ) != RES_OK )
547  return FR_DISK_ERR;
548  fs->wflag = 0;
549  if ( wsect < ( fs->fatbase + fs->fsize ) ) /* In FAT area */
550  {
551  BYTE nf;
552  for ( nf = fs->n_fats; nf > 1; nf-- ) /* Reflect the change to all FAT copies */
553  {
554  wsect += fs->fsize;
555  disk_write( fs->drv, fs->win, wsect, 1 );
556  }
557  }
558  }
559 #endif
560  if ( sector )
561  {
562  if ( disk_read( fs->drv, fs->win, sector, 1 ) != RES_OK )
563  return FR_DISK_ERR;
564  fs->winsect = sector;
565  }
566  }
567 
568  return FR_OK;
569 }
570 
571 
572 
573 
574 /*-----------------------------------------------------------------------*/
575 /* Clean-up cached data */
576 /*-----------------------------------------------------------------------*/
577 #if !_FS_READONLY
578 NODEBUG static
579 FRESULT sync( /* FR_OK: successful, FR_DISK_ERR: failed */
580  FATFS* fs /* File system object */
581 )
582 {
583  FRESULT res;
584 
585 
586  res = move_window( fs, 0 );
587  if ( res == FR_OK )
588  {
589  /* Update FSInfo sector if needed */
590  if ( fs->fs_type == FS_FAT32 && fs->fsi_flag )
591  {
592  fs->winsect = 0;
593  mem_set( fs->win, 0, 512 );
594  ST_WORD( fs->win + BS_55AA, 0xAA55 );
595  ST_DWORD( fs->win + FSI_LeadSig, 0x41615252 );
596  ST_DWORD( fs->win + FSI_StrucSig, 0x61417272 );
597  ST_DWORD( fs->win + FSI_Free_Count, fs->free_clust );
598  ST_DWORD( fs->win + FSI_Nxt_Free, fs->last_clust );
599  disk_write( fs->drv, fs->win, fs->fsi_sector, 1 );
600  fs->fsi_flag = 0;
601  }
602  /* Make sure that no pending write process in the physical drive */
603  if ( disk_ioctl( fs->drv, CTRL_SYNC, ( void* )0 ) != RES_OK )
604  res = FR_DISK_ERR;
605  }
606 
607  return res;
608 }
609 #endif
610 
611 
612 
613 
614 /*-----------------------------------------------------------------------*/
615 /* Get sector# from cluster# */
616 /*-----------------------------------------------------------------------*/
617 
618 
619 NODEBUG2 DWORD clust2sect( /* !=0: Sector number, 0: Failed - invalid cluster# */
620  FATFS* fs, /* File system object */
621  DWORD clst /* Cluster# to be converted */
622 )
623 {
624  clst -= 2;
625  if ( clst >= ( fs->n_fatent - 2 ) ) return 0; /* Invalid cluster# */
626  return clst * fs->csize + fs->database;
627 }
628 
629 
630 
631 
632 /*-----------------------------------------------------------------------*/
633 /* FAT access - Read value of a FAT entry */
634 /*-----------------------------------------------------------------------*/
635 
636 
637 NODEBUG2 DWORD get_fat( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
638  FATFS* fs, /* File system object */
639  DWORD clst /* Cluster# to get the link information */
640 )
641 {
642  UINT wc, bc;
643  BYTE* p;
644 
645 
646  if ( clst < 2 || clst >= fs->n_fatent ) /* Chack range */
647  return 1;
648 
649  switch ( fs->fs_type )
650  {
651  case FS_FAT12 :
652  bc = ( UINT )clst; bc += bc / 2;
653  if ( move_window( fs, fs->fatbase + ( bc / SS( fs ) ) ) ) break;
654  wc = fs->win[bc % SS( fs )]; bc++;
655  if ( move_window( fs, fs->fatbase + ( bc / SS( fs ) ) ) ) break;
656  wc |= fs->win[bc % SS( fs )] << 8;
657  return ( clst & 1 ) ? ( wc >> 4 ) : ( wc & 0xFFF );
658 
659  case FS_FAT16 :
660  if ( move_window( fs, fs->fatbase + ( clst / ( SS( fs ) / 2 ) ) ) ) break;
661  p = &fs->win[clst * 2 % SS( fs )];
662  return LD_WORD( p );
663 
664  case FS_FAT32 :
665  if ( move_window( fs, fs->fatbase + ( clst / ( SS( fs ) / 4 ) ) ) ) break;
666  p = &fs->win[clst * 4 % SS( fs )];
667  return LD_DWORD( p ) & 0x0FFFFFFF;
668  }
669 
670  return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */
671 }
672 
673 
674 
675 
676 /*-----------------------------------------------------------------------*/
677 /* FAT access - Change value of a FAT entry */
678 /*-----------------------------------------------------------------------*/
679 #if !_FS_READONLY
680 
681 NODEBUG2 FRESULT put_fat(
682  FATFS* fs, /* File system object */
683  DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
684  DWORD val /* New value to mark the cluster */
685 )
686 {
687  UINT bc;
688  BYTE* p;
689  FRESULT res;
690 
691 
692  if ( clst < 2 || clst >= fs->n_fatent ) /* Check range */
693  {
694  res = FR_INT_ERR;
695 
696  }
697  else
698  {
699  switch ( fs->fs_type )
700  {
701  case FS_FAT12 :
702  bc = clst; bc += bc / 2;
703  res = move_window( fs, fs->fatbase + ( bc / SS( fs ) ) );
704  if ( res != FR_OK ) break;
705  p = &fs->win[bc % SS( fs )];
706  *p = ( clst & 1 ) ? ( ( *p & 0x0F ) | ( ( BYTE )val << 4 ) ) : ( BYTE )val;
707  bc++;
708  fs->wflag = 1;
709  res = move_window( fs, fs->fatbase + ( bc / SS( fs ) ) );
710  if ( res != FR_OK ) break;
711  p = &fs->win[bc % SS( fs )];
712  *p = ( clst & 1 ) ? ( BYTE )( val >> 4 ) : ( ( *p & 0xF0 ) | ( ( BYTE )( val >> 8 ) & 0x0F ) );
713  break;
714 
715  case FS_FAT16 :
716  res = move_window( fs, fs->fatbase + ( clst / ( SS( fs ) / 2 ) ) );
717  if ( res != FR_OK ) break;
718  p = &fs->win[clst * 2 % SS( fs )];
719  ST_WORD( p, ( WORD )val );
720  break;
721 
722  case FS_FAT32 :
723  res = move_window( fs, fs->fatbase + ( clst / ( SS( fs ) / 4 ) ) );
724  if ( res != FR_OK ) break;
725  p = &fs->win[clst * 4 % SS( fs )];
726  val |= LD_DWORD( p ) & 0xF0000000;
727  ST_DWORD( p, val );
728  break;
729 
730  default :
731  res = FR_INT_ERR;
732  }
733  fs->wflag = 1;
734  }
735 
736  return res;
737 }
738 #endif /* !_FS_READONLY */
739 
740 
741 
742 
743 /*-----------------------------------------------------------------------*/
744 /* FAT handling - Remove a cluster chain */
745 /*-----------------------------------------------------------------------*/
746 #if !_FS_READONLY
747 NODEBUG static
748 FRESULT remove_chain(
749  FATFS* fs, /* File system object */
750  DWORD clst /* Cluster# to remove a chain from */
751 )
752 {
753  FRESULT res;
754  DWORD nxt;
755 #if _USE_ERASE
756  DWORD scl = clst, ecl = clst, resion[2];
757 #endif
758 
759  if ( clst < 2 || clst >= fs->n_fatent ) /* Check range */
760  {
761  res = FR_INT_ERR;
762 
763  }
764  else
765  {
766  res = FR_OK;
767  while ( clst < fs->n_fatent ) /* Not a last link? */
768  {
769  nxt = get_fat( fs, clst ); /* Get cluster status */
770  if ( nxt == 0 ) break; /* Empty cluster? */
771  if ( nxt == 1 ) { res = FR_INT_ERR; break; } /* Internal error? */
772  if ( nxt == 0xFFFFFFFF ) { res = FR_DISK_ERR; break; } /* Disk error? */
773  res = put_fat( fs, clst, 0 ); /* Mark the cluster "empty" */
774  if ( res != FR_OK ) break;
775  if ( fs->free_clust != 0xFFFFFFFF ) /* Update FSInfo */
776  {
777  fs->free_clust++;
778  fs->fsi_flag = 1;
779  }
780 #if _USE_ERASE
781  if ( ecl + 1 == nxt ) /* Next cluster is contiguous */
782  {
783  ecl = nxt;
784  }
785  else /* End of contiguous clusters */
786  {
787  resion[0] = clust2sect( fs, scl ); /* Start sector */
788  resion[1] = clust2sect( fs, ecl ) + fs->csize - 1; /* End sector */
789  disk_ioctl( fs->drv, CTRL_ERASE_SECTOR, resion ); /* Erase the block */
790  scl = ecl = nxt;
791  }
792 #endif
793  clst = nxt; /* Next cluster */
794  }
795  }
796 
797  return res;
798 }
799 #endif
800 
801 
802 
803 
804 /*-----------------------------------------------------------------------*/
805 /* FAT handling - Stretch or Create a cluster chain */
806 /*-----------------------------------------------------------------------*/
807 #if !_FS_READONLY
808 NODEBUG static
809 DWORD create_chain( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
810  FATFS* fs, /* File system object */
811  DWORD clst /* Cluster# to stretch. 0 means create a new chain. */
812 )
813 {
814  DWORD cs, ncl, scl;
815  FRESULT res;
816 
817 
818  if ( clst == 0 ) /* Create a new chain */
819  {
820  scl = fs->last_clust; /* Get suggested start point */
821  if ( !scl || scl >= fs->n_fatent ) scl = 1;
822  }
823  else /* Stretch the current chain */
824  {
825  cs = get_fat( fs, clst ); /* Check the cluster status */
826  if ( cs < 2 ) return 1; /* It is an invalid cluster */
827  if ( cs < fs->n_fatent ) return cs; /* It is already followed by next cluster */
828  scl = clst;
829  }
830 
831  ncl = scl; /* Start cluster */
832  for ( ;; )
833  {
834  ncl++; /* Next cluster */
835  if ( ncl >= fs->n_fatent ) /* Wrap around */
836  {
837  ncl = 2;
838  if ( ncl > scl ) return 0; /* No free cluster */
839  }
840  cs = get_fat( fs, ncl ); /* Get the cluster status */
841  if ( cs == 0 ) break; /* Found a free cluster */
842  if ( cs == 0xFFFFFFFF || cs == 1 ) /* An error occurred */
843  return cs;
844  if ( ncl == scl ) return 0; /* No free cluster */
845  }
846 
847  res = put_fat( fs, ncl, 0x0FFFFFFF ); /* Mark the new cluster "last link" */
848  if ( res == FR_OK && clst != 0 )
849  {
850  res = put_fat( fs, clst, ncl ); /* Link it to the previous one if needed */
851  }
852  if ( res == FR_OK )
853  {
854  fs->last_clust = ncl; /* Update FSINFO */
855  if ( fs->free_clust != 0xFFFFFFFF )
856  {
857  fs->free_clust--;
858  fs->fsi_flag = 1;
859  }
860  }
861  else
862  {
863  ncl = ( res == FR_DISK_ERR ) ? 0xFFFFFFFF : 1;
864  }
865 
866  return ncl; /* Return new cluster number or error code */
867 }
868 #endif /* !_FS_READONLY */
869 
870 
871 
872 
873 /*-----------------------------------------------------------------------*/
874 /* Directory handling - Set directory index */
875 /*-----------------------------------------------------------------------*/
876 
877 NODEBUG static
878 FRESULT dir_sdi(
879  DIR* dj, /* Pointer to directory object */
880  WORD idx /* Directory index number */
881 )
882 {
883  DWORD clst;
884  WORD ic;
885 
886 
887  dj->index = idx;
888  clst = dj->sclust;
889  if ( clst == 1 || clst >= dj->fs->n_fatent ) /* Check start cluster range */
890  return FR_INT_ERR;
891  if ( !clst && dj->fs->fs_type == FS_FAT32 ) /* Replace cluster# 0 with root cluster# if in FAT32 */
892  clst = dj->fs->dirbase;
893 
894  if ( clst == 0 ) /* Static table (root-dir in FAT12/16) */
895  {
896  dj->clust = clst;
897  if ( idx >= dj->fs->n_rootdir ) /* Index is out of range */
898  return FR_INT_ERR;
899  dj->sect = dj->fs->dirbase + idx / ( SS( dj->fs ) / 32 ); /* Sector# */
900  }
901  else /* Dynamic table (sub-dirs or root-dir in FAT32) */
902  {
903  ic = SS( dj->fs ) / 32 * dj->fs->csize; /* Entries per cluster */
904  while ( idx >= ic ) /* Follow cluster chain */
905  {
906  clst = get_fat( dj->fs, clst ); /* Get next cluster */
907  if ( clst == 0xFFFFFFFF ) return FR_DISK_ERR; /* Disk error */
908  if ( clst < 2 || clst >= dj->fs->n_fatent ) /* Reached to end of table or int error */
909  return FR_INT_ERR;
910  idx -= ic;
911  }
912  dj->clust = clst;
913  dj->sect = clust2sect( dj->fs, clst ) + idx / ( SS( dj->fs ) / 32 ); /* Sector# */
914  }
915 
916  dj->dir = dj->fs->win + ( idx % ( SS( dj->fs ) / 32 ) ) * 32; /* Ptr to the entry in the sector */
917 
918  return FR_OK; /* Seek succeeded */
919 }
920 
921 
922 
923 
924 /*-----------------------------------------------------------------------*/
925 /* Directory handling - Move directory index next */
926 /*-----------------------------------------------------------------------*/
927 
928 NODEBUG static
929 FRESULT dir_next( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
930  DIR* dj, /* Pointer to directory object */
931  int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
932 )
933 {
934  DWORD clst;
935  WORD i;
936 
937 
938  i = dj->index + 1;
939  if ( !i || !dj->sect ) /* Report EOT when index has reached 65535 */
940  return FR_NO_FILE;
941 
942  if ( !( i % ( SS( dj->fs ) / 32 ) ) ) /* Sector changed? */
943  {
944  dj->sect++; /* Next sector */
945 
946  if ( dj->clust == 0 ) /* Static table */
947  {
948  if ( i >= dj->fs->n_rootdir ) /* Report EOT when end of table */
949  return FR_NO_FILE;
950  }
951  else /* Dynamic table */
952  {
953  if ( ( ( i / ( SS( dj->fs ) / 32 ) ) & ( dj->fs->csize - 1 ) ) == 0 ) /* Cluster changed? */
954  {
955  clst = get_fat( dj->fs, dj->clust ); /* Get next cluster */
956  if ( clst <= 1 ) return FR_INT_ERR;
957  if ( clst == 0xFFFFFFFF ) return FR_DISK_ERR;
958  if ( clst >= dj->fs->n_fatent ) /* When it reached end of dynamic table */
959  {
960 #if !_FS_READONLY
961  BYTE c;
962  if ( !stretch ) return FR_NO_FILE; /* When do not stretch, report EOT */
963  clst = create_chain( dj->fs, dj->clust ); /* Stretch cluster chain */
964  if ( clst == 0 ) return FR_DENIED; /* No free cluster */
965  if ( clst == 1 ) return FR_INT_ERR;
966  if ( clst == 0xFFFFFFFF ) return FR_DISK_ERR;
967  /* Clean-up stretched table */
968  if ( move_window( dj->fs, 0 ) ) return FR_DISK_ERR; /* Flush active window */
969  mem_set( dj->fs->win, 0, SS( dj->fs ) ); /* Clear window buffer */
970  dj->fs->winsect = clust2sect( dj->fs, clst ); /* Cluster start sector */
971  for ( c = 0; c < dj->fs->csize; c++ ) /* Fill the new cluster with 0 */
972  {
973  dj->fs->wflag = 1;
974  if ( move_window( dj->fs, 0 ) ) return FR_DISK_ERR;
975  dj->fs->winsect++;
976  }
977  dj->fs->winsect -= c; /* Rewind window address */
978 #else
979  return FR_NO_FILE; /* Report EOT */
980 #endif
981  }
982  dj->clust = clst; /* Initialize data for new cluster */
983  dj->sect = clust2sect( dj->fs, clst );
984  }
985  }
986  }
987 
988  dj->index = i;
989  dj->dir = dj->fs->win + ( i % ( SS( dj->fs ) / 32 ) ) * 32;
990 
991  return FR_OK;
992 }
993 
994 
995 
996 
997 /*-----------------------------------------------------------------------*/
998 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */
999 /*-----------------------------------------------------------------------*/
1000 #if _USE_LFN
1001 static
1002 const BYTE LfnOfs[] = {1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30}; /* Offset of LFN chars in the directory entry */
1003 
1004 
1005 NODEBUG static
1006 int cmp_lfn( /* 1:Matched, 0:Not matched */
1007  WCHAR* lfnbuf, /* Pointer to the LFN to be compared */
1008  BYTE* dir /* Pointer to the directory entry containing a part of LFN */
1009 )
1010 {
1011  UINT i, s;
1012  WCHAR wc, uc;
1013 
1014 
1015  i = ( ( dir[LDIR_Ord] & 0xBF ) - 1 ) * 13; /* Get offset in the LFN buffer */
1016  s = 0; wc = 1;
1017  do
1018  {
1019  uc = LD_WORD( dir + LfnOfs[s] ); /* Pick an LFN character from the entry */
1020  if ( wc ) /* Last char has not been processed */
1021  {
1022  wc = ff_wtoupper( uc ); /* Convert it to upper case */
1023  if ( i >= _MAX_LFN || wc != ff_wtoupper( lfnbuf[i++] ) ) /* Compare it */
1024  return 0; /* Not matched */
1025  }
1026  else
1027  {
1028  if ( uc != 0xFFFF ) return 0; /* Check filler */
1029  }
1030  }
1031  while ( ++s < 13 ); /* Repeat until all chars in the entry are checked */
1032 
1033  if ( ( dir[LDIR_Ord] & 0x40 ) && wc && lfnbuf[i] ) /* Last segment matched but different length */
1034  return 0;
1035 
1036  return 1; /* The part of LFN matched */
1037 }
1038 
1039 
1040 
1041 NODEBUG static
1042 int pick_lfn( /* 1:Succeeded, 0:Buffer overflow */
1043  WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */
1044  BYTE* dir /* Pointer to the directory entry */
1045 )
1046 {
1047  UINT i, s;
1048  WCHAR wc, uc;
1049 
1050 
1051  i = ( ( dir[LDIR_Ord] & 0x3F ) - 1 ) * 13; /* Offset in the LFN buffer */
1052 
1053  s = 0; wc = 1;
1054  do
1055  {
1056  uc = LD_WORD( dir + LfnOfs[s] ); /* Pick an LFN character from the entry */
1057  if ( wc ) /* Last char has not been processed */
1058  {
1059  if ( i >= _MAX_LFN ) return 0; /* Buffer overflow? */
1060  lfnbuf[i++] = wc = uc; /* Store it */
1061  }
1062  else
1063  {
1064  if ( uc != 0xFFFF ) return 0; /* Check filler */
1065  }
1066  }
1067  while ( ++s < 13 ); /* Read all character in the entry */
1068 
1069  if ( dir[LDIR_Ord] & 0x40 ) /* Put terminator if it is the last LFN part */
1070  {
1071  if ( i >= _MAX_LFN ) return 0; /* Buffer overflow? */
1072  lfnbuf[i] = 0;
1073  }
1074 
1075  return 1;
1076 }
1077 
1078 
1079 #if !_FS_READONLY
1080 NODEBUG static
1081 void fit_lfn(
1082  const WCHAR* lfnbuf, /* Pointer to the LFN buffer */
1083  BYTE* dir, /* Pointer to the directory entry */
1084  BYTE ord, /* LFN order (1-20) */
1085  BYTE sum /* SFN sum */
1086 )
1087 {
1088  UINT i, s;
1089  WCHAR wc;
1090 
1091 
1092  dir[LDIR_Chksum] = sum; /* Set check sum */
1093  dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */
1094  dir[LDIR_Type] = 0;
1095  ST_WORD( dir + LDIR_FstClusLO, 0 );
1096 
1097  i = ( ord - 1 ) * 13; /* Get offset in the LFN buffer */
1098  s = wc = 0;
1099  do
1100  {
1101  if ( wc != 0xFFFF ) wc = lfnbuf[i++]; /* Get an effective char */
1102  ST_WORD( dir + LfnOfs[s], wc ); /* Put it */
1103  if ( !wc ) wc = 0xFFFF; /* Padding chars following last char */
1104  }
1105  while ( ++s < 13 );
1106  if ( wc == 0xFFFF || !lfnbuf[i] ) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */
1107  dir[LDIR_Ord] = ord; /* Set the LFN order */
1108 }
1109 
1110 #endif
1111 #endif
1112 
1113 
1114 
1115 /*-----------------------------------------------------------------------*/
1116 /* Create numbered name */
1117 /*-----------------------------------------------------------------------*/
1118 #if _USE_LFN
1119 NODEBUG2 void gen_numname(
1120  BYTE* dst, /* Pointer to generated SFN */
1121  const BYTE* src, /* Pointer to source SFN to be modified */
1122  const WCHAR* lfn, /* Pointer to LFN */
1123  WORD seq /* Sequence number */
1124 )
1125 {
1126  BYTE ns[8], c;
1127  UINT i, j;
1128 
1129 
1130  mem_cpy( dst, src, 11 );
1131 
1132  if ( seq > 5 ) /* On many collisions, generate a hash number instead of sequential number */
1133  {
1134  do seq = ( seq >> 1 ) + ( seq << 15 ) + ( WORD ) * lfn++; while ( *lfn );
1135  }
1136 
1137  /* itoa */
1138  i = 7;
1139  do
1140  {
1141  c = ( seq % 16 ) + '0';
1142  if ( c > '9' ) c += 7;
1143  ns[i--] = c;
1144  seq /= 16;
1145  }
1146  while ( seq );
1147  ns[i] = '~';
1148 
1149  /* Append the number */
1150  for ( j = 0; j < i && dst[j] != ' '; j++ )
1151  {
1152  if ( IsDBCS1( dst[j] ) )
1153  {
1154  if ( j == i - 1 ) break;
1155  j++;
1156  }
1157  }
1158  do
1159  {
1160  dst[j++] = ( i < 8 ) ? ns[i++] : ' ';
1161  }
1162  while ( j < 8 );
1163 }
1164 #endif
1165 
1166 
1167 
1168 
1169 /*-----------------------------------------------------------------------*/
1170 /* Calculate sum of an SFN */
1171 /*-----------------------------------------------------------------------*/
1172 #if _USE_LFN
1173 NODEBUG static
1174 BYTE sum_sfn(
1175  const BYTE* dir /* Ptr to directory entry */
1176 )
1177 {
1178  BYTE sum = 0;
1179  UINT n = 11;
1180 
1181  do sum = ( sum >> 1 ) + ( sum << 7 ) + *dir++; while ( --n );
1182  return sum;
1183 }
1184 #endif
1185 
1186 
1187 
1188 
1189 /*-----------------------------------------------------------------------*/
1190 /* Directory handling - Find an object in the directory */
1191 /*-----------------------------------------------------------------------*/
1192 
1193 NODEBUG static
1194 FRESULT dir_find(
1195  DIR* dj /* Pointer to the directory object linked to the file name */
1196 )
1197 {
1198  FRESULT res;
1199  BYTE c, *dir;
1200 #if _USE_LFN
1201  BYTE a, ord, sum;
1202 #endif
1203 
1204  res = dir_sdi( dj, 0 ); /* Rewind directory object */
1205  if ( res != FR_OK ) return res;
1206 
1207 #if _USE_LFN
1208  ord = sum = 0xFF;
1209 #endif
1210  do
1211  {
1212  res = move_window( dj->fs, dj->sect );
1213  if ( res != FR_OK ) break;
1214  dir = dj->dir; /* Ptr to the directory entry of current index */
1215  c = dir[DIR_Name];
1216  if ( c == 0 ) { res = FR_NO_FILE; break; } /* Reached to end of table */
1217 #if _USE_LFN /* LFN configuration */
1218  a = dir[DIR_Attr] & AM_MASK;
1219  if ( c == 0xE5 || ( ( a & AM_VOL ) && a != AM_LFN ) ) /* An entry without valid data */
1220  {
1221  ord = 0xFF;
1222  }
1223  else
1224  {
1225  if ( a == AM_LFN ) /* An LFN entry is found */
1226  {
1227  if ( dj->lfn )
1228  {
1229  if ( c & 0x40 ) /* Is it start of LFN sequence? */
1230  {
1231  sum = dir[LDIR_Chksum];
1232  c &= 0xBF; ord = c; /* LFN start order */
1233  dj->lfn_idx = dj->index;
1234  }
1235  /* Check validity of the LFN entry and compare it with given name */
1236  ord = ( c == ord && sum == dir[LDIR_Chksum] && cmp_lfn( dj->lfn, dir ) ) ? ord - 1 : 0xFF;
1237  }
1238  }
1239  else /* An SFN entry is found */
1240  {
1241  if ( !ord && sum == sum_sfn( dir ) ) break; /* LFN matched? */
1242  ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */
1243  if ( !( dj->fn[NS] & NS_LOSS ) && !mem_cmp( dir, dj->fn, 11 ) ) break; /* SFN matched? */
1244  }
1245  }
1246 #else /* Non LFN configuration */
1247  if ( !( dir[DIR_Attr] & AM_VOL ) && !mem_cmp( dir, dj->fn, 11 ) ) /* Is it a valid entry? */
1248  break;
1249 #endif
1250  res = dir_next( dj, 0 ); /* Next entry */
1251  }
1252  while ( res == FR_OK );
1253 
1254  return res;
1255 }
1256 
1257 
1258 
1259 
1260 /*-----------------------------------------------------------------------*/
1261 /* Read an object from the directory */
1262 /*-----------------------------------------------------------------------*/
1263 #if _FS_MINIMIZE <= 1
1264 NODEBUG static
1265 FRESULT dir_read(
1266  DIR* dj /* Pointer to the directory object that pointing the entry to be read */
1267 )
1268 {
1269  FRESULT res;
1270  BYTE c, *dir;
1271 #if _USE_LFN
1272  BYTE a, ord = 0xFF, sum = 0xFF;
1273 #endif
1274 
1275  res = FR_NO_FILE;
1276  while ( dj->sect )
1277  {
1278  res = move_window( dj->fs, dj->sect );
1279  if ( res != FR_OK ) break;
1280  dir = dj->dir; /* Ptr to the directory entry of current index */
1281  c = dir[DIR_Name];
1282  if ( c == 0 ) { res = FR_NO_FILE; break; } /* Reached to end of table */
1283 #if _USE_LFN /* LFN configuration */
1284  a = dir[DIR_Attr] & AM_MASK;
1285  if ( c == 0xE5 || ( !_FS_RPATH && c == '.' ) || ( ( a & AM_VOL ) && a != AM_LFN ) ) /* An entry without valid data */
1286  {
1287  ord = 0xFF;
1288  }
1289  else
1290  {
1291  if ( a == AM_LFN ) /* An LFN entry is found */
1292  {
1293  if ( c & 0x40 ) /* Is it start of LFN sequence? */
1294  {
1295  sum = dir[LDIR_Chksum];
1296  c &= 0xBF; ord = c;
1297  dj->lfn_idx = dj->index;
1298  }
1299  /* Check LFN validity and capture it */
1300  ord = ( c == ord && sum == dir[LDIR_Chksum] && pick_lfn( dj->lfn, dir ) ) ? ord - 1 : 0xFF;
1301  }
1302  else /* An SFN entry is found */
1303  {
1304  if ( ord || sum != sum_sfn( dir ) ) /* Is there a valid LFN? */
1305  dj->lfn_idx = 0xFFFF; /* It has no LFN. */
1306  break;
1307  }
1308  }
1309 #else /* Non LFN configuration */
1310  if ( c != 0xE5 && ( _FS_RPATH || c != '.' ) && !( dir[DIR_Attr] & AM_VOL ) ) /* Is it a valid entry? */
1311  break;
1312 #endif
1313  res = dir_next( dj, 0 ); /* Next entry */
1314  if ( res != FR_OK ) break;
1315  }
1316 
1317  if ( res != FR_OK ) dj->sect = 0;
1318 
1319  return res;
1320 }
1321 #endif
1322 
1323 
1324 
1325 /*-----------------------------------------------------------------------*/
1326 /* Register an object to the directory */
1327 /*-----------------------------------------------------------------------*/
1328 #if !_FS_READONLY
1329 NODEBUG static
1330 FRESULT dir_register( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
1331  DIR* dj /* Target directory with object name to be created */
1332 )
1333 {
1334  FRESULT res;
1335  BYTE c, *dir;
1336 #if _USE_LFN /* LFN configuration */
1337  WORD n, ne, is;
1338  BYTE sn[12], *fn, sum;
1339  WCHAR* lfn;
1340 
1341 
1342  fn = dj->fn; lfn = dj->lfn;
1343  mem_cpy( sn, fn, 12 );
1344 
1345  if ( _FS_RPATH && ( sn[NS] & NS_DOT ) ) /* Cannot create dot entry */
1346  return FR_INVALID_NAME;
1347 
1348  if ( sn[NS] & NS_LOSS ) /* When LFN is out of 8.3 format, generate a numbered name */
1349  {
1350  fn[NS] = 0; dj->lfn = 0; /* Find only SFN */
1351  for ( n = 1; n < 100; n++ )
1352  {
1353  gen_numname( fn, sn, lfn, n ); /* Generate a numbered name */
1354  res = dir_find( dj ); /* Check if the name collides with existing SFN */
1355  if ( res != FR_OK ) break;
1356  }
1357  if ( n == 100 ) return FR_DENIED; /* Abort if too many collisions */
1358  if ( res != FR_NO_FILE ) return res; /* Abort if the result is other than 'not collided' */
1359  fn[NS] = sn[NS]; dj->lfn = lfn;
1360  }
1361 
1362  if ( sn[NS] & NS_LFN ) /* When LFN is to be created, reserve an SFN + LFN entries. */
1363  {
1364  for ( ne = 0; lfn[ne]; ne++ ) ;
1365  ne = ( ne + 25 ) / 13;
1366  }
1367  else /* Otherwise reserve only an SFN entry. */
1368  {
1369  ne = 1;
1370  }
1371 
1372  /* Reserve contiguous entries */
1373  res = dir_sdi( dj, 0 );
1374  if ( res != FR_OK ) return res;
1375  n = is = 0;
1376  do
1377  {
1378  res = move_window( dj->fs, dj->sect );
1379  if ( res != FR_OK ) break;
1380  c = *dj->dir; /* Check the entry status */
1381  if ( c == 0xE5 || c == 0 ) /* Is it a blank entry? */
1382  {
1383  if ( n == 0 ) is = dj->index; /* First index of the contiguous entry */
1384  if ( ++n == ne ) break; /* A contiguous entry that required count is found */
1385  }
1386  else
1387  {
1388  n = 0; /* Not a blank entry. Restart to search */
1389  }
1390  res = dir_next( dj, 1 ); /* Next entry with table stretch */
1391  }
1392  while ( res == FR_OK );
1393 
1394  if ( res == FR_OK && ne > 1 ) /* Initialize LFN entry if needed */
1395  {
1396  res = dir_sdi( dj, is );
1397  if ( res == FR_OK )
1398  {
1399  sum = sum_sfn( dj->fn ); /* Sum of the SFN tied to the LFN */
1400  ne--;
1401  do /* Store LFN entries in bottom first */
1402  {
1403  res = move_window( dj->fs, dj->sect );
1404  if ( res != FR_OK ) break;
1405  fit_lfn( dj->lfn, dj->dir, ( BYTE )ne, sum );
1406  dj->fs->wflag = 1;
1407  res = dir_next( dj, 0 ); /* Next entry */
1408  }
1409  while ( res == FR_OK && --ne );
1410  }
1411  }
1412 
1413 #else /* Non LFN configuration */
1414  res = dir_sdi( dj, 0 );
1415  if ( res == FR_OK )
1416  {
1417  do /* Find a blank entry for the SFN */
1418  {
1419  res = move_window( dj->fs, dj->sect );
1420  if ( res != FR_OK ) break;
1421  c = *dj->dir;
1422  if ( c == 0xE5 || c == 0 ) break; /* Is it a blank entry? */
1423  res = dir_next( dj, 1 ); /* Next entry with table stretch */
1424  }
1425  while ( res == FR_OK );
1426  }
1427 #endif
1428 
1429  if ( res == FR_OK ) /* Initialize the SFN entry */
1430  {
1431  res = move_window( dj->fs, dj->sect );
1432  if ( res == FR_OK )
1433  {
1434  dir = dj->dir;
1435  mem_set( dir, 0, 32 ); /* Clean the entry */
1436  mem_cpy( dir, dj->fn, 11 ); /* Put SFN */
1437 #if _USE_LFN
1438  dir[DIR_NTres] = *( dj->fn + NS ) & ( NS_BODY | NS_EXT ); /* Put NT flag */
1439 #endif
1440  dj->fs->wflag = 1;
1441  }
1442  }
1443 
1444  return res;
1445 }
1446 #endif /* !_FS_READONLY */
1447 
1448 
1449 
1450 
1451 /*-----------------------------------------------------------------------*/
1452 /* Remove an object from the directory */
1453 /*-----------------------------------------------------------------------*/
1454 #if !_FS_READONLY && !_FS_MINIMIZE
1455 NODEBUG static
1456 FRESULT dir_remove( /* FR_OK: Successful, FR_DISK_ERR: A disk error */
1457  DIR* dj /* Directory object pointing the entry to be removed */
1458 )
1459 {
1460  FRESULT res;
1461 #if _USE_LFN /* LFN configuration */
1462  WORD i;
1463 
1464  i = dj->index; /* SFN index */
1465  res = dir_sdi( dj, ( WORD )( ( dj->lfn_idx == 0xFFFF ) ? i : dj->lfn_idx ) ); /* Goto the SFN or top of the LFN entries */
1466  if ( res == FR_OK )
1467  {
1468  do
1469  {
1470  res = move_window( dj->fs, dj->sect );
1471  if ( res != FR_OK ) break;
1472  *dj->dir = 0xE5; /* Mark the entry "deleted" */
1473  dj->fs->wflag = 1;
1474  if ( dj->index >= i ) break; /* When reached SFN, all entries of the object has been deleted. */
1475  res = dir_next( dj, 0 ); /* Next entry */
1476  }
1477  while ( res == FR_OK );
1478  if ( res == FR_NO_FILE ) res = FR_INT_ERR;
1479  }
1480 
1481 #else /* Non LFN configuration */
1482  res = dir_sdi( dj, dj->index );
1483  if ( res == FR_OK )
1484  {
1485  res = move_window( dj->fs, dj->sect );
1486  if ( res == FR_OK )
1487  {
1488  *dj->dir = 0xE5; /* Mark the entry "deleted" */
1489  dj->fs->wflag = 1;
1490  }
1491  }
1492 #endif
1493 
1494  return res;
1495 }
1496 #endif /* !_FS_READONLY */
1497 
1498 
1499 
1500 
1501 /*-----------------------------------------------------------------------*/
1502 /* Pick a segment and create the object name in directory form */
1503 /*-----------------------------------------------------------------------*/
1504 
1505 NODEBUG static
1506 FRESULT create_name(
1507  DIR* dj, /* Pointer to the directory object */
1508  const TCHAR** path /* Pointer to pointer to the segment in the path string */
1509 )
1510 {
1511 #ifdef _EXCVT
1512  static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */
1513 #endif
1514 
1515 #if _USE_LFN /* LFN configuration */
1516  BYTE b, cf;
1517  WCHAR w, *lfn;
1518  UINT i, ni, si, di;
1519  const TCHAR* p;
1520 
1521  /* Create LFN in Unicode */
1522  si = di = 0;
1523  p = *path;
1524  lfn = dj->lfn;
1525  for ( ;; )
1526  {
1527  w = p[si++]; /* Get a character */
1528  if ( w < ' ' || w == '/' || w == '\\' ) break; /* Break on end of segment */
1529  if ( di >= _MAX_LFN ) /* Reject too long name */
1530  return FR_INVALID_NAME;
1531 #if !_LFN_UNICODE
1532  w &= 0xFF;
1533  if ( IsDBCS1( w ) ) /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
1534  {
1535  b = ( BYTE )p[si++]; /* Get 2nd byte */
1536  if ( !IsDBCS2( b ) )
1537  return FR_INVALID_NAME; /* Reject invalid sequence */
1538  w = ( w << 8 ) + b; /* Create a DBC */
1539  }
1540  w = ff_convert( w, 1 ); /* Convert ANSI/OEM to Unicode */
1541  if ( !w ) return FR_INVALID_NAME; /* Reject invalid code */
1542 #endif
1543  if ( w < 0x80 && chk_chr( "\"*:<>\?|\x7F", w ) ) /* Reject illegal chars for LFN */
1544  return FR_INVALID_NAME;
1545  lfn[di++] = w; /* Store the Unicode char */
1546  }
1547  *path = &p[si]; /* Return pointer to the next segment */
1548  cf = ( w < ' ' ) ? NS_LAST : 0; /* Set last segment flag if end of path */
1549 #if _FS_RPATH
1550  if ( ( di == 1 && lfn[di - 1] == '.' ) || /* Is this a dot entry? */
1551  ( di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.' ) )
1552  {
1553  lfn[di] = 0;
1554  for ( i = 0; i < 11; i++ )
1555  dj->fn[i] = ( i < di ) ? '.' : ' ';
1556  dj->fn[i] = cf | NS_DOT; /* This is a dot entry */
1557  return FR_OK;
1558  }
1559 #endif
1560  while ( di ) /* Strip trailing spaces and dots */
1561  {
1562  w = lfn[di - 1];
1563  if ( w != ' ' && w != '.' ) break;
1564  di--;
1565  }
1566  if ( !di ) return FR_INVALID_NAME; /* Reject nul string */
1567 
1568  lfn[di] = 0; /* LFN is created */
1569 
1570  /* Create SFN in directory form */
1571  mem_set( dj->fn, ' ', 11 );
1572  for ( si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++ ) ; /* Strip leading spaces and dots */
1573  if ( si ) cf |= NS_LOSS | NS_LFN;
1574  while ( di && lfn[di - 1] != '.' ) di--; /* Find extension (di<=si: no extension) */
1575 
1576  b = i = 0; ni = 8;
1577  for ( ;; )
1578  {
1579  w = lfn[si++]; /* Get an LFN char */
1580  if ( !w ) break; /* Break on end of the LFN */
1581  if ( w == ' ' || ( w == '.' && si != di ) ) /* Remove spaces and dots */
1582  {
1583  cf |= NS_LOSS | NS_LFN; continue;
1584  }
1585 
1586  if ( i >= ni || si == di ) /* Extension or end of SFN */
1587  {
1588  if ( ni == 11 ) /* Long extension */
1589  {
1590  cf |= NS_LOSS | NS_LFN; break;
1591  }
1592  if ( si != di ) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */
1593  if ( si > di ) break; /* No extension */
1594  si = di; i = 8; ni = 11; /* Enter extension section */
1595  b <<= 2; continue;
1596  }
1597 
1598  if ( w >= 0x80 ) /* Non ASCII char */
1599  {
1600 #ifdef _EXCVT
1601  w = ff_convert( w, 0 ); /* Unicode -> OEM code */
1602  if ( w ) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */
1603 #else
1604  w = ff_convert( ff_wtoupper( w ), 0 ); /* Upper converted Unicode -> OEM code */
1605 #endif
1606  cf |= NS_LFN; /* Force create LFN entry */
1607  }
1608 
1609  if ( _DF1S && w >= 0x100 ) /* Double byte char (always false on SBCS cfg) */
1610  {
1611  if ( i >= ni - 1 )
1612  {
1613  cf |= NS_LOSS | NS_LFN; i = ni; continue;
1614  }
1615  dj->fn[i++] = ( BYTE )( w >> 8 );
1616  }
1617  else /* Single byte char */
1618  {
1619  if ( !w || chk_chr( "+,;=[]", w ) ) /* Replace illegal chars for SFN */
1620  {
1621  w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
1622  }
1623  else
1624  {
1625  if ( IsUpper( w ) ) /* ASCII large capital */
1626  {
1627  b |= 2;
1628  }
1629  else
1630  {
1631  if ( IsLower( w ) ) /* ASCII small capital */
1632  {
1633  b |= 1; w -= 0x20;
1634  }
1635  }
1636  }
1637  }
1638  dj->fn[i++] = ( BYTE )w;
1639  }
1640 
1641  if ( dj->fn[0] == 0xE5 ) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */
1642 
1643  if ( ni == 8 ) b <<= 2;
1644  if ( ( b & 0x0C ) == 0x0C || ( b & 0x03 ) == 0x03 ) /* Create LFN entry when there are composite capitals */
1645  cf |= NS_LFN;
1646  if ( !( cf & NS_LFN ) ) /* When LFN is in 8.3 format without extended char, NT flags are created */
1647  {
1648  if ( ( b & 0x03 ) == 0x01 ) cf |= NS_EXT; /* NT flag (Extension has only small capital) */
1649  if ( ( b & 0x0C ) == 0x04 ) cf |= NS_BODY; /* NT flag (Filename has only small capital) */
1650  }
1651 
1652  dj->fn[NS] = cf; /* SFN is created */
1653 
1654  return FR_OK;
1655 
1656 
1657 #else /* Non-LFN configuration */
1658  BYTE b, c, d, *sfn;
1659  UINT ni, si, i;
1660  const char* p;
1661 
1662  /* Create file name in directory form */
1663  sfn = dj->fn;
1664  mem_set( sfn, ' ', 11 );
1665  si = i = b = 0; ni = 8;
1666  p = *path;
1667 #if _FS_RPATH
1668  if ( p[si] == '.' ) /* Is this a dot entry? */
1669  {
1670  for ( ;; )
1671  {
1672  c = ( BYTE )p[si++];
1673  if ( c != '.' || si >= 3 ) break;
1674  sfn[i++] = c;
1675  }
1676  if ( c != '/' && c != '\\' && c > ' ' ) return FR_INVALID_NAME;
1677  *path = &p[si]; /* Return pointer to the next segment */
1678  sfn[NS] = ( c <= ' ' ) ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */
1679  return FR_OK;
1680  }
1681 #endif
1682  for ( ;; )
1683  {
1684  c = ( BYTE )p[si++];
1685  if ( c <= ' ' || c == '/' || c == '\\' ) break; /* Break on end of segment */
1686  if ( c == '.' || i >= ni )
1687  {
1688  if ( ni != 8 || c != '.' ) return FR_INVALID_NAME;
1689  i = 8; ni = 11;
1690  b <<= 2; continue;
1691  }
1692  if ( c >= 0x80 ) /* Extended char? */
1693  {
1694  b |= 3; /* Eliminate NT flag */
1695 #ifdef _EXCVT
1696  c = excvt[c - 0x80]; /* Upper conversion (SBCS) */
1697 #else
1698 #if !_DF1S /* ASCII only cfg */
1699  return FR_INVALID_NAME;
1700 #endif
1701 #endif
1702  }
1703  if ( IsDBCS1( c ) ) /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
1704  {
1705  d = ( BYTE )p[si++]; /* Get 2nd byte */
1706  if ( !IsDBCS2( d ) || i >= ni - 1 ) /* Reject invalid DBC */
1707  return FR_INVALID_NAME;
1708  sfn[i++] = c;
1709  sfn[i++] = d;
1710  }
1711  else /* Single byte code */
1712  {
1713  if ( chk_chr( "\"*+,:;<=>\?[]|\x7F", c ) ) /* Reject illegal chrs for SFN */
1714  return FR_INVALID_NAME;
1715  if ( IsUpper( c ) ) /* ASCII large capital? */
1716  {
1717  b |= 2;
1718  }
1719  else
1720  {
1721  if ( IsLower( c ) ) /* ASCII small capital? */
1722  {
1723  b |= 1; c -= 0x20;
1724  }
1725  }
1726  sfn[i++] = c;
1727  }
1728  }
1729  *path = &p[si]; /* Return pointer to the next segment */
1730  c = ( c <= ' ' ) ? NS_LAST : 0; /* Set last segment flag if end of path */
1731 
1732  if ( !i ) return FR_INVALID_NAME; /* Reject nul string */
1733  if ( sfn[0] == 0xE5 ) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
1734 
1735  if ( ni == 8 ) b <<= 2;
1736  if ( ( b & 0x03 ) == 0x01 ) c |= NS_EXT; /* NT flag (Name extension has only small capital) */
1737  if ( ( b & 0x0C ) == 0x04 ) c |= NS_BODY; /* NT flag (Name body has only small capital) */
1738 
1739  sfn[NS] = c; /* Store NT flag, File name is created */
1740 
1741  return FR_OK;
1742 #endif
1743 }
1744 
1745 
1746 
1747 
1748 /*-----------------------------------------------------------------------*/
1749 /* Get file information from directory entry */
1750 /*-----------------------------------------------------------------------*/
1751 #if _FS_MINIMIZE <= 1
1752 NODEBUG static
1753 void get_fileinfo( /* No return code */
1754  DIR* dj, /* Pointer to the directory object */
1755  FILINFO* fno /* Pointer to the file information to be filled */
1756 )
1757 {
1758  UINT i;
1759  BYTE nt, *dir;
1760  TCHAR* p, c;
1761 
1762 
1763  p = fno->fname;
1764  if ( dj->sect )
1765  {
1766  dir = dj->dir;
1767  nt = dir[DIR_NTres]; /* NT flag */
1768  for ( i = 0; i < 8; i++ ) /* Copy name body */
1769  {
1770  c = dir[i];
1771  if ( c == ' ' ) break;
1772  if ( c == 0x05 ) c = ( TCHAR )0xE5;
1773  if ( _USE_LFN && ( nt & NS_BODY ) && IsUpper( c ) ) c += 0x20;
1774 #if _LFN_UNICODE
1775  if ( IsDBCS1( c ) && i < 7 && IsDBCS2( dir[i + 1] ) )
1776  c = ( c << 8 ) | dir[++i];
1777  c = ff_convert( c, 1 );
1778  if ( !c ) c = '?';
1779 #endif
1780  *p++ = c;
1781  }
1782  if ( dir[8] != ' ' ) /* Copy name extension */
1783  {
1784  *p++ = '.';
1785  for ( i = 8; i < 11; i++ )
1786  {
1787  c = dir[i];
1788  if ( c == ' ' ) break;
1789  if ( _USE_LFN && ( nt & NS_EXT ) && IsUpper( c ) ) c += 0x20;
1790 #if _LFN_UNICODE
1791  if ( IsDBCS1( c ) && i < 10 && IsDBCS2( dir[i + 1] ) )
1792  c = ( c << 8 ) | dir[++i];
1793  c = ff_convert( c, 1 );
1794  if ( !c ) c = '?';
1795 #endif
1796  *p++ = c;
1797  }
1798  }
1799  fno->fattrib = dir[DIR_Attr]; /* Attribute */
1800  fno->fsize = LD_DWORD( dir + DIR_FileSize ); /* Size */
1801  fno->fdate = LD_WORD( dir + DIR_WrtDate ); /* Date */
1802  fno->ftime = LD_WORD( dir + DIR_WrtTime ); /* Time */
1803  }
1804  *p = 0; /* Terminate SFN str by a \0 */
1805 
1806 #if _USE_LFN
1807  if ( fno->lfname && fno->lfsize )
1808  {
1809  TCHAR* tp = fno->lfname;
1810  WCHAR w, *lfn;
1811 
1812  i = 0;
1813  if ( dj->sect && dj->lfn_idx != 0xFFFF ) /* Get LFN if available */
1814  {
1815  lfn = dj->lfn;
1816  while ( ( w = *lfn++ ) != 0 ) /* Get an LFN char */
1817  {
1818 #if !_LFN_UNICODE
1819  w = ff_convert( w, 0 ); /* Unicode -> OEM conversion */
1820  if ( !w ) { i = 0; break; } /* Could not convert, no LFN */
1821  if ( _DF1S && w >= 0x100 ) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
1822  tp[i++] = ( TCHAR )( w >> 8 );
1823 #endif
1824  if ( i >= fno->lfsize - 1 ) { i = 0; break; } /* Buffer overflow, no LFN */
1825  tp[i++] = ( TCHAR )w;
1826  }
1827  }
1828  tp[i] = 0; /* Terminate the LFN str by a \0 */
1829  }
1830 #endif
1831 }
1832 #endif /* _FS_MINIMIZE <= 1 */
1833 
1834 
1835 
1836 
1837 /*-----------------------------------------------------------------------*/
1838 /* Follow a file path */
1839 /*-----------------------------------------------------------------------*/
1840 
1841 NODEBUG static
1842 FRESULT follow_path( /* FR_OK(0): successful, !=0: error code */
1843  DIR* dj, /* Directory object to return last directory and found object */
1844  const TCHAR* path /* Full-path string to find a file or directory */
1845 )
1846 {
1847  FRESULT res;
1848  BYTE* dir, ns;
1849 
1850 
1851 #if _FS_RPATH
1852  if ( *path == '/' || *path == '\\' ) /* There is a heading separator */
1853  {
1854  path++; dj->sclust = 0; /* Strip it and start from the root dir */
1855  }
1856  else /* No heading separator */
1857  {
1858  dj->sclust = dj->fs->cdir; /* Start from the current dir */
1859  }
1860 #else
1861  if ( *path == '/' || *path == '\\' ) /* Strip heading separator if exist */
1862  path++;
1863  dj->sclust = 0; /* Start from the root dir */
1864 #endif
1865 
1866  if ( ( UINT )*path < ' ' ) /* Nul path means the start directory itself */
1867  {
1868  res = dir_sdi( dj, 0 );
1869  dj->dir = 0;
1870 
1871  }
1872  else /* Follow path */
1873  {
1874  for ( ;; )
1875  {
1876  res = create_name( dj, &path ); /* Get a segment */
1877  if ( res != FR_OK ) break;
1878  res = dir_find( dj ); /* Find it */
1879  ns = *( dj->fn + NS );
1880  if ( res != FR_OK ) /* Failed to find the object */
1881  {
1882  if ( res != FR_NO_FILE ) break; /* Abort if any hard error occured */
1883  /* Object not found */
1884  if ( _FS_RPATH && ( ns & NS_DOT ) ) /* If dot entry is not exit */
1885  {
1886  dj->sclust = 0; dj->dir = 0; /* It is the root dir */
1887  res = FR_OK;
1888  if ( !( ns & NS_LAST ) ) continue;
1889  }
1890  else /* Could not find the object */
1891  {
1892  if ( !( ns & NS_LAST ) ) res = FR_NO_PATH;
1893  }
1894  break;
1895  }
1896  if ( ns & NS_LAST ) break; /* Last segment match. Function completed. */
1897  dir = dj->dir; /* There is next segment. Follow the sub directory */
1898  if ( !( dir[DIR_Attr] & AM_DIR ) ) /* Cannot follow because it is a file */
1899  {
1900  res = FR_NO_PATH; break;
1901  }
1902  dj->sclust = LD_CLUST( dir );
1903  }
1904  }
1905 
1906  return res;
1907 }
1908 
1909 
1910 
1911 
1912 /*-----------------------------------------------------------------------*/
1913 /* Load boot record and check if it is an FAT boot record */
1914 /*-----------------------------------------------------------------------*/
1915 
1916 NODEBUG static
1917 BYTE check_fs( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */
1918  FATFS* fs, /* File system object */
1919  DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */
1920 )
1921 {
1922  if ( disk_read( fs->drv, fs->win, sect, 1 ) != RES_OK ) /* Load boot record */
1923  return 3;
1924  if ( LD_WORD( &fs->win[BS_55AA] ) != 0xAA55 ) /* Check record signature (always placed at offset 510 even if the sector size is >512) */
1925  return 2;
1926 
1927  if ( ( LD_DWORD( &fs->win[BS_FilSysType] ) & 0xFFFFFF ) == 0x544146 ) /* Check "FAT" string */
1928  return 0;
1929  if ( ( LD_DWORD( &fs->win[BS_FilSysType32] ) & 0xFFFFFF ) == 0x544146 )
1930  return 0;
1931 
1932  return 1;
1933 }
1934 
1935 
1936 
1937 
1938 /*-----------------------------------------------------------------------*/
1939 /* Check if the file system object is valid or not */
1940 /*-----------------------------------------------------------------------*/
1941 NODEBUG2 FRESULT chk_mounted( /* FR_OK(0): successful, !=0: any error occurred */
1942  const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
1943  FATFS** rfs, /* Pointer to pointer to the found file system object */
1944  BYTE chk_wp /* !=0: Check media write protection for write access */
1945 )
1946 {
1947  BYTE fmt, b, *tbl;
1948  UINT vol;
1949  DSTATUS stat;
1950  DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
1951  WORD nrsv;
1952  const TCHAR* p = *path;
1953  FATFS* fs;
1954 
1955  /* Get logical drive number from the path name */
1956  vol = p[0] - '0'; /* Is there a drive number? */
1957  if ( vol <= 9 && p[1] == ':' ) /* Found a drive number, get and strip it */
1958  {
1959  p += 2; *path = p; /* Return pointer to the path name */
1960  }
1961  else /* No drive number is given */
1962  {
1963 #if _FS_RPATH
1964  vol = CurrVol; /* Use current drive */
1965 #else
1966  vol = 0; /* Use drive 0 */
1967 #endif
1968  }
1969 
1970  /* Check if the logical drive is valid or not */
1971  if ( vol >= _VOLUMES ) /* Is the drive number valid? */
1972  return FR_INVALID_DRIVE;
1973  *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */
1974  if ( !fs ) return FR_NOT_ENABLED; /* Is the file system object available? */
1975 
1976  ENTER_FF( fs ); /* Lock file system */
1977 
1978  if ( fs->fs_type ) /* If the logical drive has been mounted */
1979  {
1980  stat = disk_status( fs->drv );
1981  if ( !( stat & STA_NOINIT ) ) /* and the physical drive is kept initialized (has not been changed), */
1982  {
1983 #if !_FS_READONLY
1984  if ( chk_wp && ( stat & STA_PROTECT ) ) /* Check write protection if needed */
1985  return FR_WRITE_PROTECTED;
1986 #endif
1987  return FR_OK; /* The file system object is valid */
1988  }
1989  }
1990 
1991  /* The logical drive must be mounted. */
1992  /* Following code attempts to mount a volume. (analyze BPB and initialize the fs object) */
1993 
1994  fs->fs_type = 0; /* Clear the file system object */
1995  fs->drv = ( BYTE )LD2PD( vol ); /* Bind the logical drive and a physical drive */
1996  stat = disk_initialize( fs->drv ); /* Initialize low level disk I/O layer */
1997  if ( stat & STA_NOINIT ) /* Check if the initialization succeeded */
1998  return FR_NOT_READY; /* Failed to initialize due to no media or hard error */
1999 #if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */
2000  if ( disk_ioctl( fs->drv, GET_SECTOR_SIZE, &fs->ssize ) != RES_OK )
2001  return FR_DISK_ERR;
2002 #endif
2003 #if !_FS_READONLY
2004  if ( chk_wp && ( stat & STA_PROTECT ) ) /* Check disk write protection if needed */
2005  return FR_WRITE_PROTECTED;
2006 #endif
2007  /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */
2008  fmt = check_fs( fs, bsect = 0 ); /* Check sector 0 if it is a VBR */
2009  if ( fmt == 1 ) /* Not an FAT-VBR, the disk may be partitioned */
2010  {
2011  /* Check the partition listed in top of the partition table */
2012  tbl = &fs->win[MBR_Table + LD2PT( vol ) * 16]; /* Partition table */
2013  if ( tbl[4] ) /* Is the partition existing? */
2014  {
2015  bsect = LD_DWORD( &tbl[8] ); /* Partition offset in LBA */
2016  fmt = check_fs( fs, bsect ); /* Check the partition */
2017  }
2018  }
2019  if ( fmt == 3 ) return FR_DISK_ERR;
2020  if ( fmt ) return FR_NO_FILESYSTEM; /* No FAT volume is found */
2021 
2022  /* Following code initializes the file system object */
2023 
2024  if ( LD_WORD( fs->win + BPB_BytsPerSec ) != SS( fs ) ) /* (BPB_BytsPerSec must be equal to the physical sector size) */
2025  return FR_NO_FILESYSTEM;
2026 
2027  fasize = LD_WORD( fs->win + BPB_FATSz16 ); /* Number of sectors per FAT */
2028  if ( !fasize ) fasize = LD_DWORD( fs->win + BPB_FATSz32 );
2029  fs->fsize = fasize;
2030 
2031  fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */
2032  if ( b != 1 && b != 2 ) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
2033  fasize *= b; /* Number of sectors for FAT area */
2034 
2035  fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
2036  if ( !b || ( b & ( b - 1 ) ) ) return FR_NO_FILESYSTEM; /* (Must be power of 2) */
2037 
2038  fs->n_rootdir = LD_WORD( fs->win + BPB_RootEntCnt ); /* Number of root directory entries */
2039  if ( fs->n_rootdir % ( SS( fs ) / 32 ) ) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */
2040 
2041  tsect = LD_WORD( fs->win + BPB_TotSec16 ); /* Number of sectors on the volume */
2042  if ( !tsect ) tsect = LD_DWORD( fs->win + BPB_TotSec32 );
2043 
2044  nrsv = LD_WORD( fs->win + BPB_RsvdSecCnt ); /* Number of reserved sectors */
2045  if ( !nrsv ) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */
2046 
2047  /* Determine the FAT sub type */
2048  sysect = nrsv + fasize + fs->n_rootdir / ( SS( fs ) / 32 ); /* RSV+FAT+DIR */
2049  if ( tsect < sysect ) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
2050  nclst = ( tsect - sysect ) / fs->csize; /* Number of clusters */
2051  if ( !nclst ) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
2052  fmt = FS_FAT12;
2053  if ( nclst >= MIN_FAT16 ) fmt = FS_FAT16;
2054  if ( nclst >= MIN_FAT32 ) fmt = FS_FAT32;
2055 
2056  /* Boundaries and Limits */
2057  fs->n_fatent = nclst + 2; /* Number of FAT entries */
2058  fs->database = bsect + sysect; /* Data start sector */
2059  fs->fatbase = bsect + nrsv; /* FAT start sector */
2060  if ( fmt == FS_FAT32 )
2061  {
2062  if ( fs->n_rootdir ) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
2063  fs->dirbase = LD_DWORD( fs->win + BPB_RootClus ); /* Root directory start cluster */
2064  szbfat = fs->n_fatent * 4; /* (Required FAT size) */
2065  }
2066  else
2067  {
2068  if ( !fs->n_rootdir ) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
2069  fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
2070  szbfat = ( fmt == FS_FAT16 ) ? /* (Required FAT size) */
2071  fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + ( fs->n_fatent & 1 );
2072  }
2073  if ( fs->fsize < ( szbfat + ( SS( fs ) - 1 ) ) / SS( fs ) ) /* (FAT size must not be less than FAT sectors */
2074  return FR_NO_FILESYSTEM;
2075 
2076 #if !_FS_READONLY
2077  /* Initialize cluster allocation information */
2078  fs->free_clust = 0xFFFFFFFF;
2079  fs->last_clust = 0;
2080 
2081  /* Get fsinfo if available */
2082  if ( fmt == FS_FAT32 )
2083  {
2084  fs->fsi_flag = 0;
2085  fs->fsi_sector = bsect + LD_WORD( fs->win + BPB_FSInfo );
2086  if ( disk_read( fs->drv, fs->win, fs->fsi_sector, 1 ) == RES_OK &&
2087  LD_WORD( fs->win + BS_55AA ) == 0xAA55 &&
2088  LD_DWORD( fs->win + FSI_LeadSig ) == 0x41615252 &&
2089  LD_DWORD( fs->win + FSI_StrucSig ) == 0x61417272 )
2090  {
2091  fs->last_clust = LD_DWORD( fs->win + FSI_Nxt_Free );
2092  fs->free_clust = LD_DWORD( fs->win + FSI_Free_Count );
2093  }
2094  }
2095 #endif
2096  fs->fs_type = fmt; /* FAT sub-type */
2097  fs->id = ++Fsid; /* File system mount ID */
2098  fs->winsect = 0; /* Invalidate sector cache */
2099  fs->wflag = 0;
2100 #if _FS_RPATH
2101  fs->cdir = 0; /* Current directory (root dir) */
2102 #endif
2103 #if _FS_SHARE /* Clear file lock semaphores */
2104  clear_lock( fs );
2105 #endif
2106 
2107  return FR_OK;
2108 }
2109 
2110 
2111 
2112 
2113 /*-----------------------------------------------------------------------*/
2114 /* Check if the file/dir object is valid or not */
2115 /*-----------------------------------------------------------------------*/
2116 
2117 NODEBUG static
2118 FRESULT validate( /* FR_OK(0): The object is valid, !=0: Invalid */
2119  FATFS* fs, /* Pointer to the file system object */
2120  WORD id /* Member id of the target object to be checked */
2121 )
2122 {
2123  if ( !fs || !fs->fs_type || fs->id != id )
2124  return FR_INVALID_OBJECT;
2125 
2126  ENTER_FF( fs ); /* Lock file system */
2127 
2128  if ( disk_status( fs->drv ) & STA_NOINIT )
2129  return FR_NOT_READY;
2130 
2131  return FR_OK;
2132 }
2133 
2134 
2135 
2136 
2137 /*--------------------------------------------------------------------------
2138 
2139  Public Functions
2140 
2141 --------------------------------------------------------------------------*/
2142 
2143 
2144 
2145 /*-----------------------------------------------------------------------*/
2146 /* Mount/Unmount a Logical Drive */
2147 /*-----------------------------------------------------------------------*/
2148 
2149 NODEBUG FRESULT f_mount(
2150  BYTE vol, /* Logical drive number to be mounted/unmounted */
2151  FATFS* fs /* Pointer to new file system object (NULL for unmount)*/
2152 )
2153 {
2154  FATFS* rfs;
2155 
2156 
2157  if ( vol >= _VOLUMES ) /* Check if the drive number is valid */
2158  return FR_INVALID_DRIVE;
2159  rfs = FatFs[vol]; /* Get current fs object */
2160 
2161  if ( rfs )
2162  {
2163 #if _FS_SHARE
2164  clear_lock( rfs );
2165 #endif
2166 #if _FS_REENTRANT /* Discard sync object of the current volume */
2167  if ( !ff_del_syncobj( rfs->sobj ) ) return FR_INT_ERR;
2168 #endif
2169  rfs->fs_type = 0; /* Clear old fs object */
2170  }
2171 
2172  if ( fs )
2173  {
2174  fs->fs_type = 0; /* Clear new fs object */
2175 #if _FS_REENTRANT /* Create sync object for the new volume */
2176  if ( !ff_cre_syncobj( vol, &fs->sobj ) ) return FR_INT_ERR;
2177 #endif
2178  }
2179  FatFs[vol] = fs;
2180  /* Register new fs object */
2181 
2182  return FR_OK;
2183 }
2184 
2185 
2186 
2187 
2188 /*-----------------------------------------------------------------------*/
2189 /* Open or Create a File */
2190 /*-----------------------------------------------------------------------*/
2191 
2192 NODEBUG FRESULT f_open(
2193  FIL* fp, /* Pointer to the blank file object */
2194  const TCHAR* path, /* Pointer to the file name */
2195  BYTE mode /* Access mode and file open mode flags */
2196 )
2197 {
2198  FRESULT res;
2199  DIR dj;
2200  BYTE* dir;
2201  DEF_NAMEBUF;
2202 
2203 
2204  fp->fs = 0; /* Clear file object */
2205 
2206 #if !_FS_READONLY
2207  mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
2208  res = chk_mounted( &path, &dj.fs, ( BYTE )( mode & ~FA_READ ) );
2209 #else
2210  mode &= FA_READ;
2211  res = chk_mounted( &path, &dj.fs, 0 );
2212 #endif
2213  INIT_BUF( dj );
2214  if ( res == FR_OK )
2215  res = follow_path( &dj, path ); /* Follow the file path */
2216  dir = dj.dir;
2217 
2218 #if !_FS_READONLY /* R/W configuration */
2219  if ( res == FR_OK )
2220  {
2221  if ( !dir ) /* Current dir itself */
2222  res = FR_INVALID_NAME;
2223 #if _FS_SHARE
2224  else
2225  res = chk_lock( &dj, ( mode & ~FA_READ ) ? 1 : 0 );
2226 #endif
2227  }
2228  /* Create or Open a file */
2229  if ( mode & ( FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW ) )
2230  {
2231  DWORD dw, cl;
2232 
2233  if ( res != FR_OK ) /* No file, create new */
2234  {
2235  if ( res == FR_NO_FILE ) /* There is no file to open, create a new entry */
2236 #if _FS_SHARE
2237  res = enq_lock( dj.fs ) ? dir_register( &dj ) : FR_TOO_MANY_OPEN_FILES;
2238 #else
2239  res = dir_register( &dj );
2240 #endif
2241  mode |= FA_CREATE_ALWAYS; /* File is created */
2242  dir = dj.dir; /* New entry */
2243  }
2244  else /* Any object is already existing */
2245  {
2246  if ( mode & FA_CREATE_NEW ) /* Cannot create new */
2247  {
2248  res = FR_EXIST;
2249  }
2250  else
2251  {
2252  if ( dir[DIR_Attr] & ( AM_RDO | AM_DIR ) ) /* Cannot overwrite it (R/O or DIR) */
2253  res = FR_DENIED;
2254  }
2255  }
2256  if ( res == FR_OK && ( mode & FA_CREATE_ALWAYS ) ) /* Truncate it if overwrite mode */
2257  {
2258  dw = get_fattime(); /* Created time */
2259  ST_DWORD( dir + DIR_CrtTime, dw );
2260  dir[DIR_Attr] = 0; /* Reset attribute */
2261  ST_DWORD( dir + DIR_FileSize, 0 ); /* size = 0 */
2262  cl = LD_CLUST( dir ); /* Get start cluster */
2263  ST_CLUST( dir, 0 ); /* cluster = 0 */
2264  dj.fs->wflag = 1;
2265  if ( cl ) /* Remove the cluster chain if exist */
2266  {
2267  dw = dj.fs->winsect;
2268  res = remove_chain( dj.fs, cl );
2269  if ( res == FR_OK )
2270  {
2271  dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */
2272  res = move_window( dj.fs, dw );
2273  }
2274  }
2275  }
2276  }
2277  else /* Open an existing file */
2278  {
2279  if ( res == FR_OK ) /* Follow succeeded */
2280  {
2281  if ( dir[DIR_Attr] & AM_DIR ) /* It is a directory */
2282  {
2283  res = FR_NO_FILE;
2284  }
2285  else
2286  {
2287  if ( ( mode & FA_WRITE ) && ( dir[DIR_Attr] & AM_RDO ) ) /* R/O violation */
2288  res = FR_DENIED;
2289  }
2290  }
2291  }
2292  if ( res == FR_OK )
2293  {
2294  if ( mode & FA_CREATE_ALWAYS ) /* Set file change flag if created or overwritten */
2295  mode |= FA__WRITTEN;
2296  fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */
2297  fp->dir_ptr = dir;
2298 #if _FS_SHARE
2299  fp->lockid = inc_lock( &dj, ( mode & ~FA_READ ) ? 1 : 0 );
2300  if ( !fp->lockid ) res = FR_INT_ERR;
2301 #endif
2302  }
2303 
2304 #else /* R/O configuration */
2305  if ( res == FR_OK ) /* Follow succeeded */
2306  {
2307  if ( !dir ) /* Current dir itself */
2308  {
2309  res = FR_INVALID_NAME;
2310  }
2311  else
2312  {
2313  if ( dir[DIR_Attr] & AM_DIR ) /* It is a directory */
2314  res = FR_NO_FILE;
2315  }
2316  }
2317 #endif
2318  FREE_BUF();
2319 
2320  if ( res == FR_OK )
2321  {
2322  fp->flag = mode; /* File access mode */
2323  fp->org_clust = LD_CLUST( dir ); /* File start cluster */
2324  fp->fsize = LD_DWORD( dir + DIR_FileSize ); /* File size */
2325  fp->fptr = 0; /* File pointer */
2326  fp->dsect = 0;
2327 #if _USE_FASTSEEK
2328  fp->cltbl = 0; /* No cluster link map table */
2329 #endif
2330  fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */
2331  }
2332 
2333  LEAVE_FF( dj.fs, res );
2334 }
2335 
2336 
2337 
2338 
2339 /*-----------------------------------------------------------------------*/
2340 /* Read File */
2341 /*-----------------------------------------------------------------------*/
2342 
2343 NODEBUG FRESULT f_read(
2344  FIL* fp, /* Pointer to the file object */
2345  void* buff, /* Pointer to data buffer */
2346  UINT btr, /* Number of bytes to read */
2347  UINT* br /* Pointer to number of bytes read */
2348 )
2349 {
2350  FRESULT res;
2351  DWORD clst, sect, remain;
2352  UINT rcnt, cc;
2353  BYTE csect, *rbuff = buff;
2354 
2355 
2356  *br = 0; /* Initialize byte counter */
2357 
2358  res = validate( fp->fs, fp->id ); /* Check validity of the object */
2359  if ( res != FR_OK ) LEAVE_FF( fp->fs, res );
2360  if ( fp->flag & FA__ERROR ) /* Check abort flag */
2361  LEAVE_FF( fp->fs, FR_INT_ERR );
2362  if ( !( fp->flag & FA_READ ) ) /* Check access mode */
2363  LEAVE_FF( fp->fs, FR_DENIED );
2364  remain = fp->fsize - fp->fptr;
2365  if ( btr > remain ) btr = ( UINT )remain; /* Truncate btr by remaining bytes */
2366 
2367  for ( ; btr; /* Repeat until all data transferred */
2368  rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt )
2369  {
2370  if ( ( fp->fptr % SS( fp->fs ) ) == 0 ) /* On the sector boundary? */
2371  {
2372  csect = ( BYTE )( fp->fptr / SS( fp->fs ) & ( fp->fs->csize - 1 ) ); /* Sector offset in the cluster */
2373  if ( !csect ) /* On the cluster boundary? */
2374  {
2375  clst = ( fp->fptr == 0 ) ? /* On the top of the file? */
2376  fp->org_clust : get_fat( fp->fs, fp->curr_clust );
2377  if ( clst <= 1 ) ABORT( fp->fs, FR_INT_ERR );
2378  if ( clst == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
2379  fp->curr_clust = clst; /* Update current cluster */
2380  }
2381  sect = clust2sect( fp->fs, fp->curr_clust ); /* Get current sector */
2382  if ( !sect ) ABORT( fp->fs, FR_INT_ERR );
2383  sect += csect;
2384  cc = btr / SS( fp->fs ); /* When remaining bytes >= sector size, */
2385  if ( cc ) /* Read maximum contiguous sectors directly */
2386  {
2387  if ( csect + cc > fp->fs->csize ) /* Clip at cluster boundary */
2388  cc = fp->fs->csize - csect;
2389  if ( disk_read( fp->fs->drv, rbuff, sect, ( BYTE )cc ) != RES_OK )
2390  ABORT( fp->fs, FR_DISK_ERR );
2391 #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
2392 #if _FS_TINY
2393  if ( fp->fs->wflag && fp->fs->winsect - sect < cc )
2394  mem_cpy( rbuff + ( ( fp->fs->winsect - sect ) * SS( fp->fs ) ), fp->fs->win, SS( fp->fs ) );
2395 #else
2396  if ( ( fp->flag & FA__DIRTY ) && fp->dsect - sect < cc )
2397  mem_cpy( rbuff + ( ( fp->dsect - sect ) * SS( fp->fs ) ), fp->buf, SS( fp->fs ) );
2398 #endif
2399 #endif
2400  rcnt = SS( fp->fs ) * cc; /* Number of bytes transferred */
2401  continue;
2402  }
2403 #if !_FS_TINY
2404 #if !_FS_READONLY
2405  if ( fp->flag & FA__DIRTY ) /* Write sector I/O buffer if needed */
2406  {
2407  if ( disk_write( fp->fs->drv, fp->buf, fp->dsect, 1 ) != RES_OK )
2408  ABORT( fp->fs, FR_DISK_ERR );
2409  fp->flag &= ~FA__DIRTY;
2410  }
2411 #endif
2412  if ( fp->dsect != sect ) /* Fill sector buffer with file data */
2413  {
2414  if ( disk_read( fp->fs->drv, fp->buf, sect, 1 ) != RES_OK )
2415  ABORT( fp->fs, FR_DISK_ERR );
2416  }
2417 #endif
2418  fp->dsect = sect;
2419  }
2420  rcnt = SS( fp->fs ) - ( fp->fptr % SS( fp->fs ) ); /* Get partial sector data from sector buffer */
2421  if ( rcnt > btr ) rcnt = btr;
2422 #if _FS_TINY
2423  if ( move_window( fp->fs, fp->dsect ) ) /* Move sector window */
2424  ABORT( fp->fs, FR_DISK_ERR );
2425  mem_cpy( rbuff, &fp->fs->win[fp->fptr % SS( fp->fs )], rcnt ); /* Pick partial sector */
2426 #else
2427  mem_cpy( rbuff, &fp->buf[fp->fptr % SS( fp->fs )], rcnt ); /* Pick partial sector */
2428 #endif
2429  }
2430 
2431  LEAVE_FF( fp->fs, FR_OK );
2432 }
2433 
2434 
2435 
2436 
2437 #if !_FS_READONLY
2438 /*-----------------------------------------------------------------------*/
2439 /* Write File */
2440 /*-----------------------------------------------------------------------*/
2441 
2442 NODEBUG FRESULT f_write(
2443  FIL* fp, /* Pointer to the file object */
2444  const void* buff, /* Pointer to the data to be written */
2445  UINT btw, /* Number of bytes to write */
2446  UINT* bw /* Pointer to number of bytes written */
2447 )
2448 {
2449  FRESULT res;
2450  DWORD clst, sect;
2451  UINT wcnt, cc;
2452  const BYTE* wbuff = buff;
2453  BYTE csect;
2454 
2455 
2456  *bw = 0; /* Initialize byte counter */
2457 
2458  res = validate( fp->fs, fp->id ); /* Check validity of the object */
2459  if ( res != FR_OK ) LEAVE_FF( fp->fs, res );
2460  if ( fp->flag & FA__ERROR ) /* Check abort flag */
2461  LEAVE_FF( fp->fs, FR_INT_ERR );
2462  if ( !( fp->flag & FA_WRITE ) ) /* Check access mode */
2463  LEAVE_FF( fp->fs, FR_DENIED );
2464  if ( fp->fsize + btw < fp->fsize ) btw = 0; /* File size cannot reach 4GB */
2465 
2466  for ( ; btw; /* Repeat until all data transferred */
2467  wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt )
2468  {
2469  if ( ( fp->fptr % SS( fp->fs ) ) == 0 ) /* On the sector boundary? */
2470  {
2471  csect = ( BYTE )( fp->fptr / SS( fp->fs ) & ( fp->fs->csize - 1 ) ); /* Sector offset in the cluster */
2472  if ( !csect ) /* On the cluster boundary? */
2473  {
2474  if ( fp->fptr == 0 ) /* On the top of the file? */
2475  {
2476  clst = fp->org_clust; /* Follow from the origin */
2477  if ( clst == 0 ) /* When there is no cluster chain, */
2478  fp->org_clust = clst = create_chain( fp->fs, 0 ); /* Create a new cluster chain */
2479  }
2480  else /* Middle or end of the file */
2481  {
2482  clst = create_chain( fp->fs, fp->curr_clust ); /* Follow or stretch cluster chain */
2483  }
2484  if ( clst == 0 ) break; /* Could not allocate a new cluster (disk full) */
2485  if ( clst == 1 ) ABORT( fp->fs, FR_INT_ERR );
2486  if ( clst == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
2487  fp->curr_clust = clst; /* Update current cluster */
2488  }
2489 #if _FS_TINY
2490  if ( fp->fs->winsect == fp->dsect && move_window( fp->fs, 0 ) ) /* Write back data buffer prior to following direct transfer */
2491  ABORT( fp->fs, FR_DISK_ERR );
2492 #else
2493  if ( fp->flag & FA__DIRTY ) /* Write back data buffer prior to following direct transfer */
2494  {
2495  if ( disk_write( fp->fs->drv, fp->buf, fp->dsect, 1 ) != RES_OK )
2496  ABORT( fp->fs, FR_DISK_ERR );
2497  fp->flag &= ~FA__DIRTY;
2498  }
2499 #endif
2500  sect = clust2sect( fp->fs, fp->curr_clust ); /* Get current sector */
2501  if ( !sect ) ABORT( fp->fs, FR_INT_ERR );
2502  sect += csect;
2503  cc = btw / SS( fp->fs ); /* When remaining bytes >= sector size, */
2504  if ( cc ) /* Write maximum contiguous sectors directly */
2505  {
2506  if ( csect + cc > fp->fs->csize ) /* Clip at cluster boundary */
2507  cc = fp->fs->csize - csect;
2508  if ( disk_write( fp->fs->drv, wbuff, sect, ( BYTE )cc ) != RES_OK )
2509  ABORT( fp->fs, FR_DISK_ERR );
2510 #if _FS_TINY
2511  if ( fp->fs->winsect - sect < cc ) /* Refill sector cache if it gets dirty by the direct write */
2512  {
2513  mem_cpy( fp->fs->win, wbuff + ( ( fp->fs->winsect - sect ) * SS( fp->fs ) ), SS( fp->fs ) );
2514  fp->fs->wflag = 0;
2515  }
2516 #else
2517  if ( fp->dsect - sect < cc ) /* Refill sector cache if it gets dirty by the direct write */
2518  {
2519  mem_cpy( fp->buf, wbuff + ( ( fp->dsect - sect ) * SS( fp->fs ) ), SS( fp->fs ) );
2520  fp->flag &= ~FA__DIRTY;
2521  }
2522 #endif
2523  wcnt = SS( fp->fs ) * cc; /* Number of bytes transferred */
2524  continue;
2525  }
2526 #if _FS_TINY
2527  if ( fp->fptr >= fp->fsize ) /* Avoid silly buffer filling at growing edge */
2528  {
2529  if ( move_window( fp->fs, 0 ) ) ABORT( fp->fs, FR_DISK_ERR );
2530  fp->fs->winsect = sect;
2531  }
2532 #else
2533  if ( fp->dsect != sect ) /* Fill sector buffer with file data */
2534  {
2535  if ( fp->fptr < fp->fsize &&
2536  disk_read( fp->fs->drv, fp->buf, sect, 1 ) != RES_OK )
2537  ABORT( fp->fs, FR_DISK_ERR );
2538  }
2539 #endif
2540  fp->dsect = sect;
2541  }
2542  wcnt = SS( fp->fs ) - ( fp->fptr % SS( fp->fs ) ); /* Put partial sector into file I/O buffer */
2543  if ( wcnt > btw ) wcnt = btw;
2544 #if _FS_TINY
2545  if ( move_window( fp->fs, fp->dsect ) ) /* Move sector window */
2546  ABORT( fp->fs, FR_DISK_ERR );
2547  mem_cpy( &fp->fs->win[fp->fptr % SS( fp->fs )], wbuff, wcnt ); /* Fit partial sector */
2548  fp->fs->wflag = 1;
2549 #else
2550  mem_cpy( &fp->buf[fp->fptr % SS( fp->fs )], wbuff, wcnt ); /* Fit partial sector */
2551  fp->flag |= FA__DIRTY;
2552 #endif
2553  }
2554 
2555  if ( fp->fptr > fp->fsize ) fp->fsize = fp->fptr; /* Update file size if needed */
2556  fp->flag |= FA__WRITTEN; /* Set file change flag */
2557 
2558  LEAVE_FF( fp->fs, FR_OK );
2559 }
2560 
2561 
2562 
2563 
2564 /*-----------------------------------------------------------------------*/
2565 /* Synchronize the File Object */
2566 /*-----------------------------------------------------------------------*/
2567 
2568 NODEBUG FRESULT f_sync(
2569  FIL* fp /* Pointer to the file object */
2570 )
2571 {
2572  FRESULT res;
2573  DWORD tim;
2574  BYTE* dir;
2575 
2576 
2577  res = validate( fp->fs, fp->id ); /* Check validity of the object */
2578  if ( res == FR_OK )
2579  {
2580  if ( fp->flag & FA__WRITTEN ) /* Has the file been written? */
2581  {
2582 #if !_FS_TINY /* Write-back dirty buffer */
2583  if ( fp->flag & FA__DIRTY )
2584  {
2585  if ( disk_write( fp->fs->drv, fp->buf, fp->dsect, 1 ) != RES_OK )
2586  LEAVE_FF( fp->fs, FR_DISK_ERR );
2587  fp->flag &= ~FA__DIRTY;
2588  }
2589 #endif
2590  /* Update the directory entry */
2591  res = move_window( fp->fs, fp->dir_sect );
2592  if ( res == FR_OK )
2593  {
2594  dir = fp->dir_ptr;
2595  dir[DIR_Attr] |= AM_ARC; /* Set archive bit */
2596  ST_DWORD( dir + DIR_FileSize, fp->fsize ); /* Update file size */
2597  ST_CLUST( dir, fp->org_clust ); /* Update start cluster */
2598  tim = get_fattime(); /* Update updated time */
2599  ST_DWORD( dir + DIR_WrtTime, tim );
2600  fp->flag &= ~FA__WRITTEN;
2601  fp->fs->wflag = 1;
2602  res = sync( fp->fs );
2603  }
2604  }
2605  }
2606 
2607  LEAVE_FF( fp->fs, res );
2608 }
2609 
2610 #endif /* !_FS_READONLY */
2611 
2612 
2613 
2614 
2615 /*-----------------------------------------------------------------------*/
2616 /* Close File */
2617 /*-----------------------------------------------------------------------*/
2618 
2619 NODEBUG FRESULT f_close(
2620  FIL* fp /* Pointer to the file object to be closed */
2621 )
2622 {
2623  FRESULT res;
2624 
2625 #if _FS_READONLY
2626  FATFS* fs = fp->fs;
2627  res = validate( fs, fp->id );
2628  if ( res == FR_OK ) fp->fs = 0; /* Discard file object */
2629  LEAVE_FF( fs, res );
2630 
2631 #else
2632  res = f_sync( fp ); /* Flush cached data */
2633 #if _FS_SHARE
2634  if ( res == FR_OK ) /* Decrement open counter */
2635  {
2636 #if _FS_REENTRANT
2637  res = validate( fp->fs, fp->id );
2638  if ( res == FR_OK )
2639  {
2640  res = dec_lock( fp->lockid );
2641  unlock_fs( fp->fs, FR_OK );
2642  }
2643 #else
2644  res = dec_lock( fp->lockid );
2645 #endif
2646  }
2647 #endif
2648  if ( res == FR_OK ) fp->fs = 0; /* Discard file object */
2649  return res;
2650 #endif
2651 }
2652 
2653 
2654 
2655 
2656 /*-----------------------------------------------------------------------*/
2657 /* Current Drive/Directory Handlings */
2658 /*-----------------------------------------------------------------------*/
2659 
2660 #if _FS_RPATH >= 1
2661 
2662 NODEBUG FRESULT f_chdrive(
2663  BYTE drv /* Drive number */
2664 )
2665 {
2666  if ( drv >= _VOLUMES ) return FR_INVALID_DRIVE;
2667 
2668  CurrVol = drv;
2669 
2670  return FR_OK;
2671 }
2672 
2673 
2674 
2675 NODEBUG FRESULT f_chdir(
2676  const TCHAR* path /* Pointer to the directory path */
2677 )
2678 {
2679  FRESULT res;
2680  DIR dj;
2681  DEF_NAMEBUF;
2682 
2683 
2684  res = chk_mounted( &path, &dj.fs, 0 );
2685  if ( res == FR_OK )
2686  {
2687  INIT_BUF( dj );
2688  res = follow_path( &dj, path ); /* Follow the path */
2689  FREE_BUF();
2690  if ( res == FR_OK ) /* Follow completed */
2691  {
2692  if ( !dj.dir )
2693  {
2694  dj.fs->cdir = dj.sclust; /* Start directory itself */
2695  }
2696  else
2697  {
2698  if ( dj.dir[DIR_Attr] & AM_DIR ) /* Reached to the directory */
2699  dj.fs->cdir = LD_CLUST( dj.dir );
2700  else
2701  res = FR_NO_PATH; /* Reached but a file */
2702  }
2703  }
2704  if ( res == FR_NO_FILE ) res = FR_NO_PATH;
2705  }
2706 
2707  LEAVE_FF( dj.fs, res );
2708 }
2709 
2710 
2711 #if _FS_RPATH >= 2
2712 NODEBUG FRESULT f_getcwd(
2713  TCHAR* path, /* Pointer to the directory path */
2714  UINT sz_path /* Size of path */
2715 )
2716 {
2717  FRESULT res;
2718  DIR dj;
2719  UINT i, n;
2720  DWORD ccl;
2721  TCHAR* tp;
2722  FILINFO fno;
2723  DEF_NAMEBUF;
2724 
2725 
2726  *path = 0;
2727  res = chk_mounted( ( const TCHAR** )&path, &dj.fs, 0 ); /* Get current volume */
2728  if ( res == FR_OK )
2729  {
2730  INIT_BUF( dj );
2731  i = sz_path; /* Bottom of buffer (dir stack base) */
2732  dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */
2733  while ( ( ccl = dj.sclust ) != 0 ) /* Repeat while current dir is a sub-dir */
2734  {
2735  res = dir_sdi( &dj, 1 ); /* Get parent dir */
2736  if ( res != FR_OK ) break;
2737  res = dir_read( &dj );
2738  if ( res != FR_OK ) break;
2739  dj.sclust = LD_CLUST( dj.dir ); /* Goto parent dir */
2740  res = dir_sdi( &dj, 0 );
2741  if ( res != FR_OK ) break;
2742  do /* Find the entry links to the child dir */
2743  {
2744  res = dir_read( &dj );
2745  if ( res != FR_OK ) break;
2746  if ( ccl == LD_CLUST( dj.dir ) ) break; /* Found the entry */
2747  res = dir_next( &dj, 0 );
2748  }
2749  while ( res == FR_OK );
2750  if ( res == FR_NO_FILE ) res = FR_INT_ERR; /* It cannot be 'not found'. */
2751  if ( res != FR_OK ) break;
2752 #if _USE_LFN
2753  fno.lfname = path;
2754  fno.lfsize = i;
2755 #endif
2756  get_fileinfo( &dj, &fno ); /* Get the dir name and push it to the buffer */
2757  tp = fno.fname;
2758  if ( _USE_LFN && *path ) tp = path;
2759  for ( n = 0; tp[n]; n++ ) ;
2760  if ( i < n + 3 )
2761  {
2762  res = FR_NOT_ENOUGH_CORE; break;
2763  }
2764  while ( n ) path[--i] = tp[--n];
2765  path[--i] = '/';
2766  }
2767  tp = path;
2768  if ( res == FR_OK )
2769  {
2770  *tp++ = '0' + CurrVol; /* Put drive number */
2771  *tp++ = ':';
2772  if ( i == sz_path ) /* Root-dir */
2773  {
2774  *tp++ = '/';
2775  }
2776  else /* Sub-dir */
2777  {
2778  do /* Add stacked path str */
2779  *tp++ = path[i++];
2780  while ( i < sz_path );
2781  }
2782  }
2783  *tp = 0;
2784  FREE_BUF();
2785  }
2786 
2787  LEAVE_FF( dj.fs, res );
2788 }
2789 #endif /* _FS_RPATH >= 2 */
2790 #endif /* _FS_RPATH >= 1 */
2791 
2792 
2793 
2794 #if _FS_MINIMIZE <= 2
2795 /*-----------------------------------------------------------------------*/
2796 /* Seek File R/W Pointer */
2797 /*-----------------------------------------------------------------------*/
2798 
2799 NODEBUG FRESULT f_lseek(
2800  FIL* fp, /* Pointer to the file object */
2801  DWORD ofs /* File pointer from top of file */
2802 )
2803 {
2804  FRESULT res;
2805 
2806 
2807  res = validate( fp->fs, fp->id ); /* Check validity of the object */
2808  if ( res != FR_OK ) LEAVE_FF( fp->fs, res );
2809  if ( fp->flag & FA__ERROR ) /* Check abort flag */
2810  LEAVE_FF( fp->fs, FR_INT_ERR );
2811 
2812 #if _USE_FASTSEEK
2813  if ( fp->cltbl ) /* Fast seek */
2814  {
2815  DWORD cl, pcl, ncl, tcl, dsc, tlen, *tbl = fp->cltbl;
2816  BYTE csc;
2817 
2818  tlen = *tbl++;
2819  if ( ofs == CREATE_LINKMAP ) /* Create link map table */
2820  {
2821  cl = fp->org_clust;
2822  if ( cl )
2823  {
2824  do
2825  {
2826  if ( tlen < 4 ) /* Not enough table items */
2827  {
2828  res = FR_NOT_ENOUGH_CORE; break;
2829  }
2830  tcl = cl; ncl = 0;
2831  do /* Get a fragment and store the top and length */
2832  {
2833  pcl = cl; ncl++;
2834  cl = get_fat( fp->fs, cl );
2835  if ( cl <= 1 ) ABORT( fp->fs, FR_INT_ERR );
2836  if ( cl == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
2837  }
2838  while ( cl == pcl + 1 );
2839  *tbl++ = ncl; *tbl++ = tcl;
2840  tlen -= 2;
2841  }
2842  while ( cl < fp->fs->n_fatent );
2843  }
2844  *tbl = 0; /* Terminate table */
2845 
2846  }
2847  else /* Fast seek */
2848  {
2849  if ( ofs > fp->fsize ) /* Clip offset at the file size */
2850  ofs = fp->fsize;
2851  fp->fptr = ofs; /* Set file pointer */
2852  if ( ofs )
2853  {
2854  dsc = ( ofs - 1 ) / SS( fp->fs );
2855  cl = dsc / fp->fs->csize;
2856  for ( ;; )
2857  {
2858  ncl = *tbl++;
2859  if ( !ncl ) ABORT( fp->fs, FR_INT_ERR );
2860  if ( cl < ncl ) break;
2861  cl -= ncl; tbl++;
2862  }
2863  fp->curr_clust = cl + *tbl;
2864  csc = ( BYTE )( dsc & ( fp->fs->csize - 1 ) );
2865  dsc = clust2sect( fp->fs, fp->curr_clust );
2866  if ( !dsc ) ABORT( fp->fs, FR_INT_ERR );
2867  dsc += csc;
2868  if ( fp->fptr % SS( fp->fs ) && dsc != fp->dsect )
2869  {
2870 #if !_FS_TINY
2871 #if !_FS_READONLY
2872  if ( fp->flag & FA__DIRTY ) /* Flush dirty buffer if needed */
2873  {
2874  if ( disk_write( fp->fs->drv, fp->buf, fp->dsect, 1 ) != RES_OK )
2875  ABORT( fp->fs, FR_DISK_ERR );
2876  fp->flag &= ~FA__DIRTY;
2877  }
2878 #endif
2879  if ( disk_read( fp->fs->drv, fp->buf, dsc, 1 ) != RES_OK )
2880  ABORT( fp->fs, FR_DISK_ERR );
2881 #endif
2882  fp->dsect = dsc;
2883  }
2884  }
2885  }
2886  }
2887  else
2888 #endif
2889 
2890  /* Normal Seek */
2891  {
2892  DWORD clst, bcs, nsect, ifptr;
2893 
2894  if ( ofs > fp->fsize /* In read-only mode, clip offset with the file size */
2895 #if !_FS_READONLY
2896  && !( fp->flag & FA_WRITE )
2897 #endif
2898  ) ofs = fp->fsize;
2899 
2900  ifptr = fp->fptr;
2901  fp->fptr = nsect = 0;
2902  if ( ofs )
2903  {
2904  bcs = ( DWORD )fp->fs->csize * SS( fp->fs ); /* Cluster size (byte) */
2905  if ( ifptr > 0 &&
2906  ( ofs - 1 ) / bcs >= ( ifptr - 1 ) / bcs ) /* When seek to same or following cluster, */
2907  {
2908  fp->fptr = ( ifptr - 1 ) & ~( bcs - 1 ); /* start from the current cluster */
2909  ofs -= fp->fptr;
2910  clst = fp->curr_clust;
2911  }
2912  else /* When seek to back cluster, */
2913  {
2914  clst = fp->org_clust; /* start from the first cluster */
2915 #if !_FS_READONLY
2916  if ( clst == 0 ) /* If no cluster chain, create a new chain */
2917  {
2918  clst = create_chain( fp->fs, 0 );
2919  if ( clst == 1 ) ABORT( fp->fs, FR_INT_ERR );
2920  if ( clst == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
2921  fp->org_clust = clst;
2922  }
2923 #endif
2924  fp->curr_clust = clst;
2925  }
2926  if ( clst != 0 )
2927  {
2928  while ( ofs > bcs ) /* Cluster following loop */
2929  {
2930 #if !_FS_READONLY
2931  if ( fp->flag & FA_WRITE ) /* Check if in write mode or not */
2932  {
2933  clst = create_chain( fp->fs, clst ); /* Force stretch if in write mode */
2934  if ( clst == 0 ) /* When disk gets full, clip file size */
2935  {
2936  ofs = bcs; break;
2937  }
2938  }
2939  else
2940 #endif
2941  clst = get_fat( fp->fs, clst ); /* Follow cluster chain if not in write mode */
2942  if ( clst == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
2943  if ( clst <= 1 || clst >= fp->fs->n_fatent ) ABORT( fp->fs, FR_INT_ERR );
2944  fp->curr_clust = clst;
2945  fp->fptr += bcs;
2946  ofs -= bcs;
2947  }
2948  fp->fptr += ofs;
2949  if ( ofs % SS( fp->fs ) )
2950  {
2951  nsect = clust2sect( fp->fs, clst ); /* Current sector */
2952  if ( !nsect ) ABORT( fp->fs, FR_INT_ERR );
2953  nsect += ofs / SS( fp->fs );
2954  }
2955  }
2956  }
2957  if ( fp->fptr % SS( fp->fs ) && nsect != fp->dsect )
2958  {
2959 #if !_FS_TINY
2960 #if !_FS_READONLY
2961  if ( fp->flag & FA__DIRTY ) /* Flush dirty buffer if needed */
2962  {
2963  if ( disk_write( fp->fs->drv, fp->buf, fp->dsect, 1 ) != RES_OK )
2964  ABORT( fp->fs, FR_DISK_ERR );
2965  fp->flag &= ~FA__DIRTY;
2966  }
2967 #endif
2968  if ( disk_read( fp->fs->drv, fp->buf, nsect, 1 ) != RES_OK )
2969  ABORT( fp->fs, FR_DISK_ERR );
2970 #endif
2971  fp->dsect = nsect;
2972  }
2973 #if !_FS_READONLY
2974  if ( fp->fptr > fp->fsize ) /* Set change flag if the file size is extended */
2975  {
2976  fp->fsize = fp->fptr;
2977  fp->flag |= FA__WRITTEN;
2978  }
2979 #endif
2980  }
2981 
2982  LEAVE_FF( fp->fs, res );
2983 }
2984 
2985 
2986 
2987 #if _FS_MINIMIZE <= 1
2988 /*-----------------------------------------------------------------------*/
2989 /* Create a Directroy Object */
2990 /*-----------------------------------------------------------------------*/
2991 
2992 NODEBUG FRESULT f_opendir(
2993  DIR* dj, /* Pointer to directory object to create */
2994  const TCHAR* path /* Pointer to the directory path */
2995 )
2996 {
2997  FRESULT res;
2998  DEF_NAMEBUF;
2999 
3000 
3001  res = chk_mounted( &path, &dj->fs, 0 );
3002  if ( res == FR_OK )
3003  {
3004  INIT_BUF( *dj );
3005  res = follow_path( dj, path ); /* Follow the path to the directory */
3006  FREE_BUF();
3007  if ( res == FR_OK ) /* Follow completed */
3008  {
3009  if ( dj->dir ) /* It is not the root dir */
3010  {
3011  if ( dj->dir[DIR_Attr] & AM_DIR ) /* The object is a directory */
3012  {
3013  dj->sclust = LD_CLUST( dj->dir );
3014  }
3015  else /* The object is not a directory */
3016  {
3017  res = FR_NO_PATH;
3018  }
3019  }
3020  if ( res == FR_OK )
3021  {
3022  dj->id = dj->fs->id;
3023  res = dir_sdi( dj, 0 ); /* Rewind dir */
3024  }
3025  }
3026  if ( res == FR_NO_FILE ) res = FR_NO_PATH;
3027  }
3028 
3029  LEAVE_FF( dj->fs, res );
3030 }
3031 
3032 
3033 
3034 
3035 /*-----------------------------------------------------------------------*/
3036 /* Read Directory Entry in Sequense */
3037 /*-----------------------------------------------------------------------*/
3038 
3039 NODEBUG FRESULT f_readdir(
3040  DIR* dj, /* Pointer to the open directory object */
3041  FILINFO* fno /* Pointer to file information to return */
3042 )
3043 {
3044  FRESULT res;
3045  DEF_NAMEBUF;
3046 
3047 
3048  res = validate( dj->fs, dj->id ); /* Check validity of the object */
3049  if ( res == FR_OK )
3050  {
3051  if ( !fno )
3052  {
3053  res = dir_sdi( dj, 0 ); /* Rewind the directory object */
3054  }
3055  else
3056  {
3057  INIT_BUF( *dj );
3058  res = dir_read( dj ); /* Read an directory item */
3059  if ( res == FR_NO_FILE ) /* Reached end of dir */
3060  {
3061  dj->sect = 0;
3062  res = FR_OK;
3063  }
3064  if ( res == FR_OK ) /* A valid entry is found */
3065  {
3066  get_fileinfo( dj, fno ); /* Get the object information */
3067  res = dir_next( dj, 0 ); /* Increment index for next */
3068  if ( res == FR_NO_FILE )
3069  {
3070  dj->sect = 0;
3071  res = FR_OK;
3072  }
3073  }
3074  FREE_BUF();
3075  }
3076  }
3077 
3078  LEAVE_FF( dj->fs, res );
3079 }
3080 
3081 
3082 
3083 #if _FS_MINIMIZE == 0
3084 /*-----------------------------------------------------------------------*/
3085 /* Get File Status */
3086 /*-----------------------------------------------------------------------*/
3087 
3088 NODEBUG FRESULT f_stat(
3089  const TCHAR* path, /* Pointer to the file path */
3090  FILINFO* fno /* Pointer to file information to return */
3091 )
3092 {
3093  FRESULT res;
3094  DIR dj;
3095  DEF_NAMEBUF;
3096 
3097 
3098  res = chk_mounted( &path, &dj.fs, 0 );
3099  if ( res == FR_OK )
3100  {
3101  INIT_BUF( dj );
3102  res = follow_path( &dj, path ); /* Follow the file path */
3103  if ( res == FR_OK ) /* Follow completed */
3104  {
3105  if ( dj.dir ) /* Found an object */
3106  get_fileinfo( &dj, fno );
3107  else /* It is root dir */
3108  res = FR_INVALID_NAME;
3109  }
3110  FREE_BUF();
3111  }
3112 
3113  LEAVE_FF( dj.fs, res );
3114 }
3115 
3116 
3117 
3118 #if !_FS_READONLY
3119 /*-----------------------------------------------------------------------*/
3120 /* Get Number of Free Clusters */
3121 /*-----------------------------------------------------------------------*/
3122 
3123 NODEBUG FRESULT f_getfree(
3124  const TCHAR* path, /* Pointer to the logical drive number (root dir) */
3125  DWORD* nclst, /* Pointer to the variable to return number of free clusters */
3126  FATFS** fatfs /* Pointer to pointer to corresponding file system object to return */
3127 )
3128 {
3129  FRESULT res;
3130  DWORD n, clst, sect, stat;
3131  UINT i;
3132  BYTE fat, *p;
3133 
3134 
3135  /* Get drive number */
3136  res = chk_mounted( &path, fatfs, 0 );
3137  if ( res == FR_OK )
3138  {
3139  /* If free_clust is valid, return it without full cluster scan */
3140  if ( ( *fatfs )->free_clust <= ( *fatfs )->n_fatent - 2 )
3141  {
3142  *nclst = ( *fatfs )->free_clust;
3143  }
3144  else
3145  {
3146  /* Get number of free clusters */
3147  fat = ( *fatfs )->fs_type;
3148  n = 0;
3149  if ( fat == FS_FAT12 )
3150  {
3151  clst = 2;
3152  do
3153  {
3154  stat = get_fat( *fatfs, clst );
3155  if ( stat == 0xFFFFFFFF ) { res = FR_DISK_ERR; break; }
3156  if ( stat == 1 ) { res = FR_INT_ERR; break; }
3157  if ( stat == 0 ) n++;
3158  }
3159  while ( ++clst < ( *fatfs )->n_fatent );
3160  }
3161  else
3162  {
3163  clst = ( *fatfs )->n_fatent;
3164  sect = ( *fatfs )->fatbase;
3165  i = 0; p = 0;
3166  do
3167  {
3168  if ( !i )
3169  {
3170  res = move_window( *fatfs, sect++ );
3171  if ( res != FR_OK ) break;
3172  p = ( *fatfs )->win;
3173  i = SS( *fatfs );
3174  }
3175  if ( fat == FS_FAT16 )
3176  {
3177  if ( LD_WORD( p ) == 0 ) n++;
3178  p += 2; i -= 2;
3179  }
3180  else
3181  {
3182  if ( ( LD_DWORD( p ) & 0x0FFFFFFF ) == 0 ) n++;
3183  p += 4; i -= 4;
3184  }
3185  }
3186  while ( --clst );
3187  }
3188  ( *fatfs )->free_clust = n;
3189  if ( fat == FS_FAT32 )( *fatfs )->fsi_flag = 1;
3190  *nclst = n;
3191  }
3192  }
3193  LEAVE_FF( *fatfs, res );
3194 }
3195 
3196 
3197 
3198 
3199 /*-----------------------------------------------------------------------*/
3200 /* Truncate File */
3201 /*-----------------------------------------------------------------------*/
3202 
3203 NODEBUG FRESULT f_truncate(
3204  FIL* fp /* Pointer to the file object */
3205 )
3206 {
3207  FRESULT res;
3208  DWORD ncl;
3209 
3210 
3211  res = validate( fp->fs, fp->id ); /* Check validity of the object */
3212  if ( res == FR_OK )
3213  {
3214  if ( fp->flag & FA__ERROR ) /* Check abort flag */
3215  {
3216  res = FR_INT_ERR;
3217  }
3218  else
3219  {
3220  if ( !( fp->flag & FA_WRITE ) ) /* Check access mode */
3221  res = FR_DENIED;
3222  }
3223  }
3224  if ( res == FR_OK )
3225  {
3226  if ( fp->fsize > fp->fptr )
3227  {
3228  fp->fsize = fp->fptr; /* Set file size to current R/W point */
3229  fp->flag |= FA__WRITTEN;
3230  if ( fp->fptr == 0 ) /* When set file size to zero, remove entire cluster chain */
3231  {
3232  res = remove_chain( fp->fs, fp->org_clust );
3233  fp->org_clust = 0;
3234  }
3235  else /* When truncate a part of the file, remove remaining clusters */
3236  {
3237  ncl = get_fat( fp->fs, fp->curr_clust );
3238  res = FR_OK;
3239  if ( ncl == 0xFFFFFFFF ) res = FR_DISK_ERR;
3240  if ( ncl == 1 ) res = FR_INT_ERR;
3241  if ( res == FR_OK && ncl < fp->fs->n_fatent )
3242  {
3243  res = put_fat( fp->fs, fp->curr_clust, 0x0FFFFFFF );
3244  if ( res == FR_OK ) res = remove_chain( fp->fs, ncl );
3245  }
3246  }
3247  }
3248  if ( res != FR_OK ) fp->flag |= FA__ERROR;
3249  }
3250 
3251  LEAVE_FF( fp->fs, res );
3252 }
3253 
3254 
3255 
3256 
3257 /*-----------------------------------------------------------------------*/
3258 /* Delete a File or Directory */
3259 /*-----------------------------------------------------------------------*/
3260 
3261 NODEBUG FRESULT f_unlink(
3262  const TCHAR* path /* Pointer to the file or directory path */
3263 )
3264 {
3265  FRESULT res;
3266  DIR dj, sdj;
3267  BYTE* dir;
3268  DWORD dclst;
3269  DEF_NAMEBUF;
3270 
3271 
3272  res = chk_mounted( &path, &dj.fs, 1 );
3273  if ( res == FR_OK )
3274  {
3275  INIT_BUF( dj );
3276  res = follow_path( &dj, path ); /* Follow the file path */
3277  if ( _FS_RPATH && res == FR_OK && ( dj.fn[NS] & NS_DOT ) )
3278  res = FR_INVALID_NAME; /* Cannot remove dot entry */
3279 #if _FS_SHARE
3280  if ( res == FR_OK ) res = chk_lock( &dj, 2 ); /* Cannot remove open file */
3281 #endif
3282  if ( res == FR_OK ) /* The object is accessible */
3283  {
3284  dir = dj.dir;
3285  if ( !dir )
3286  {
3287  res = FR_INVALID_NAME; /* Cannot remove the start directory */
3288  }
3289  else
3290  {
3291  if ( dir[DIR_Attr] & AM_RDO )
3292  res = FR_DENIED; /* Cannot remove R/O object */
3293  }
3294  dclst = LD_CLUST( dir );
3295  if ( res == FR_OK && ( dir[DIR_Attr] & AM_DIR ) ) /* Is it a sub-dir? */
3296  {
3297  if ( dclst < 2 )
3298  {
3299  res = FR_INT_ERR;
3300  }
3301  else
3302  {
3303  mem_cpy( &sdj, &dj, sizeof( DIR ) ); /* Check if the sub-dir is empty or not */
3304  sdj.sclust = dclst;
3305  res = dir_sdi( &sdj, 2 ); /* Exclude dot entries */
3306  if ( res == FR_OK )
3307  {
3308  res = dir_read( &sdj );
3309  if ( res == FR_OK /* Not empty dir */
3310 #if _FS_RPATH
3311  || dclst == sdj.fs->cdir /* Current dir */
3312 #endif
3313  ) res = FR_DENIED;
3314  if ( res == FR_NO_FILE ) res = FR_OK; /* Empty */
3315  }
3316  }
3317  }
3318  if ( res == FR_OK )
3319  {
3320  res = dir_remove( &dj ); /* Remove the directory entry */
3321  if ( res == FR_OK )
3322  {
3323  if ( dclst ) /* Remove the cluster chain if exist */
3324  res = remove_chain( dj.fs, dclst );
3325  if ( res == FR_OK ) res = sync( dj.fs );
3326  }
3327  }
3328  }
3329  FREE_BUF();
3330  }
3331  LEAVE_FF( dj.fs, res );
3332 }
3333 
3334 
3335 
3336 
3337 /*-----------------------------------------------------------------------*/
3338 /* Create a Directory */
3339 /*-----------------------------------------------------------------------*/
3340 
3341 NODEBUG FRESULT f_mkdir(
3342  const TCHAR* path /* Pointer to the directory path */
3343 )
3344 {
3345  FRESULT res;
3346  DIR dj;
3347  BYTE* dir, n;
3348  DWORD dsc, dcl, pcl, tim = get_fattime();
3349  DEF_NAMEBUF;
3350 
3351 
3352  res = chk_mounted( &path, &dj.fs, 1 );
3353  if ( res == FR_OK )
3354  {
3355  INIT_BUF( dj );
3356  res = follow_path( &dj, path ); /* Follow the file path */
3357  if ( res == FR_OK ) res = FR_EXIST; /* Any object with same name is already existing */
3358  if ( _FS_RPATH && res == FR_NO_FILE && ( dj.fn[NS] & NS_DOT ) )
3359  res = FR_INVALID_NAME;
3360  if ( res == FR_NO_FILE ) /* Can create a new directory */
3361  {
3362  dcl = create_chain( dj.fs, 0 ); /* Allocate a cluster for the new directory table */
3363  res = FR_OK;
3364  if ( dcl == 0 ) res = FR_DENIED; /* No space to allocate a new cluster */
3365  if ( dcl == 1 ) res = FR_INT_ERR;
3366  if ( dcl == 0xFFFFFFFF ) res = FR_DISK_ERR;
3367  if ( res == FR_OK ) /* Flush FAT */
3368  res = move_window( dj.fs, 0 );
3369  if ( res == FR_OK ) /* Initialize the new directory table */
3370  {
3371  dsc = clust2sect( dj.fs, dcl );
3372  dir = dj.fs->win;
3373  mem_set( dir, 0, SS( dj.fs ) );
3374  mem_set( dir + DIR_Name, ' ', 8 + 3 ); /* Create "." entry */
3375  dir[DIR_Name] = '.';
3376  dir[DIR_Attr] = AM_DIR;
3377  ST_DWORD( dir + DIR_WrtTime, tim );
3378  ST_CLUST( dir, dcl );
3379  mem_cpy( dir + 32, dir, 32 ); /* Create ".." entry */
3380  dir[33] = '.'; pcl = dj.sclust;
3381  if ( dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase )
3382  pcl = 0;
3383  ST_CLUST( dir + 32, pcl );
3384  for ( n = dj.fs->csize; n; n-- ) /* Write dot entries and clear following sectors */
3385  {
3386  dj.fs->winsect = dsc++;
3387  dj.fs->wflag = 1;
3388  res = move_window( dj.fs, 0 );
3389  if ( res != FR_OK ) break;
3390  mem_set( dir, 0, SS( dj.fs ) );
3391  }
3392  }
3393  if ( res == FR_OK ) res = dir_register( &dj ); /* Register the object to the directoy */
3394  if ( res != FR_OK )
3395  {
3396  remove_chain( dj.fs, dcl ); /* Could not register, remove cluster chain */
3397  }
3398  else
3399  {
3400  dir = dj.dir;
3401  dir[DIR_Attr] = AM_DIR; /* Attribute */
3402  ST_DWORD( dir + DIR_WrtTime, tim ); /* Created time */
3403  ST_CLUST( dir, dcl ); /* Table start cluster */
3404  dj.fs->wflag = 1;
3405  res = sync( dj.fs );
3406  }
3407  }
3408  FREE_BUF();
3409  }
3410 
3411  LEAVE_FF( dj.fs, res );
3412 }
3413 
3414 
3415 
3416 
3417 /*-----------------------------------------------------------------------*/
3418 /* Change Attribute */
3419 /*-----------------------------------------------------------------------*/
3420 
3421 NODEBUG FRESULT f_chmod(
3422  const TCHAR* path, /* Pointer to the file path */
3423  BYTE value, /* Attribute bits */
3424  BYTE mask /* Attribute mask to change */
3425 )
3426 {
3427  FRESULT res;
3428  DIR dj;
3429  BYTE* dir;
3430  DEF_NAMEBUF;
3431 
3432 
3433  res = chk_mounted( &path, &dj.fs, 1 );
3434  if ( res == FR_OK )
3435  {
3436  INIT_BUF( dj );
3437  res = follow_path( &dj, path ); /* Follow the file path */
3438  FREE_BUF();
3439  if ( _FS_RPATH && res == FR_OK && ( dj.fn[NS] & NS_DOT ) )
3440  res = FR_INVALID_NAME;
3441  if ( res == FR_OK )
3442  {
3443  dir = dj.dir;
3444  if ( !dir ) /* Is it a root directory? */
3445  {
3446  res = FR_INVALID_NAME;
3447  }
3448  else /* File or sub directory */
3449  {
3450  mask &= AM_RDO | AM_HID | AM_SYS | AM_ARC; /* Valid attribute mask */
3451  dir[DIR_Attr] = ( value & mask ) | ( dir[DIR_Attr] & ( BYTE )~mask ); /* Apply attribute change */
3452  dj.fs->wflag = 1;
3453  res = sync( dj.fs );
3454  }
3455  }
3456  }
3457 
3458  LEAVE_FF( dj.fs, res );
3459 }
3460 
3461 
3462 
3463 
3464 /*-----------------------------------------------------------------------*/
3465 /* Change Timestamp */
3466 /*-----------------------------------------------------------------------*/
3467 
3468 NODEBUG FRESULT f_utime(
3469  const TCHAR* path, /* Pointer to the file/directory name */
3470  const FILINFO* fno /* Pointer to the time stamp to be set */
3471 )
3472 {
3473  FRESULT res;
3474  DIR dj;
3475  BYTE* dir;
3476  DEF_NAMEBUF;
3477 
3478 
3479  res = chk_mounted( &path, &dj.fs, 1 );
3480  if ( res == FR_OK )
3481  {
3482  INIT_BUF( dj );
3483  res = follow_path( &dj, path ); /* Follow the file path */
3484  FREE_BUF();
3485  if ( _FS_RPATH && res == FR_OK && ( dj.fn[NS] & NS_DOT ) )
3486  res = FR_INVALID_NAME;
3487  if ( res == FR_OK )
3488  {
3489  dir = dj.dir;
3490  if ( !dir ) /* Root directory */
3491  {
3492  res = FR_INVALID_NAME;
3493  }
3494  else /* File or sub-directory */
3495  {
3496  ST_WORD( dir + DIR_WrtTime, fno->ftime );
3497  ST_WORD( dir + DIR_WrtDate, fno->fdate );
3498  dj.fs->wflag = 1;
3499  res = sync( dj.fs );
3500  }
3501  }
3502  }
3503 
3504  LEAVE_FF( dj.fs, res );
3505 }
3506 
3507 
3508 
3509 
3510 /*-----------------------------------------------------------------------*/
3511 /* Rename File/Directory */
3512 /*-----------------------------------------------------------------------*/
3513 
3514 NODEBUG FRESULT f_rename(
3515  const TCHAR* path_old, /* Pointer to the old name */
3516  const TCHAR* path_new /* Pointer to the new name */
3517 )
3518 {
3519  FRESULT res;
3520  DIR djo, djn;
3521  BYTE buf[21], *dir;
3522  DWORD dw;
3523  DEF_NAMEBUF;
3524 
3525 
3526  res = chk_mounted( &path_old, &djo.fs, 1 );
3527  if ( res == FR_OK )
3528  {
3529  djn.fs = djo.fs;
3530  INIT_BUF( djo );
3531  res = follow_path( &djo, path_old ); /* Check old object */
3532  if ( _FS_RPATH && res == FR_OK && ( djo.fn[NS] & NS_DOT ) )
3533  res = FR_INVALID_NAME;
3534 #if _FS_SHARE
3535  if ( res == FR_OK ) res = chk_lock( &djo, 2 );
3536 #endif
3537  if ( res == FR_OK ) /* Old object is found */
3538  {
3539  if ( !djo.dir ) /* Is root dir? */
3540  {
3541  res = FR_NO_FILE;
3542  }
3543  else
3544  {
3545  mem_cpy( buf, djo.dir + DIR_Attr, 21 ); /* Save the object information except for name */
3546  mem_cpy( &djn, &djo, sizeof( DIR ) ); /* Check new object */
3547  res = follow_path( &djn, path_new );
3548  if ( res == FR_OK ) res = FR_EXIST; /* The new object name is already existing */
3549  if ( res == FR_NO_FILE ) /* Is it a valid path and no name collision? */
3550  {
3551  /* Start critical section that any interruption or error can cause cross-link */
3552  res = dir_register( &djn ); /* Register the new entry */
3553  if ( res == FR_OK )
3554  {
3555  dir = djn.dir; /* Copy object information except for name */
3556  mem_cpy( dir + 13, buf + 2, 19 );
3557  dir[DIR_Attr] = buf[0] | AM_ARC;
3558  djo.fs->wflag = 1;
3559  if ( djo.sclust != djn.sclust && ( dir[DIR_Attr] & AM_DIR ) ) /* Update .. entry in the directory if needed */
3560  {
3561  dw = clust2sect( djn.fs, LD_CLUST( dir ) );
3562  if ( !dw )
3563  {
3564  res = FR_INT_ERR;
3565  }
3566  else
3567  {
3568  res = move_window( djn.fs, dw );
3569  dir = djn.fs->win + 32; /* .. entry */
3570  if ( res == FR_OK && dir[1] == '.' )
3571  {
3572  dw = ( djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase ) ? 0 : djn.sclust;
3573  ST_CLUST( dir, dw );
3574  djn.fs->wflag = 1;
3575  }
3576  }
3577  }
3578  if ( res == FR_OK )
3579  {
3580  res = dir_remove( &djo ); /* Remove old entry */
3581  if ( res == FR_OK )
3582  res = sync( djo.fs );
3583  }
3584  }
3585  /* End critical section */
3586  }
3587  }
3588  }
3589  FREE_BUF();
3590  }
3591  LEAVE_FF( djo.fs, res );
3592 }
3593 
3594 #endif /* !_FS_READONLY */
3595 #endif /* _FS_MINIMIZE == 0 */
3596 #endif /* _FS_MINIMIZE <= 1 */
3597 #endif /* _FS_MINIMIZE <= 2 */
3598 
3599 
3600 
3601 /*-----------------------------------------------------------------------*/
3602 /* Forward data to the stream directly (available on only tiny cfg) */
3603 /*-----------------------------------------------------------------------*/
3604 #if _USE_FORWARD && _FS_TINY
3605 
3606 NODEBUG FRESULT f_forward(
3607  FIL* fp, /* Pointer to the file object */
3608  UINT( *func )( const BYTE*, UINT ), /* Pointer to the streaming function */
3609  UINT btr, /* Number of bytes to forward */
3610  UINT* bf /* Pointer to number of bytes forwarded */
3611 )
3612 {
3613  FRESULT res;
3614  DWORD remain, clst, sect;
3615  UINT rcnt;
3616  BYTE csect;
3617 
3618 
3619  *bf = 0; /* Initialize byte counter */
3620 
3621  res = validate( fp->fs, fp->id ); /* Check validity of the object */
3622  if ( res != FR_OK ) LEAVE_FF( fp->fs, res );
3623  if ( fp->flag & FA__ERROR ) /* Check error flag */
3624  LEAVE_FF( fp->fs, FR_INT_ERR );
3625  if ( !( fp->flag & FA_READ ) ) /* Check access mode */
3626  LEAVE_FF( fp->fs, FR_DENIED );
3627 
3628  remain = fp->fsize - fp->fptr;
3629  if ( btr > remain ) btr = ( UINT )remain; /* Truncate btr by remaining bytes */
3630 
3631  for ( ; btr && ( *func )( 0, 0 ); /* Repeat until all data transferred or stream becomes busy */
3632  fp->fptr += rcnt, *bf += rcnt, btr -= rcnt )
3633  {
3634  csect = ( BYTE )( fp->fptr / SS( fp->fs ) & ( fp->fs->csize - 1 ) ); /* Sector offset in the cluster */
3635  if ( ( fp->fptr % SS( fp->fs ) ) == 0 ) /* On the sector boundary? */
3636  {
3637  if ( !csect ) /* On the cluster boundary? */
3638  {
3639  clst = ( fp->fptr == 0 ) ? /* On the top of the file? */
3640  fp->org_clust : get_fat( fp->fs, fp->curr_clust );
3641  if ( clst <= 1 ) ABORT( fp->fs, FR_INT_ERR );
3642  if ( clst == 0xFFFFFFFF ) ABORT( fp->fs, FR_DISK_ERR );
3643  fp->curr_clust = clst; /* Update current cluster */
3644  }
3645  }
3646  sect = clust2sect( fp->fs, fp->curr_clust ); /* Get current data sector */
3647  if ( !sect ) ABORT( fp->fs, FR_INT_ERR );
3648  sect += csect;
3649  if ( move_window( fp->fs, sect ) ) /* Move sector window */
3650  ABORT( fp->fs, FR_DISK_ERR );
3651  fp->dsect = sect;
3652  rcnt = SS( fp->fs ) - ( WORD )( fp->fptr % SS( fp->fs ) ); /* Forward data from sector window */
3653  if ( rcnt > btr ) rcnt = btr;
3654  rcnt = ( *func )( &fp->fs->win[( WORD )fp->fptr % SS( fp->fs )], rcnt );
3655  if ( !rcnt ) ABORT( fp->fs, FR_INT_ERR );
3656  }
3657 
3658  LEAVE_FF( fp->fs, FR_OK );
3659 }
3660 #endif /* _USE_FORWARD */
3661 
3662 
3663 
3664 #if _USE_MKFS && !_FS_READONLY
3665 /*-----------------------------------------------------------------------*/
3666 /* Create File System on the Drive */
3667 /*-----------------------------------------------------------------------*/
3668 #define N_ROOTDIR 512 /* Multiple of 32 */
3669 #define N_FATS 1 /* 1 or 2 */
3670 
3671 
3672 NODEBUG FRESULT f_mkfs(
3673  BYTE drv, /* Logical drive number */
3674  BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */
3675  UINT au /* Allocation unit size [bytes] */
3676 )
3677 {
3678  static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0};
3679  static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
3680  BYTE fmt, md, *tbl;
3681  DWORD n_clst, vs, n, wsect;
3682  UINT i;
3683  DWORD b_vol, b_fat, b_dir, b_data; /* Offset (LBA) */
3684  DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */
3685  FATFS* fs;
3686  DSTATUS stat;
3687 
3688 
3689  /* Check mounted drive and clear work area */
3690  if ( drv >= _VOLUMES ) return FR_INVALID_DRIVE;
3691  fs = FatFs[drv];
3692  if ( !fs ) return FR_NOT_ENABLED;
3693  fs->fs_type = 0;
3694  drv = LD2PD( drv );
3695 
3696  /* Get disk statics */
3697  stat = disk_initialize( drv );
3698  if ( stat & STA_NOINIT ) return FR_NOT_READY;
3699  if ( stat & STA_PROTECT ) return FR_WRITE_PROTECTED;
3700 #if _MAX_SS != 512 /* Get disk sector size */
3701  if ( disk_ioctl( drv, GET_SECTOR_SIZE, &SS( fs ) ) != RES_OK )
3702  return FR_DISK_ERR;
3703 #endif
3704  if ( disk_ioctl( drv, GET_SECTOR_COUNT, &n_vol ) != RES_OK || n_vol < 128 )
3705  return FR_DISK_ERR;
3706  b_vol = ( sfd ) ? 0 : 63; /* Volume start sector */
3707  n_vol -= b_vol;
3708  if ( au & ( au - 1 ) ) au = 0; /* Check validity of the allocation unit size */
3709  if ( !au ) /* AU auto selection */
3710  {
3711  vs = n_vol / ( 2000 / ( SS( fs ) / 512 ) );
3712  for ( i = 0; vs < vst[i]; i++ ) ;
3713  au = cst[i];
3714  }
3715  au /= SS( fs ); /* Number of sectors per cluster */
3716  if ( au == 0 ) au = 1;
3717  if ( au > 128 ) au = 128;
3718 
3719  /* Pre-compute number of clusters and FAT syb-type */
3720  n_clst = n_vol / au;
3721  fmt = FS_FAT12;
3722  if ( n_clst >= MIN_FAT16 ) fmt = FS_FAT16;
3723  if ( n_clst >= MIN_FAT32 ) fmt = FS_FAT32;
3724 
3725  /* Determine offset and size of FAT structure */
3726  if ( fmt == FS_FAT32 )
3727  {
3728  n_fat = ( ( n_clst * 4 ) + 8 + SS( fs ) - 1 ) / SS( fs );
3729  n_rsv = 32;
3730  n_dir = 0;
3731  }
3732  else
3733  {
3734  n_fat = ( fmt == FS_FAT12 ) ? ( n_clst * 3 + 1 ) / 2 + 3 : ( n_clst * 2 ) + 4;
3735  n_fat = ( n_fat + SS( fs ) - 1 ) / SS( fs );
3736  n_rsv = 1;
3737  n_dir = N_ROOTDIR * 32UL / SS( fs );
3738  }
3739  b_fat = b_vol + n_rsv; /* FAT area start sector */
3740  b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */
3741  b_data = b_dir + n_dir; /* Data area start sector */
3742  if ( n_vol < b_data + au ) return FR_MKFS_ABORTED; /* Too small volume */
3743 
3744  /* Align data start sector to erase block boundary (for flash memory media) */
3745  if ( disk_ioctl( drv, GET_BLOCK_SIZE, &n ) != RES_OK || !n || n > 32768 ) n = 1;
3746  n = ( b_data + n - 1 ) & ~( n - 1 ); /* Next nearest erase block from current data start */
3747  n = ( n - b_data ) / N_FATS;
3748  if ( fmt == FS_FAT32 ) /* FAT32: Move FAT offset */
3749  {
3750  n_rsv += n;
3751  b_fat += n;
3752  }
3753  else /* FAT12/16: Expand FAT size */
3754  {
3755  n_fat += n;
3756  }
3757 
3758  /* Determine number of cluster and final check of validity of the FAT sub-type */
3759  n_clst = ( n_vol - n_rsv - n_fat * N_FATS - n_dir ) / au;
3760  if ( ( fmt == FS_FAT16 && n_clst < MIN_FAT16 )
3761  || ( fmt == FS_FAT32 && n_clst < MIN_FAT32 ) )
3762  return FR_MKFS_ABORTED;
3763 
3764  /* Create partition table if required */
3765  if ( sfd )
3766  {
3767  md = 0xF0;
3768  }
3769  else
3770  {
3771  DWORD n_disk = b_vol + n_vol;
3772 
3773  mem_set( fs->win, 0, SS( fs ) );
3774  tbl = fs->win + MBR_Table;
3775  ST_DWORD( tbl, 0x00010180 ); /* Partition start in CHS */
3776  if ( n_disk < 63UL * 255 * 1024 ) /* Partition end in CHS */
3777  {
3778  n_disk = n_disk / 63 / 255;
3779  tbl[7] = ( BYTE )n_disk;
3780  tbl[6] = ( BYTE )( ( n_disk >> 2 ) | 63 );
3781  }
3782  else
3783  {
3784  ST_WORD( &tbl[6], 0xFFFF );
3785  }
3786  tbl[5] = 254;
3787  if ( fmt != FS_FAT32 ) /* System ID */
3788  tbl[4] = ( n_vol < 0x10000 ) ? 0x04 : 0x06;
3789  else
3790  tbl[4] = 0x0c;
3791  ST_DWORD( tbl + 8, 63 ); /* Partition start in LBA */
3792  ST_DWORD( tbl + 12, n_vol ); /* Partition size in LBA */
3793  ST_WORD( tbl + 64, 0xAA55 ); /* Signature */
3794  if ( disk_write( drv, fs->win, 0, 1 ) != RES_OK )
3795  return FR_DISK_ERR;
3796  md = 0xF8;
3797  }
3798 
3799  /* Create volume boot record */
3800  tbl = fs->win; /* Clear sector */
3801  mem_set( tbl, 0, SS( fs ) );
3802  mem_cpy( tbl, "\xEB\xFE\x90" "MSDOS5.0", 11 ); /* Boot code, OEM name */
3803  i = SS( fs ); /* Sector size */
3804  ST_WORD( tbl + BPB_BytsPerSec, i );
3805  tbl[BPB_SecPerClus] = ( BYTE )au; /* Sectors per cluster */
3806  ST_WORD( tbl + BPB_RsvdSecCnt, n_rsv ); /* Reserved sectors */
3807  tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */
3808  i = ( fmt == FS_FAT32 ) ? 0 : N_ROOTDIR; /* Number of rootdir entries */
3809  ST_WORD( tbl + BPB_RootEntCnt, i );
3810  if ( n_vol < 0x10000 ) /* Number of total sectors */
3811  {
3812  ST_WORD( tbl + BPB_TotSec16, n_vol );
3813  }
3814  else
3815  {
3816  ST_DWORD( tbl + BPB_TotSec32, n_vol );
3817  }
3818  tbl[BPB_Media] = md; /* Media descriptor */
3819  ST_WORD( tbl + BPB_SecPerTrk, 63 ); /* Number of sectors per track */
3820  ST_WORD( tbl + BPB_NumHeads, 255 ); /* Number of heads */
3821  ST_DWORD( tbl + BPB_HiddSec, b_vol ); /* Hidden sectors */
3822  n = get_fattime(); /* Use current time as VSN */
3823  if ( fmt == FS_FAT32 )
3824  {
3825  ST_DWORD( tbl + BS_VolID32, n ); /* VSN */
3826  ST_DWORD( tbl + BPB_FATSz32, n_fat ); /* Number of sectors per FAT */
3827  ST_DWORD( tbl + BPB_RootClus, 2 ); /* Root directory start cluster (2) */
3828  ST_WORD( tbl + BPB_FSInfo, 1 ); /* FSInfo record offset (VBR+1) */
3829  ST_WORD( tbl + BPB_BkBootSec, 6 ); /* Backup boot record offset (VBR+6) */
3830  tbl[BS_DrvNum32] = 0x80; /* Drive number */
3831  tbl[BS_BootSig32] = 0x29; /* Extended boot signature */
3832  mem_cpy( tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19 ); /* Volume label, FAT signature */
3833  }
3834  else
3835  {
3836  ST_DWORD( tbl + BS_VolID, n ); /* VSN */
3837  ST_WORD( tbl + BPB_FATSz16, n_fat ); /* Number of sectors per FAT */
3838  tbl[BS_DrvNum] = 0x80; /* Drive number */
3839  tbl[BS_BootSig] = 0x29; /* Extended boot signature */
3840  mem_cpy( tbl + BS_VolLab, "NO NAME " "FAT ", 19 ); /* Volume label, FAT signature */
3841  }
3842  ST_WORD( tbl + BS_55AA, 0xAA55 ); /* Signature (Offset is fixed here regardless of sector size) */
3843  if ( disk_write( drv, tbl, b_vol, 1 ) != RES_OK ) /* Write original (VBR) */
3844  return FR_DISK_ERR;
3845  if ( fmt == FS_FAT32 ) /* Write backup (VBR+6) */
3846  disk_write( drv, tbl, b_vol + 6, 1 );
3847 
3848  /* Initialize FAT area */
3849  wsect = b_fat;
3850  for ( i = 0; i < N_FATS; i++ )
3851  {
3852  mem_set( tbl, 0, SS( fs ) ); /* 1st sector of the FAT */
3853  n = md; /* Media descriptor byte */
3854  if ( fmt != FS_FAT32 )
3855  {
3856  n |= ( fmt == FS_FAT12 ) ? 0x00FFFF00 : 0xFFFFFF00;
3857  ST_DWORD( tbl + 0, n ); /* Reserve cluster #0-1 (FAT12/16) */
3858  }
3859  else
3860  {
3861  n |= 0xFFFFFF00;
3862  ST_DWORD( tbl + 0, n ); /* Reserve cluster #0-1 (FAT32) */
3863  ST_DWORD( tbl + 4, 0xFFFFFFFF );
3864  ST_DWORD( tbl + 8, 0x0FFFFFFF ); /* Reserve cluster #2 for root dir */
3865  }
3866  if ( disk_write( drv, tbl, wsect++, 1 ) != RES_OK )
3867  return FR_DISK_ERR;
3868  mem_set( tbl, 0, SS( fs ) ); /* Fill following FAT entries with zero */
3869  for ( n = 1; n < n_fat; n++ ) /* This loop may take a time on FAT32 volume due to many single sector write */
3870  {
3871  if ( disk_write( drv, tbl, wsect++, 1 ) != RES_OK )
3872  return FR_DISK_ERR;
3873  }
3874  }
3875 
3876  /* Initialize root directory */
3877  i = ( fmt == FS_FAT32 ) ? au : n_dir;
3878  do
3879  {
3880  if ( disk_write( drv, tbl, wsect++, 1 ) != RES_OK )
3881  return FR_DISK_ERR;
3882  }
3883  while ( --i );
3884 
3885 #if _USE_ERASE /* Erase data area if needed */
3886  {
3887  DWORD eb[2];
3888 
3889  eb[0] = wsect; eb[1] = wsect + n_clst * au - 1;
3890  disk_ioctl( drv, CTRL_ERASE_SECTOR, eb );
3891  }
3892 #endif
3893 
3894  /* Create FSInfo if needed */
3895  if ( fmt == FS_FAT32 )
3896  {
3897  ST_WORD( tbl + BS_55AA, 0xAA55 );
3898  ST_DWORD( tbl + FSI_LeadSig, 0x41615252 );
3899  ST_DWORD( tbl + FSI_StrucSig, 0x61417272 );
3900  ST_DWORD( tbl + FSI_Free_Count, n_clst - 1 );
3901  ST_DWORD( tbl + FSI_Nxt_Free, 0xFFFFFFFF );
3902  disk_write( drv, tbl, b_vol + 1, 1 ); /* Write original (VBR+1) */
3903  disk_write( drv, tbl, b_vol + 7, 1 ); /* Write backup (VBR+7) */
3904  }
3905 
3906  return ( disk_ioctl( drv, CTRL_SYNC, ( void* )0 ) == RES_OK ) ? FR_OK : FR_DISK_ERR;
3907 }
3908 
3909 #endif /* _USE_MKFS && !_FS_READONLY */
3910 
3911 
3912 
3913 
3914 #if _USE_STRFUNC
3915 /*-----------------------------------------------------------------------*/
3916 /* Get a string from the file */
3917 /*-----------------------------------------------------------------------*/
3918 NODEBUG TCHAR* f_gets(
3919  TCHAR* buff, /* Pointer to the string buffer to read */
3920  int len, /* Size of string buffer (characters) */
3921  FIL* fil /* Pointer to the file object */
3922 )
3923 {
3924  int n = 0;
3925  TCHAR c, *p = buff;
3926  BYTE s[2];
3927  UINT rc;
3928 
3929 
3930  while ( n < len - 1 ) /* Read bytes until buffer gets filled */
3931  {
3932  f_read( fil, s, 1, &rc );
3933  if ( rc != 1 ) break; /* Break on EOF or error */
3934  c = s[0];
3935 #if _LFN_UNICODE /* Read a character in UTF-8 encoding */
3936  if ( c >= 0x80 )
3937  {
3938  if ( c < 0xC0 ) continue; /* Skip stray trailer */
3939  if ( c < 0xE0 ) /* Two-byte sequense */
3940  {
3941  f_read( fil, s, 1, &rc );
3942  if ( rc != 1 ) break;
3943  c = ( ( c & 0x1F ) << 6 ) | ( s[0] & 0x3F );
3944  if ( c < 0x80 ) c = '?';
3945  }
3946  else
3947  {
3948  if ( c < 0xF0 ) /* Three-byte sequense */
3949  {
3950  f_read( fil, s, 2, &rc );
3951  if ( rc != 2 ) break;
3952  c = ( c << 12 ) | ( ( s[0] & 0x3F ) << 6 ) | ( s[1] & 0x3F );
3953  if ( c < 0x800 ) c = '?';
3954  }
3955  else /* Reject four-byte sequense */
3956  {
3957  c = '?';
3958  }
3959  }
3960  }
3961 #endif
3962 #if _USE_STRFUNC >= 2
3963  if ( c == '\r' ) continue; /* Strip '\r' */
3964 #endif
3965  *p++ = c;
3966  n++;
3967  if ( c == '\n' ) break; /* Break on EOL */
3968  }
3969  *p = 0;
3970  return n ? buff : 0; /* When no data read (eof or error), return with error. */
3971 }
3972 
3973 
3974 
3975 #if !_FS_READONLY
3976 #include <stdarg.h>
3977 /*-----------------------------------------------------------------------*/
3978 /* Put a character to the file */
3979 /*-----------------------------------------------------------------------*/
3980 NODEBUG int f_putc(
3981  TCHAR c, /* A character to be output */
3982  FIL* fil /* Pointer to the file object */
3983 )
3984 {
3985  UINT bw, btw;
3986  BYTE s[3];
3987 
3988 
3989 #if _USE_STRFUNC >= 2
3990  if ( c == '\n' ) f_putc( '\r', fil ); /* LF -> CRLF conversion */
3991 #endif
3992 
3993 #if _LFN_UNICODE /* Write the character in UTF-8 encoding */
3994  if ( c < 0x80 ) /* 7-bit */
3995  {
3996  s[0] = ( BYTE )c;
3997  btw = 1;
3998  }
3999  else
4000  {
4001  if ( c < 0x800 ) /* 11-bit */
4002  {
4003  s[0] = ( BYTE )( 0xC0 | ( c >> 6 ) );
4004  s[1] = ( BYTE )( 0x80 | ( c & 0x3F ) );
4005  btw = 2;
4006  }
4007  else /* 16-bit */
4008  {
4009  s[0] = ( BYTE )( 0xE0 | ( c >> 12 ) );
4010  s[1] = ( BYTE )( 0x80 | ( ( c >> 6 ) & 0x3F ) );
4011  s[2] = ( BYTE )( 0x80 | ( c & 0x3F ) );
4012  btw = 3;
4013  }
4014  }
4015 #else /* Write the character without conversion */
4016  s[0] = ( BYTE )c;
4017  btw = 1;
4018 #endif
4019  f_write( fil, s, btw, &bw ); /* Write the char to the file */
4020  return ( bw == btw ) ? 1 : EOF; /* Return the result */
4021 }
4022 
4023 
4024 
4025 
4026 /*-----------------------------------------------------------------------*/
4027 /* Put a string to the file */
4028 /*-----------------------------------------------------------------------*/
4029 NODEBUG int f_puts(
4030  const TCHAR* str, /* Pointer to the string to be output */
4031  FIL* fil /* Pointer to the file object */
4032 )
4033 {
4034  int n;
4035 
4036 
4037  for ( n = 0; *str; str++, n++ )
4038  {
4039  if ( f_putc( *str, fil ) == EOF ) return EOF;
4040  }
4041  return n;
4042 }
4043 
4044 
4045 
4046 
4047 /*-----------------------------------------------------------------------*/
4048 /* Put a formatted string to the file */
4049 /*-----------------------------------------------------------------------*/
4050 NODEBUG int f_printf(
4051  FIL* fil, /* Pointer to the file object */
4052  const TCHAR* str, /* Pointer to the format string */
4053  ... /* Optional arguments... */
4054 )
4055 {
4056  va_list arp;
4057  BYTE f, r;
4058  UINT i, w;
4059  ULONG val;
4060  TCHAR c, d, s[16];
4061  int res, cc;
4062 
4063 
4064  va_start( arp, str );
4065 
4066  for ( cc = res = 0; cc != EOF; res += cc )
4067  {
4068  c = *str++;
4069  if ( c == 0 ) break; /* End of string */
4070  if ( c != '%' ) /* Non escape character */
4071  {
4072  cc = f_putc( c, fil );
4073  if ( cc != EOF ) cc = 1;
4074  continue;
4075  }
4076  w = f = 0;
4077  c = *str++;
4078  if ( c == '0' ) /* Flag: '0' padding */
4079  {
4080  f = 1; c = *str++;
4081  }
4082  while ( IsDigit( c ) ) /* Precision */
4083  {
4084  w = w * 10 + c - '0';
4085  c = *str++;
4086  }
4087  if ( c == 'l' || c == 'L' ) /* Prefix: Size is long int */
4088  {
4089  f |= 2; c = *str++;
4090  }
4091  if ( !c ) break;
4092  d = c;
4093  if ( IsLower( d ) ) d -= 0x20;
4094  switch ( d ) /* Type is... */
4095  {
4096  case 'S' : /* String */
4097  cc = f_puts( va_arg( arp, TCHAR* ), fil ); continue;
4098  case 'C' : /* Character */
4099  cc = f_putc( ( TCHAR )va_arg( arp, int ), fil ); continue;
4100  case 'B' : /* Binary */
4101  r = 2; break;
4102  case 'O' : /* Octal */
4103  r = 8; break;
4104  case 'D' : /* Signed decimal */
4105  case 'U' : /* Unsigned decimal */
4106  r = 10; break;
4107  case 'X' : /* Hexdecimal */
4108  r = 16; break;
4109  default: /* Unknown */
4110  cc = f_putc( c, fil ); continue;
4111  }
4112 
4113  /* Get an argument */
4114  val = ( f & 2 ) ? va_arg( arp, long ) : ( ( d == 'D' ) ? ( long )va_arg( arp, int ) : va_arg( arp, unsigned int ) );
4115  if ( d == 'D' && ( val & 0x80000000 ) )
4116  {
4117  val = 0 - val;
4118  f |= 4;
4119  }
4120  /* Put it in numeral string */
4121  i = 0;
4122  do
4123  {
4124  d = ( TCHAR )( val % r ); val /= r;
4125  if ( d > 9 )
4126  {
4127  d += 7;
4128  if ( c == 'x' ) d += 0x20;
4129  }
4130  s[i++] = d + '0';
4131  }
4132  while ( val && i < sizeof( s ) / sizeof( s[0] ) );
4133  if ( f & 4 ) s[i++] = '-';
4134  cc = 0;
4135  while ( i < w-- && cc != EOF )
4136  {
4137  cc = f_putc( ( TCHAR )( ( f & 1 ) ? '0' : ' ' ), fil );
4138  res++;
4139  }
4140  do
4141  {
4142  cc = f_putc( s[--i], fil );
4143  res++;
4144  }
4145  while ( i && cc != EOF );
4146  if ( cc != EOF ) cc = 0;
4147  }
4148 
4149  va_end( arp );
4150  return ( cc == EOF ) ? cc : res;
4151 }
4152 
4153 #endif /* !_FS_READONLY */
4154 #endif /* _USE_STRFUNC */