ff.c 105 KB
Newer Older
1
/*----------------------------------------------------------------------------/
2
/  FatFs - FAT file system module  R0.08                  (C)ChaN, 2010
3
4
5
/-----------------------------------------------------------------------------/
/ FatFs module is a generic FAT file system module for small embedded systems.
/ This is a free software that opened for education, research and commercial
6
/ developments under license policy of following terms.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/
/  Copyright (C) 2010, ChaN, all right reserved.
/
/ * The FatFs module is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/-----------------------------------------------------------------------------/
/ Feb 26,'06 R0.00  Prototype.
/
/ Apr 29,'06 R0.01  First stable version.
/
/ Jun 01,'06 R0.02  Added FAT12 support.
/                   Removed unbuffered mode.
22
/                   Fixed a problem on small (<32M) partition.
23
24
25
26
/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
/
/ Sep 22,'06 R0.03  Added f_rename().
/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
27
/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
28
29
30
31
32
33
/                   Fixed f_mkdir() creates incorrect directory on FAT32.
/
/ Feb 04,'07 R0.04  Supported multiple drive system.
/                   Changed some interfaces for multiple drive system.
/                   Changed f_mountdrv() to f_mount().
/                   Added f_mkfs().
34
/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/                   Added a capability of extending file size to f_lseek().
/                   Added minimization level 3.
/                   Fixed an endian sensitive code in f_mkfs().
/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
/                   Added FSInfo support.
/                   Fixed DBCS name can result FR_INVALID_NAME.
/                   Fixed short seek (<= csize) collapses the file object.
/
/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
/                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
/                   Fixed off by one error at FAT sub-type determination.
/                   Fixed btr in f_read() can be mistruncated.
/                   Fixed cached sector is not flushed when create and close
/                   without write.
/
/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
/                   Improved performance of f_lseek() on moving to the same
/                   or following cluster.
/
/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.
/                   Added long file name support.
/                   Added multiple code page support.
/                   Added re-entrancy for multitask operation.
/                   Added auto cluster size selection to f_mkfs().
/                   Added rewind option to f_readdir().
/                   Changed result code of critical errors.
/                   Renamed string functions to avoid name collision.
/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
/                   Added multiple sector size support.
/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
/                   Fixed wrong cache control in f_lseek().
/                   Added relative path feature.
/                   Added f_chdir() and f_chdrive().
/                   Added proper case conversion to extended char.
/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
/                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
/                   Fixed name matching error on the 13 char boundary.
/                   Added a configuration option, _LFN_UNICODE.
/                   Changed f_readdir() to return the SFN with always upper
/                   case on non-LFN cfg.
77
78
79
80
81
82
83
/
/ May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN)
/                   Added file lock feature. (_FS_SHARE)
/                   Added fast seek feature. (_USE_FASTSEEK)
/                   Changed some types on the API, XCHAR->TCHAR.
/                   Changed fname member in the FILINFO structure on Unicode cfg.
/                   String functions support UTF-8 encoding files on Unicode cfg.
84
85
86
87
88
/---------------------------------------------------------------------------*/

#include "ff.h"			/* FatFs configurations and declarations */
#include "diskio.h"		/* Declarations of low level disk I/O functions */

89

90
91
92
93
94
95
/*--------------------------------------------------------------------------

   Module Private Definitions

---------------------------------------------------------------------------*/

96
#if _FATFS != 8085
97
98
99
#error Wrong include file (ff.h).
#endif

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

/* FAT sub-type boundaries */
/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
#define MIN_FAT16	4086	/* Minimum number of clusters for FAT16 */
#define	MIN_FAT32	65526	/* Minimum number of clusters for FAT32 */


/* Definitions corresponds to multiple sector size */
#if _MAX_SS == 512		/* Single sector size */
#define	SS(fs)	512U
#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096	/* Multiple sector size */
#define	SS(fs)	((fs)->ssize)
#else
#error Wrong sector size.
#endif


/* Reentrancy related */
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#if _FS_REENTRANT
#if _USE_LFN == 1
#error Static LFN work area must not be used in re-entrant configuration.
#endif
#define	ENTER_FF(fs)		{ if (!lock_fs(fs)) return FR_TIMEOUT; }
#define	LEAVE_FF(fs, res)	{ unlock_fs(fs, res); return res; }

#else
#define	ENTER_FF(fs)
#define LEAVE_FF(fs, res)	return res

#endif

#define	ABORT(fs, res)		{ fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

/* Character code support macros */
#define IsUpper(c)	(((c)>='A')&&((c)<='Z'))
#define IsLower(c)	(((c)>='a')&&((c)<='z'))
#define IsDigit(c)	(((c)>='0')&&((c)<='9'))

#if _DF1S		/* Code page is DBCS */

#ifdef _DF2S	/* Two 1st byte areas */
#define IsDBCS1(c)	(((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
#else			/* One 1st byte area */
#define IsDBCS1(c)	((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
#endif

#ifdef _DS3S	/* Three 2nd byte areas */
#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
#else			/* Two 2nd byte areas */
#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
151
152
#endif

153
154
155
156
157
158
159
160
#else			/* Code page is SBCS */

#define IsDBCS1(c)	0
#define IsDBCS2(c)	0

#endif /* _DF1S */


161
162
163
164
165
166
167
168
169
170
171
/* Name status flags */
#define NS			11		/* Offset of name status byte */
#define NS_LOSS		0x01	/* Out of 8.3 format */
#define NS_LFN		0x02	/* Force to create LFN entry */
#define NS_LAST		0x04	/* Last segment */
#define NS_BODY		0x08	/* Lower case flag (body) */
#define NS_EXT		0x10	/* Lower case flag (ext) */
#define NS_DOT		0x20	/* Dot entry */



172
173
/*------------------------------------------------------------*/
/* Work area                                                  */
174

175
176
#if !_DRIVES
#error Number of drives must not be 0.
177
178
179
#endif
static
WORD Fsid;				/* File system mount ID */
180
181
static
FATFS *FatFs[_DRIVES];	/* Pointer to the file system objects (logical drives) */
182
183
184
185
186
187
188

#if _FS_RPATH
static
BYTE Drive;				/* Current drive */
#endif


189
190
191
192
#if _USE_LFN == 0			/* No LFN */
#define	DEF_NAMEBUF			BYTE sfn[12]
#define INIT_BUF(dobj)		(dobj).fn = sfn
#define	FREE_BUF()
193

194
195
196
197
198
#elif _USE_LFN == 1			/* LFN with static LFN working buffer */
static WCHAR LfnBuf[_MAX_LFN + 1];
#define	DEF_NAMEBUF			BYTE sfn[12]
#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
#define	FREE_BUF()
199

200
201
202
203
#elif _USE_LFN == 2 		/* LFN with dynamic LFN working buffer on the stack */
#define	DEF_NAMEBUF			BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1]
#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = lbuf; }
#define	FREE_BUF()
204

205
206
207
208
209
210
211
212
213
#elif _USE_LFN == 3 		/* LFN with dynamic LFN working buffer on the heap */
#define	DEF_NAMEBUF			BYTE sfn[12]; WCHAR *lfn
#define INIT_BUF(dobj)		{ lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
							  if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
							  (dobj).lfn = lfn;	(dobj).fn = sfn; }
#define	FREE_BUF()			ff_memfree(lfn)

#else
#error Wrong LFN configuration.
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#endif




/*--------------------------------------------------------------------------

   Module Private Functions

---------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------*/
/* String functions                                                      */
/*-----------------------------------------------------------------------*/

/* Copy memory to memory */
static
void mem_cpy (void* dst, const void* src, int cnt) {
233
234
235
236
237
238
239
240
241
242
243
244
	BYTE *d = (BYTE*)dst;
	const BYTE *s = (const BYTE*)src;

#if _WORD_ACCESS == 1
	while (cnt >= sizeof(int)) {
		*(int*)d = *(int*)s;
		d += sizeof(int); s += sizeof(int);
		cnt -= sizeof(int);
	}
#endif
	while (cnt--)
		*d++ = *s++;
245
246
247
248
249
}

/* Fill memory */
static
void mem_set (void* dst, int val, int cnt) {
250
251
252
253
	BYTE *d = (BYTE*)dst;

	while (cnt--)
		*d++ = (BYTE)val;
254
255
256
257
258
}

/* Compare memory to memory */
static
int mem_cmp (const void* dst, const void* src, int cnt) {
259
	const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
260
	int r = 0;
261

262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
	while (cnt-- && (r = *d++ - *s++) == 0) ;
	return r;
}

/* Check if chr is contained in the string */
static
int chk_chr (const char* str, int chr) {
	while (*str && *str != chr) str++;
	return *str;
}



/*-----------------------------------------------------------------------*/
/* Request/Release grant to access the volume                            */
/*-----------------------------------------------------------------------*/
#if _FS_REENTRANT

static
281
int lock_fs (
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
	FATFS *fs		/* File system object */
)
{
	return ff_req_grant(fs->sobj);
}


static
void unlock_fs (
	FATFS *fs,		/* File system object */
	FRESULT res		/* Result code to be returned */
)
{
	if (res != FR_NOT_ENABLED &&
		res != FR_INVALID_DRIVE &&
		res != FR_INVALID_OBJECT &&
		res != FR_TIMEOUT) {
		ff_rel_grant(fs->sobj);
	}
}
#endif



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*-----------------------------------------------------------------------*/
/* File shareing control functions                                       */
/*-----------------------------------------------------------------------*/
#if _FS_SHARE

static
FRESULT chk_lock (	/* Check if the file can be accessed */
	DIR* dj,		/* Directory object pointing the file to be checked */
	int acc			/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
)
{
	UINT i, be;

	/* Search file semaphore table */
	for (i = be = 0; i < _FS_SHARE; i++) {
		if (dj->fs->flsem[i].ctr) {	/* Existing entry */
			if (dj->fs->flsem[i].clu == dj->sclust && 	/* The file is found (identified with its location) */
				dj->fs->flsem[i].idx == dj->index) break;
		} else {					/* Blank entry */
			be++;
		}
	}
	if (i == _FS_SHARE)	/* The file has not been opened */
		return (be || acc != 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;	/* Is there a blank entry for new file? */

	/* The file has been opened. Reject any open against writing file and all write mode open */
	return (acc || dj->fs->flsem[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
}


static
int enq_lock (	/* Check if an entry is available for a new file */
	FATFS* fs	/* File system object */
)
{
	UINT i;

	for (i = 0; i < _FS_SHARE && fs->flsem[i].ctr; i++) ;
	return (i == _FS_SHARE) ? 0 : 1;
}


static
UINT inc_lock (	/* Increment file open counter and returns its index (0:int error) */
	DIR* dj,	/* Directory object pointing the file to register or increment */
	int acc		/* Desired access mode (0:Read, !0:Write) */
)
{
	UINT i;


	for (i = 0; i < _FS_SHARE; i++) {	/* Find the file */
		if (dj->fs->flsem[i].ctr &&
			dj->fs->flsem[i].clu == dj->sclust &&
			dj->fs->flsem[i].idx == dj->index) break;
	}

	if (i == _FS_SHARE) {				/* Not opened. Register it as new. */
		for (i = 0; i < _FS_SHARE && dj->fs->flsem[i].ctr; i++) ;
		if (i == _FS_SHARE) return 0;	/* No space to register (int err) */
		dj->fs->flsem[i].clu = dj->sclust;
		dj->fs->flsem[i].idx = dj->index;
	}

	if (acc && dj->fs->flsem[i].ctr) return 0;	/* Access violation (int err) */

	dj->fs->flsem[i].ctr = acc ? 0x100 : dj->fs->flsem[i].ctr + 1;	/* Set semaphore value */

	return i + 1;
}


static
FRESULT dec_lock (	/* Decrement file open counter */
	FATFS* fs,		/* File system object */
	UINT i			/* Semaphore index */
)
{
	WORD n;
	FRESULT res;


	if (--i < _FS_SHARE) {
		n = fs->flsem[i].ctr;
		if (n >= 0x100) n = 0;
		if (n) n--;
		fs->flsem[i].ctr = n;
		res = FR_OK;
	} else {
		res = FR_INT_ERR;
	}
	return res;
}

#endif



404
405
406
407
408
409
410
/*-----------------------------------------------------------------------*/
/* Change window offset                                                  */
/*-----------------------------------------------------------------------*/

static
FRESULT move_window (
	FATFS *fs,		/* File system object */
411
	DWORD sector	/* Sector number to make appearance in the fs->win[] */
412
413
414
415
416
417
418
419
420
)					/* Move to zero only writes back dirty window */
{
	DWORD wsect;


	wsect = fs->winsect;
	if (wsect != sector) {	/* Changed current window */
#if !_FS_READONLY
		if (fs->wflag) {	/* Write back dirty window if needed */
421
			if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK)
422
423
				return FR_DISK_ERR;
			fs->wflag = 0;
424
			if (wsect < (fs->fatbase + fs->fsize)) {	/* In FAT area */
425
				BYTE nf;
426
427
428
				for (nf = fs->n_fats; nf > 1; nf--) {	/* Reflect the change to all FAT copies */
					wsect += fs->fsize;
					disk_write(fs->drv, fs->win, wsect, 1);
429
430
431
432
433
				}
			}
		}
#endif
		if (sector) {
434
			if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK)
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
				return FR_DISK_ERR;
			fs->winsect = sector;
		}
	}

	return FR_OK;
}




/*-----------------------------------------------------------------------*/
/* Clean-up cached data                                                  */
/*-----------------------------------------------------------------------*/
#if !_FS_READONLY
static
FRESULT sync (	/* FR_OK: successful, FR_DISK_ERR: failed */
	FATFS *fs	/* File system object */
)
{
	FRESULT res;


	res = move_window(fs, 0);
	if (res == FR_OK) {
		/* Update FSInfo sector if needed */
		if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
			fs->winsect = 0;
			mem_set(fs->win, 0, 512);
			ST_WORD(fs->win+BS_55AA, 0xAA55);
			ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
			ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
			ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
			ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
469
			disk_write(fs->drv, fs->win, fs->fsi_sector, 1);
470
471
472
			fs->fsi_flag = 0;
		}
		/* Make sure that no pending write process in the physical drive */
473
		if (disk_ioctl(fs->drv, CTRL_SYNC, (void*)0) != RES_OK)
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
			res = FR_DISK_ERR;
	}

	return res;
}
#endif




/*-----------------------------------------------------------------------*/
/* FAT access - Read value of a FAT entry                                */
/*-----------------------------------------------------------------------*/


489
DWORD get_fat (	/* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
490
491
492
493
494
	FATFS *fs,	/* File system object */
	DWORD clst	/* Cluster# to get the link information */
)
{
	UINT wc, bc;
495
	BYTE *p;
496
497


498
	if (clst < 2 || clst >= fs->n_fatent)	/* Chack range */
499
500
501
502
		return 1;

	switch (fs->fs_type) {
	case FS_FAT12 :
503
504
505
506
507
		bc = (UINT)clst; bc += bc / 2;
		if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
		wc = fs->win[bc % SS(fs)]; bc++;
		if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
		wc |= fs->win[bc % SS(fs)] << 8;
508
509
510
		return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);

	case FS_FAT16 :
511
512
513
		if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
		p = &fs->win[clst * 2 % SS(fs)];
		return LD_WORD(p);
514
515

	case FS_FAT32 :
516
517
518
		if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
		p = &fs->win[clst * 4 % SS(fs)];
		return LD_DWORD(p) & 0x0FFFFFFF;
519
520
	}

521
	return 0xFFFFFFFF;	/* An error occurred at the disk I/O layer */
522
523
524
525
526
527
528
529
530
531
532
533
}




/*-----------------------------------------------------------------------*/
/* FAT access - Change value of a FAT entry                              */
/*-----------------------------------------------------------------------*/
#if !_FS_READONLY

FRESULT put_fat (
	FATFS *fs,	/* File system object */
534
	DWORD clst,	/* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
535
536
537
538
539
540
541
542
	DWORD val	/* New value to mark the cluster */
)
{
	UINT bc;
	BYTE *p;
	FRESULT res;


543
	if (clst < 2 || clst >= fs->n_fatent) {	/* Check range */
544
545
546
547
548
549
		res = FR_INT_ERR;

	} else {
		switch (fs->fs_type) {
		case FS_FAT12 :
			bc = clst; bc += bc / 2;
550
			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
551
			if (res != FR_OK) break;
552
			p = &fs->win[bc % SS(fs)];
553
554
555
			*p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
			bc++;
			fs->wflag = 1;
556
			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
557
			if (res != FR_OK) break;
558
			p = &fs->win[bc % SS(fs)];
559
560
561
562
			*p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
			break;

		case FS_FAT16 :
563
			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
564
			if (res != FR_OK) break;
565
566
			p = &fs->win[clst * 2 % SS(fs)];
			ST_WORD(p, (WORD)val);
567
568
569
			break;

		case FS_FAT32 :
570
			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
571
			if (res != FR_OK) break;
572
573
574
			p = &fs->win[clst * 4 % SS(fs)];
			val |= LD_DWORD(p) & 0xF0000000;
			ST_DWORD(p, val);
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
			break;

		default :
			res = FR_INT_ERR;
		}
		fs->wflag = 1;
	}

	return res;
}
#endif /* !_FS_READONLY */




/*-----------------------------------------------------------------------*/
/* FAT handling - Remove a cluster chain                                 */
/*-----------------------------------------------------------------------*/
#if !_FS_READONLY
static
FRESULT remove_chain (
	FATFS *fs,			/* File system object */
	DWORD clst			/* Cluster# to remove a chain from */
)
{
	FRESULT res;
	DWORD nxt;


604
	if (clst < 2 || clst >= fs->n_fatent) {	/* Check range */
605
606
607
608
		res = FR_INT_ERR;

	} else {
		res = FR_OK;
609
		while (clst < fs->n_fatent) {			/* Not a last link? */
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
			nxt = get_fat(fs, clst);			/* Get cluster status */
			if (nxt == 0) break;				/* Empty cluster? */
			if (nxt == 1) { res = FR_INT_ERR; break; }	/* Internal error? */
			if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }	/* Disk error? */
			res = put_fat(fs, clst, 0);			/* Mark the cluster "empty" */
			if (res != FR_OK) break;
			if (fs->free_clust != 0xFFFFFFFF) {	/* Update FSInfo */
				fs->free_clust++;
				fs->fsi_flag = 1;
			}
			clst = nxt;	/* Next cluster */
		}
	}

	return res;
}
#endif




/*-----------------------------------------------------------------------*/
/* FAT handling - Stretch or Create a cluster chain                      */
/*-----------------------------------------------------------------------*/
#if !_FS_READONLY
static
DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
	FATFS *fs,			/* File system object */
	DWORD clst			/* Cluster# to stretch. 0 means create a new chain. */
)
{
641
	DWORD cs, ncl, scl;
642
643


644
	if (clst == 0) {		/* Create a new chain */
645
		scl = fs->last_clust;			/* Get suggested start point */
646
		if (!scl || scl >= fs->n_fatent) scl = 1;
647
	}
648
	else {					/* Stretch the current chain */
649
650
		cs = get_fat(fs, clst);			/* Check the cluster status */
		if (cs < 2) return 1;			/* It is an invalid cluster */
651
		if (cs < fs->n_fatent) return cs;	/* It is already followed by next cluster */
652
653
654
655
656
657
		scl = clst;
	}

	ncl = scl;				/* Start cluster */
	for (;;) {
		ncl++;							/* Next cluster */
658
		if (ncl >= fs->n_fatent) {		/* Wrap around */
659
			ncl = 2;
660
			if (ncl > scl) return 0;	/* No free cluster */
661
662
663
		}
		cs = get_fat(fs, ncl);			/* Get the cluster status */
		if (cs == 0) break;				/* Found a free cluster */
664
		if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
665
			return cs;
666
		if (ncl == scl) return 0;		/* No free cluster */
667
668
	}

669
	if (put_fat(fs, ncl, 0x0FFFFFFF))	/* Mark the new cluster "last link" */
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
		return 0xFFFFFFFF;
	if (clst != 0) {					/* Link it to the previous one if needed */
		if (put_fat(fs, clst, ncl))
			return 0xFFFFFFFF;
	}

	fs->last_clust = ncl;				/* Update FSINFO */
	if (fs->free_clust != 0xFFFFFFFF) {
		fs->free_clust--;
		fs->fsi_flag = 1;
	}

	return ncl;		/* Return new cluster number */
}
#endif /* !_FS_READONLY */




/*-----------------------------------------------------------------------*/
/* Get sector# from cluster#                                             */
/*-----------------------------------------------------------------------*/


DWORD clust2sect (	/* !=0: Sector number, 0: Failed - invalid cluster# */
	FATFS *fs,		/* File system object */
	DWORD clst		/* Cluster# to be converted */
)
{
	clst -= 2;
700
	if (clst >= (fs->n_fatent - 2)) return 0;		/* Invalid cluster# */
701
702
703
704
705
706
707
	return clst * fs->csize + fs->database;
}




/*-----------------------------------------------------------------------*/
708
/* Directory handling - Set directory index                              */
709
710
711
/*-----------------------------------------------------------------------*/

static
712
FRESULT dir_sdi (
713
714
715
716
717
718
719
720
721
722
	DIR *dj,		/* Pointer to directory object */
	WORD idx		/* Directory index number */
)
{
	DWORD clst;
	WORD ic;


	dj->index = idx;
	clst = dj->sclust;
723
	if (clst == 1 || clst >= dj->fs->n_fatent)	/* Check start cluster range */
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
		return FR_INT_ERR;
	if (!clst && dj->fs->fs_type == FS_FAT32)	/* Replace cluster# 0 with root cluster# if in FAT32 */
		clst = dj->fs->dirbase;

	if (clst == 0) {	/* Static table */
		dj->clust = clst;
		if (idx >= dj->fs->n_rootdir)		/* Index is out of range */
			return FR_INT_ERR;
		dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);	/* Sector# */
	}
	else {				/* Dynamic table */
		ic = SS(dj->fs) / 32 * dj->fs->csize;	/* Entries per cluster */
		while (idx >= ic) {	/* Follow cluster chain */
			clst = get_fat(dj->fs, clst);				/* Get next cluster */
			if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
739
			if (clst < 2 || clst >= dj->fs->n_fatent)	/* Reached to end of table or int error */
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
				return FR_INT_ERR;
			idx -= ic;
		}
		dj->clust = clst;
		dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);	/* Sector# */
	}

	dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;	/* Ptr to the entry in the sector */

	return FR_OK;	/* Seek succeeded */
}




/*-----------------------------------------------------------------------*/
/* Directory handling - Move directory index next                        */
/*-----------------------------------------------------------------------*/

static
760
FRESULT dir_next (	/* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
761
	DIR *dj,		/* Pointer to directory object */
762
	int stretch		/* 0: Do not stretch table, 1: Stretch table if needed */
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
)
{
	DWORD clst;
	WORD i;


	i = dj->index + 1;
	if (!i || !dj->sect)	/* Report EOT when index has reached 65535 */
		return FR_NO_FILE;

	if (!(i % (SS(dj->fs) / 32))) {	/* Sector changed? */
		dj->sect++;					/* Next sector */

		if (dj->clust == 0) {	/* Static table */
			if (i >= dj->fs->n_rootdir)	/* Report EOT when end of table */
				return FR_NO_FILE;
		}
		else {					/* Dynamic table */
			if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {	/* Cluster changed? */
				clst = get_fat(dj->fs, dj->clust);				/* Get next cluster */
				if (clst <= 1) return FR_INT_ERR;
				if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
785
				if (clst >= dj->fs->n_fatent) {					/* When it reached end of dynamic table */
786
787
#if !_FS_READONLY
					BYTE c;
788
789
					if (!stretch) return FR_NO_FILE;			/* When do not stretch, report EOT */
					clst = create_chain(dj->fs, dj->clust);		/* Stretch cluster chain */
790
791
792
					if (clst == 0) return FR_DENIED;			/* No free cluster */
					if (clst == 1) return FR_INT_ERR;
					if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
793
					/* Clean-up stretched table */
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
					if (move_window(dj->fs, 0)) return FR_DISK_ERR;	/* Flush active window */
					mem_set(dj->fs->win, 0, SS(dj->fs));			/* Clear window buffer */
					dj->fs->winsect = clust2sect(dj->fs, clst);	/* Cluster start sector */
					for (c = 0; c < dj->fs->csize; c++) {		/* Fill the new cluster with 0 */
						dj->fs->wflag = 1;
						if (move_window(dj->fs, 0)) return FR_DISK_ERR;
						dj->fs->winsect++;
					}
					dj->fs->winsect -= c;						/* Rewind window address */
#else
					return FR_NO_FILE;			/* Report EOT */
#endif
				}
				dj->clust = clst;				/* Initialize data for new cluster */
				dj->sect = clust2sect(dj->fs, clst);
			}
		}
	}

	dj->index = i;
	dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;

	return FR_OK;
}




/*-----------------------------------------------------------------------*/
/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
/*-----------------------------------------------------------------------*/
#if _USE_LFN
static
const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};	/* Offset of LFN chars in the directory entry */


static
831
int cmp_lfn (			/* 1:Matched, 0:Not matched */
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
	WCHAR *lfnbuf,		/* Pointer to the LFN to be compared */
	BYTE *dir			/* Pointer to the directory entry containing a part of LFN */
)
{
	int i, s;
	WCHAR wc, uc;


	i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;	/* Get offset in the LFN buffer */
	s = 0; wc = 1;
	do {
		uc = LD_WORD(dir+LfnOfs[s]);	/* Pick an LFN character from the entry */
		if (wc) {	/* Last char has not been processed */
			wc = ff_wtoupper(uc);		/* Convert it to upper case */
			if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))	/* Compare it */
847
				return 0;				/* Not matched */
848
		} else {
849
			if (uc != 0xFFFF) return 0;	/* Check filler */
850
851
852
853
		}
	} while (++s < 13);				/* Repeat until all chars in the entry are checked */

	if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i])	/* Last segment matched but different length */
854
		return 0;
855

856
	return 1;						/* The part of LFN matched */
857
858
859
860
861
}



static
862
int pick_lfn (			/* 1:Succeeded, 0:Buffer overflow */
863
864
865
866
867
868
869
870
871
872
873
874
	WCHAR *lfnbuf,		/* Pointer to the Unicode-LFN buffer */
	BYTE *dir			/* Pointer to the directory entry */
)
{
	int i, s;
	WCHAR wc, uc;


	i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;	/* Offset in the LFN buffer */

	s = 0; wc = 1;
	do {
875
		uc = LD_WORD(dir+LfnOfs[s]);		/* Pick an LFN character from the entry */
876
		if (wc) {	/* Last char has not been processed */
877
878
			if (i >= _MAX_LFN) return 0;	/* Buffer overflow? */
			lfnbuf[i++] = wc = uc;			/* Store it */
879
		} else {
880
			if (uc != 0xFFFF) return 0;		/* Check filler */
881
882
883
884
		}
	} while (++s < 13);						/* Read all character in the entry */

	if (dir[LDIR_Ord] & 0x40) {				/* Put terminator if it is the last LFN part */
885
		if (i >= _MAX_LFN) return 0;		/* Buffer overflow? */
886
887
888
		lfnbuf[i] = 0;
	}

889
	return 1;
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
}


#if !_FS_READONLY
static
void fit_lfn (
	const WCHAR *lfnbuf,	/* Pointer to the LFN buffer */
	BYTE *dir,				/* Pointer to the directory entry */
	BYTE ord,				/* LFN order (1-20) */
	BYTE sum				/* SFN sum */
)
{
	int i, s;
	WCHAR wc;


	dir[LDIR_Chksum] = sum;			/* Set check sum */
	dir[LDIR_Attr] = AM_LFN;		/* Set attribute. LFN entry */
	dir[LDIR_Type] = 0;
	ST_WORD(dir+LDIR_FstClusLO, 0);

	i = (ord - 1) * 13;				/* Get offset in the LFN buffer */
	s = wc = 0;
	do {
		if (wc != 0xFFFF) wc = lfnbuf[i++];	/* Get an effective char */
		ST_WORD(dir+LfnOfs[s], wc);	/* Put it */
		if (!wc) wc = 0xFFFF;		/* Padding chars following last char */
	} while (++s < 13);
	if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40;	/* Bottom LFN part is the start of LFN sequence */
	dir[LDIR_Ord] = ord;			/* Set the LFN order */
}

#endif
#endif



/*-----------------------------------------------------------------------*/
/* Create numbered name                                                  */
/*-----------------------------------------------------------------------*/
#if _USE_LFN
void gen_numname (
932
	BYTE *dst,			/* Pointer to generated SFN */
933
934
	const BYTE *src,	/* Pointer to source SFN to be modified */
	const WCHAR *lfn,	/* Pointer to LFN */
935
	WORD seq			/* Sequence number */
936
937
)
{
938
	BYTE ns[8], c;
939
940
941
942
943
	int i, j;


	mem_cpy(dst, src, 11);

944
945
	if (seq > 5) {	/* On many collisions, generate a hash number instead of sequential number */
		do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn);
946
947
948
949
950
	}

	/* itoa */
	i = 7;
	do {
951
952
953
954
955
		c = (seq % 16) + '0';
		if (c > '9') c += 7;
		ns[i--] = c;
		seq /= 16;
	} while (seq);
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	ns[i] = '~';

	/* Append the number */
	for (j = 0; j < i && dst[j] != ' '; j++) {
		if (IsDBCS1(dst[j])) {
			if (j == i - 1) break;
			j++;
		}
	}
	do {
		dst[j++] = (i < 8) ? ns[i++] : ' ';
	} while (j < 8);
}
#endif




/*-----------------------------------------------------------------------*/
/* Calculate sum of an SFN                                               */
/*-----------------------------------------------------------------------*/
#if _USE_LFN
static
BYTE sum_sfn (
	const BYTE *dir		/* Ptr to directory entry */
)
{
	BYTE sum = 0;
	int n = 11;

	do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
	return sum;
}
#endif




/*-----------------------------------------------------------------------*/
/* Directory handling - Find an object in the directory                  */
/*-----------------------------------------------------------------------*/

static
FRESULT dir_find (
	DIR *dj			/* Pointer to the directory object linked to the file name */
For faster browsing, not all history is shown. View entire blame