From 18dcc96bfae81fad5503a09ab2595258119699cf Mon Sep 17 00:00:00 2001 From: savelij13 Date: Thu, 11 Sep 2025 10:30:37 +0300 Subject: [PATCH] fatfs v0.13a October 14, 2017: - Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) - Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). - Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). - Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) - Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) - Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) --- documents/00index_e.html | 13 +- documents/doc/appnote.html | 30 +- documents/doc/chdir.html | 4 +- documents/doc/chdrive.html | 4 +- documents/doc/chmod.html | 4 +- documents/doc/close.html | 2 +- documents/doc/closedir.html | 4 +- documents/doc/config.html | 47 +- documents/doc/dinit.html | 2 +- documents/doc/dioctl.html | 10 +- documents/doc/dread.html | 6 +- documents/doc/dstat.html | 6 +- documents/doc/dwrite.html | 4 +- documents/doc/eof.html | 2 +- documents/doc/error.html | 2 +- documents/doc/expand.html | 4 +- documents/doc/fattime.html | 2 +- documents/doc/fdisk.html | 9 +- documents/doc/filename.html | 23 +- documents/doc/findfirst.html | 4 +- documents/doc/findnext.html | 2 +- documents/doc/forward.html | 4 +- documents/doc/getcwd.html | 6 +- documents/doc/getfree.html | 8 +- documents/doc/getlabel.html | 16 +- documents/doc/gets.html | 8 +- documents/doc/lseek.html | 6 +- documents/doc/mkdir.html | 4 +- documents/doc/mkfs.html | 19 +- documents/doc/mount.html | 4 +- documents/doc/open.html | 4 +- documents/doc/opendir.html | 4 +- documents/doc/printf.html | 31 +- documents/doc/putc.html | 10 +- documents/doc/puts.html | 13 +- documents/doc/rc.html | 13 +- documents/doc/read.html | 2 +- documents/doc/readdir.html | 19 +- documents/doc/rename.html | 4 +- documents/doc/sdir.html | 4 +- documents/doc/setcp.html | 8 +- documents/doc/setlabel.html | 6 +- documents/doc/sfatfs.html | 8 +- documents/doc/sfile.html | 4 +- documents/doc/sfileinfo.html | 10 +- documents/doc/size.html | 2 +- documents/doc/stat.html | 4 +- documents/doc/sync.html | 4 +- documents/doc/tell.html | 2 +- documents/doc/truncate.html | 4 +- documents/doc/unlink.html | 4 +- documents/doc/utime.html | 4 +- documents/doc/write.html | 4 +- documents/updates.txt | 8 + source/00history.txt | 12 + source/00readme.txt | 2 +- source/ff.c | 1287 +++++++++++++++++++++------------- source/ff.h | 36 +- source/ffconf.h | 48 +- source/ffsystem.c | 6 +- source/ffunicode.c | 190 ++--- 61 files changed, 1238 insertions(+), 778 deletions(-) diff --git a/documents/00index_e.html b/documents/00index_e.html index 28bf432..bcc499e 100644 --- a/documents/00index_e.html +++ b/documents/00index_e.html @@ -24,12 +24,12 @@
  • Very small footprint for program code and work area.
  • Various configuration options to support for:
  • @@ -119,7 +119,7 @@

    Resources

    The FatFs module is a free software opened for education, research and development. You can use, modify and/or redistribute it for personal projects or commercial products without any restriction under your responsibility. For further information, refer to the application note.

    diff --git a/documents/doc/appnote.html b/documents/doc/appnote.html index 5be9d23..4955c14 100644 --- a/documents/doc/appnote.html +++ b/documents/doc/appnote.html @@ -1,7 +1,7 @@ - + @@ -31,7 +31,7 @@

    How to Port

    -

    Basic considerations

    +

    Basic Considerations

    The FatFs module is assuming following conditions on portability.

    -

    System organizations

    +

    System Organizations

    The dependency diagram shown below is a typical but not specific configuration of the embedded system with FatFs module.

    dependency diagram

    (a) If a working disk module with FatFs disk interface is provided, nothing else will be needed. (b) To attach existing disk drivers with different interface, glue functions are needed to translate the interfaces between FatFs and the drivers.

    functional diagram

    -

    Required functions

    +

    Required Functions

    You need to provide only low level disk I/O functions required by FatFs module and nothing else. If a working disk module for the target system is already provided, you need to write only glue functions to attach it to the FatFs module. If not, you need to port another disk I/O module or write it from scratch. Most of defined functions are not that always required. For example, any write function is not required at read-only configuration. Following table shows which function is required depends on the configuration options.

    @@ -81,17 +81,17 @@ The FatFs module assumes that size of char/short/long - - - - + + + +
    FunctionRequired whenNote
    ARM7
    32bit
    ARM7
    Thumb
    CM3
    Thumb-2
    AVRH8/300HPIC24RL78V850ESSH-2ARX600IA-32
    CompilerGCCGCCGCCGCCCH38C30CC78K0RCA850SHCRXCMSC
    text (Full, R/W)10.3k6.7k6.3k12.4k 9.9k11.4k13.1k8.7k9.0k6.5k8.6k
    text (Min, R/W) 6.9k4.7k4.4k 8.4k 6.9k 8.0k 9.4k6.2k6.2k4.6k6.1k
    text (Full, R/O) 4.7k3.1k2.8k 5.7k 4.7k 5.4k 6.4k4.2k4.0k3.1k4.1k
    text (Min, R/O) 3.6k2.4k2.2k 4.4k 3.6k 4.2k 5.0k3.3k3.1k2.4k3.2k
    text (Full, R/W)10.4k6.7k6.3k12.4k 9.9k11.2k13.0k8.7k9.0k6.5k8.9k
    text (Min, R/W) 7.0k4.7k4.4k 8.4k 6.9k 7.8k 9.4k6.0k6.2k4.6k6.3k
    text (Full, R/O) 4.8k3.1k2.8k 5.7k 4.7k 5.3k 6.4k4.2k4.0k3.1k4.2k
    text (Min, R/O) 3.6k2.4k2.2k 4.4k 3.6k 4.1k 5.0k3.3k3.1k2.4k3.3k
    bssV*4 + 2V*4 + 2V*4 + 2V*2 + 2V*4 + 2V*2 + 2V*2 + 2V*4 + 2V*4 + 2V*4 + 2V*4 + 2
    Work area
    (FF_FS_TINY == 0)
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    V*560
    + F*546
    V*560
    + F*546
    V*560
    + F*546
    V*560
    + F*546
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    Work area
    (FF_FS_TINY == 1)
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40
    V*560
    + F*34
    V*560
    + F*34
    V*560
    + F*34
    V*560
    + F*34
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40

    These are the memory usage on some target systems with following condition. The memory sizes are in unit of byte, V denotes option FF_VOLUMES and F denotes number of open files. All samples here are optimezed in code size.

    -FatFs R0.13 options:
    +FatFs R0.13a options:
     FF_FS_READONLY   0 (R/W) or 1 (R/O)
     FF_FS_MINIMIZE   0 (Full, with all basic functions) or 3 (Min, with fully minimized)
     FF_FS_TINY       0 (Default) or 1 (Tiny file object)
    @@ -155,20 +155,20 @@ And other options are left unchanged from original setting.
     950 (Traditional Chinese)+111k
     0 (All code pages)+486k
     
    -

    When the LFN is enabled, the module size will be increased depends on the configured code page. Right table shows increment of code size by LFN function at different code pages. Especially, in the CJK region, tens of thousands of characters are being used. Unfortunately, it requires a huge OEM-Unicode bidirectional conversion table and the module size will be drastically increased as shown in the table. As the result, the FatFs with LFN enebled with those code pages will not able to be ported on the most 8-bit MCU systems.

    +

    When the LFN is enabled, the module size will be increased depends on the configured code page. Right table shows increment of code size by LFN function at different code pages. Especially, in the CJK region, tens of thousands of characters are being used. Unfortunately, it requires a huge OEM-Unicode bidirectional conversion table and the module size will be drastically increased as shown in the table. As the result, the FatFs with LFN enebled with those code pages will not able to be ported on the most 8-bit MCU systems. If you can discard ANSI/OEM code API and compatibility with non-ASCII SFN, you will able to configure FatFs for Unicode API with any SBCS.

    There ware some restrictions on using LFN for open source project because the support for LFN on the FAT volume was a patent of Microsoft Corporation. The related patents have expired and using the LFN function have got free for any projects.

    Unicode API

    -

    By default, FatFs uses ANSI/OEM code set on the API even at LFN configuration. FatFs can also switch the character encoding on the API to Unicode by configuration option FF_LFN_UNICODE. This means that FatFs supports the full featured LFN specification. The data type TCHAR specifies path name strings on the API is an alias of either char(ANSI/OEM) or WCHAR(UTF-16) depends on that option. For more information, refer to the description in the file name.

    -

    Note that code page setting, FF_CODE_PAGE, has actually no meaning for the path names at the Unicode API. However it still affects code conversion of string I/O functions at FF_STRF_ENCODE = 0 and backward compatibility with non-LFN systems, so that code page needs to be set properly when it is considered a problem.

    +

    By default, FatFs uses ANSI/OEM code set on the API even at LFN configuration. FatFs can also switch the character encoding on the API to Unicode by configuration option FF_LFN_UNICODE. This means that FatFs supports the full featured LFN specification. The data type TCHAR specifies path name strings on the API is an alias of either char(ANSI/OEM or UTF-8) or WCHAR(UTF-16) depends on that option. For more information, refer to the description in the file name.

    +

    Note that code page setting, FF_CODE_PAGE, has actually no meaning for the path names at the Unicode API. However it still affects code conversion of string I/O functions at FF_STRF_ENCODE == 0 and backward compatibility with non-LFN systems, so that code page needs to be set properly when it is considered a problem.

    exFAT Filesystem

    -

    The exFAT (Microsoft's Extended File Allocation Table) filesystem is a succession of the FAT filesystem which has been widely used in embedded systems, consumer devices and portable storage media. It is adopted by SDA (SD Association) as a recommended filesystem for high capacity SD cards larger than 32 GB and they are being shipped with this format, so that the exFAT became one of the standard filesystems for removable media as well as FAT. The exFAT filesystem allows the file size beyond the 4 GB limit what FAT filesystem allows upto and some filesystem overhead, especially cluster allocation delay, are reduced as well. This feature improves the write throughput to the file.

    -

    Note that the exFAT is a patent of Microsoft Corporation. The exFAT function of FatFs is an implementation based on US. Pat. App. Pub. No. 2009/0164440 A1. FatFs module can swich the support for exFAT on/off by configuration option, FF_FS_EXFAT. When enable the exFAT on the commercial products, a license by Microsoft will be needed depends on the final destination of the products.

    +

    The exFAT (Microsoft's Extended File Allocation Table) filesystem is a succession of the FAT/FAT32 filesystem which has been widely used in embedded systems, consumer devices and portable storage media. It is adopted by SDA (SD Association) as a recommended filesystem for high capacity SD cards larger than 32 GB and they are being shipped with this format, so that the exFAT became one of the standard filesystems for removable media as well as FAT. The exFAT filesystem allows the file size beyond the 4 GB limit what FAT filesystem allows upto and some filesystem overhead, especially cluster allocation delay, are reduced as well. This feature improves the write throughput to the file.

    +

    Note that the exFAT is a patent of Microsoft Corporation. The exFAT function of FatFs is an implementation based on US. Pat. App. Pub. No. 2009/0164440 A1. FatFs module can switch the support for exFAT on/off by configuration option, FF_FS_EXFAT. When enable the exFAT on the commercial products, a license by Microsoft will be needed depends on the final destination of the products.

    Remark: Enabling exFAT discards ANSI C (C89) compatibility because of need for 64-bit integer type.

    @@ -257,7 +257,7 @@ Figure 5. Minimized critical section

    FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that included in the source files.

     /*----------------------------------------------------------------------------/
    -/  FatFs - Generic FAT Filesystem Module  Rx.xx                              /
    +/  FatFs - Generic FAT Filesystem Module  Rx.xx                               /
     /-----------------------------------------------------------------------------/
     /
     / Copyright (C) 20xx, ChaN, all right reserved.
    diff --git a/documents/doc/chdir.html b/documents/doc/chdir.html
    index dcee833..ef84280 100644
    --- a/documents/doc/chdir.html
    +++ b/documents/doc/chdir.html
    @@ -1,7 +1,7 @@
     
     
     
    -
    +
     
     
     
    @@ -56,7 +56,7 @@ FRESULT f_chdir (
     
     

    QuickInfo

    -

    Available when FF_FS_RPATH >= 1.

    +

    Available when FF_FS_RPATH >= 1.

    diff --git a/documents/doc/chdrive.html b/documents/doc/chdrive.html index d3bf71f..fb36087 100644 --- a/documents/doc/chdrive.html +++ b/documents/doc/chdrive.html @@ -1,7 +1,7 @@ - + @@ -46,7 +46,7 @@ FRESULT f_chdrive (

    QuickInfo

    -

    Available when FF_FS_RPATH >= 1 and FF_VOLUMES >= 2.

    +

    Available when FF_FS_RPATH >= 1 and FF_VOLUMES >= 2.

    diff --git a/documents/doc/chmod.html b/documents/doc/chmod.html index cb4e555..0da34b2 100644 --- a/documents/doc/chmod.html +++ b/documents/doc/chmod.html @@ -1,7 +1,7 @@ - + @@ -72,7 +72,7 @@ FRESULT f_chmod (

    QuickInfo

    -

    Available when FF_FS_READONLY == 0 and FF_USE_CHMOD == 1.

    +

    Available when FF_FS_READONLY == 0 and FF_USE_CHMOD == 1.

    diff --git a/documents/doc/close.html b/documents/doc/close.html index b5f43e3..4f709af 100644 --- a/documents/doc/close.html +++ b/documents/doc/close.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/closedir.html b/documents/doc/closedir.html index d32d9c5..79ce1ad 100644 --- a/documents/doc/closedir.html +++ b/documents/doc/closedir.html @@ -1,7 +1,7 @@ - + @@ -50,7 +50,7 @@ FRESULT f_closedir (

    QuickInfo

    -

    Available when FF_FS_MINIMIZE <= 1.

    +

    Available when FF_FS_MINIMIZE <= 1.

    diff --git a/documents/doc/config.html b/documents/doc/config.html index d3d1b86..b62e287 100644 --- a/documents/doc/config.html +++ b/documents/doc/config.html @@ -33,6 +33,7 @@
  • FF_USE_LFN
  • FF_MAX_LFN
  • FF_LFN_UNICODE
  • +
  • FF_LFN_BUF, FF_SFN_BUF
  • FF_STRF_ENCODE
  • FF_FS_RPATH
  • @@ -143,7 +144,7 @@

    FF_USE_LFN

    -

    This option switches the support for long file name (LFN). When enable the LFN, Unicode support functions ffunicode.c need to be added to the project. The working buffer for LFN occupies (FF_MAX_LFN + 1) * 2 bytes and additional ((FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT enabled. When use stack for the working buffer, take care on stack overflow. When use heap memory for the working buffer, memory management functions, ff_memalloc and ff_memfree, need to be added to the project.

    +

    This option switches the support for long file name (LFN). When enable the LFN, Unicode support module ffunicode.c need to be added to the project. When use stack for the working buffer, take care on stack overflow. When use heap memory for the working buffer, memory management functions (ff_memalloc and ff_memfree) need to be added to the project.

    @@ -153,19 +154,37 @@
    ValueDescription
    0Disable LFN. Path name in only 8.3 format can be used.

    FF_MAX_LFN

    -

    This option defines the size of LFN working buffer from 12 to 255 in unit of character. This option has no effect when LFN is disabled.

    +

    LFN function requiers certain internal working buffer. This option defines size of the buffer and the value can be in range of 12 to 255 in UTF-16 encoding unit of the LFN. The buffer occupies (FF_MAX_LFN + 1) * 2 bytes and additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. It is recommended to be set 255 to fully support the LFN specification. This option has no effect when LFN is disabled.

    FF_LFN_UNICODE

    -

    This option switches character encoding on the API, ANSI/OEM (0) or Unicode in UTF-16 (1). To use Unicode string for the path name, enable LFN and set this option to 1. This option also affects behavior of string I/O functions. When LFN is disabled, this option has no effect. For more information, read here.

    +

    This option switches character encoding on the API.

    + + + + + +
    ValueCharacter EncodingTCHAR
    0ANSI/OEM in current CPchar
    1Unicode in UTF-16WCHAR
    2Unicode in UTF-8char
    +

    This option also affects behavior of string I/O functions. When LFN is disabled, this option has no effect. For more information, read here.

    + +

    FF_LFN_BUF, FF_SFN_BUF

    +

    This set of options defines size of file name members, fname[] and altname[], in the FILINFO structure which is used to read out the directory items. These values should be suffcient for the file names to read. The maximum possible length of the read file name depends on the character encoding on the API as follows:

    + + + + + + +
    EncodingLFN lengthSFN length
    ANSI/OEM at SBCS255 items12 items
    ANSI/OEM at DBCS510 items12 items
    Unicode in UTF-16255 items12 items
    Unicode in UTF-8765 items34 items
    +

    If the size of name member is insufficient for the LFN, the item is treated as without LFN. When LFN is not enabled, these options have no effect.

    FF_STRF_ENCODE

    -

    When Unicode API is selected by FF_LFN_UNICODE = 1, string I/O functions, f_gets, f_putc, f_puts and f_printf convert the character encodins in it. This option defines the assumption of character encoding on the file to be read/written via those functions. When Unicode API is not selected, the string functions work without any conversion and this option has no effect.

    - +

    When character encoding on the API is Unicode (FF_LFN_UNICODE >= 1), string I/O functions, f_gets, f_putc, f_puts and f_printf, convert the character encodins in it. This option defines the assumption of character encoding on the file to be read/written via those functions. When FF_LFN_UNICODE == 0, the string functions work without any code conversion and this option has no effect.

    +
    - - - - + + + +
    ValueDescription
    0ANSI/OEM
    1UTF-16LE
    2UTF-16BE
    3UTF-8
    0ANSI/OEM in current CP
    1Unicode in UTF-16LE
    2Unicode in UTF-16BE
    3Unicode in UTF-8

    FF_FS_RPATH

    @@ -221,13 +240,13 @@

    Normal (0) or Tiny (1). At the tiny configuration, size of the file object FIL is reduced FF_MAX_SS bytes. Instead of private data buffer eliminated from the file object, common sector buffer in the filesystem object FATFS is used for the file data transfer.

    FF_FS_EXFAT

    -

    This option switches support for the exFAT filesystem in addition to the FAT/FAT32 filesystem, Enabled(1) or Disabled(0). To enable this feature, also LFN must be enabled and configureing FF_LFN_UNICODE = 1 and FF_MAX_LFN = 255 is recommended for full-featured exFAT function. Note that enabling exFAT discards ANSI C (C89) compatibility because of need for 64-bit integer type.

    +

    This option switches support for the exFAT filesystem in addition to the FAT/FAT32 filesystem, Enabled (1) or Disabled (0). To enable exFAT, also LFN must be enabled and configureing FF_LFN_UNICODE >= 1 and FF_MAX_LFN == 255 is recommended for full-featured exFAT function. Note that enabling exFAT discards ANSI C (C89) compatibility because of need for 64-bit integer type.

    FF_FS_NORTC

    -

    Use RTC (0) or Do not use RTC (1). This option controls timestamp function. If the system does not have an RTC function or valid timestamp is not needed, set FF_FS_NORTC to 1 to disable the timestamp function. Any object modified by FatFs will have a fixed timestamp defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR. To use the timestamp function, set FF_FS_NORTC = 0 and add get_fattime function to the project to get the current time form real-time clock. This option has no effect at read-only configuration.

    +

    Use RTC (0) or Do not use RTC (1). This option controls timestamp function. If the system does not have any RTC function or valid timestamp is not needed, set FF_FS_NORTC to 1 to disable the timestamp function. Every objects modified by FatFs will have a fixed timestamp defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR. To use the timestamp function, set FF_FS_NORTC == 0 and add get_fattime function to the project to get current time form the RTC. This option has no effect at read-only configuration.

    FF_NORTC_MON, FF_NORTC_MDAY, FF_NORTC_YEAR

    -

    This set of options defines the time to be used at no RTC systems. This option has no effect at read-only configuration or FF_FS_NORTC = 0.

    +

    This set of options defines the time to be used at no RTC systems. This option has no effect at read-only configuration or FF_FS_NORTC == 0.

    FF_FS_LOCK

    This option switches file lock function to control duplicated file open and illegal operations to open objects. Note that the file lock function is independent of re-entrancy. This option must be 0 at read-only configuration.

    @@ -241,10 +260,10 @@

    Disable (0) or Enable (1). This option switches the re-entrancy (thread safe) of the FatFs module itself. Note that file/directory access to the different volume is always re-entrant and it can work simultaneously regardless of this option but volume control functions. f_mount, f_mkfs and f_fdisk, are always not re-entrant. Only file/directory access to the same volume, in other words, exclusive use of each filesystem object, is under control of this function. To enable this feature, also user provided synchronization handlers, ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj, need to be added to the project. Sample code is available in ffsystem.c.

    FF_FS_TIMEOUT

    -

    Number of time ticks to abort the file function with FR_TIMEOUT when wait time is too long. This option has no effect when FF_FS_REENTRANT = 0.

    +

    Number of time ticks to abort the file function with FR_TIMEOUT when wait time is too long. This option has no effect when FF_FS_REENTRANT == 0.

    FF_SYNC_t

    -

    This option defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, SemaphoreHandle_t and etc. A header file for O/S definitions needs to be included somewhere in the scope of ff.c. This option has no effect when FF_FS_REENTRANT = 0.

    +

    This option defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, SemaphoreHandle_t and etc. A header file for O/S definitions needs to be included somewhere in the scope of ff.c. This option has no effect when FF_FS_REENTRANT == 0.

    diff --git a/documents/doc/dinit.html b/documents/doc/dinit.html index 86f445b..3e3a3cb 100644 --- a/documents/doc/dinit.html +++ b/documents/doc/dinit.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/dioctl.html b/documents/doc/dioctl.html index 1ea3d76..9522b8d 100644 --- a/documents/doc/dioctl.html +++ b/documents/doc/dioctl.html @@ -1,7 +1,7 @@ - + @@ -31,7 +31,7 @@ DRESULT disk_ioctl (
    cmd
    Command code.
    buff
    -
    Pointer to the parameter depends on the command code. Do not care if no parameter to be passed.
    +
    Pointer to the parameter depends on the command code. Do not care if the command has no parameter to be passed.
    @@ -60,11 +60,11 @@ DRESULT disk_ioctl ( CTRL_SYNCMake sure that the device has finished pending write process. If the disk I/O module has a write back cache, the dirty buffers must be written back to the media immediately. Nothing to do for this command if each write operation to the media is completed within the disk_write function. GET_SECTOR_COUNTReturns number of available sectors on the drive into the DWORD variable pointed by buff. This command is used in only f_mkfs and f_fdisk function to determine the volume/partition size to be created. Required at FF_USE_MKFS == 1 or FF_MULTI_PARTITION == 1. GET_SECTOR_SIZEReturns sector size of the device into the WORD variable pointed by buff. Valid return values for this command are 512, 1024, 2048 and 4096. This command is required only if FF_MAX_SS > FF_MIN_SS. When FF_MAX_SS == FF_MIN_SS, this command is never used and the device must work at that sector size. -GET_BLOCK_SIZEReturns erase block size of the flash memory media in unit of sector into the DWORD variable pointed by buff. The allowable value is from 1 to 32768 in power of 2. Return 1 if the erase block size is unknown or non flash memory media. This command is used by only f_mkfs function and it attempts to align data area on the erase block boundary. Required at FF_USE_MKFS == 1. +GET_BLOCK_SIZEReturns erase block size of the flash memory media in unit of sector into the DWORD variable pointed by buff. The allowable value is 1 to 32768 in power of 2. Return 1 if the erase block size is unknown or non flash memory media. This command is used by only f_mkfs function and it attempts to align data area on the erase block boundary. Required at FF_USE_MKFS == 1. CTRL_TRIMInforms the device the data on the block of sectors is no longer needed and it can be erased. The sector block is specified by a DWORD array {<start sector>, <end sector>} pointed by buff. This is an identical command to Trim of ATA device. Nothing to do for this command if this funcion is not supported or not a flash memory device. FatFs does not check the result code and the file function is not affected even if the sector block was not erased well. This command is called on remove a cluster chain and in the f_mkfs function. Required at FF_USE_TRIM == 1. -

    FatFs never uses any device dependent command nor user defined command. Following table shows an example of non-standard commands may be useful for some applications.

    +

    FatFs never uses any device dependent command nor user defined command. Following table shows an example of non-standard commands which may be useful for some applications.

    @@ -91,7 +91,7 @@ DRESULT disk_ioctl (

    QuickInfo

    -

    The disk_ioctl function is not needed when FF_FS_READONLY == 1 and FF_MAX_SS == FF_MIN_SS.

    +

    The disk_ioctl function is not needed when FF_FS_READONLY == 1 and FF_MAX_SS == FF_MIN_SS.

    diff --git a/documents/doc/dread.html b/documents/doc/dread.html index 29ce549..685b84a 100644 --- a/documents/doc/dread.html +++ b/documents/doc/dread.html @@ -1,7 +1,7 @@ - + @@ -56,12 +56,12 @@ DRESULT disk_read (

    Description

    -

    Read/write operation to the generic storage devices, such as memory card, hadddisk and optical disk, is done in unit of block of data bytes called sector. FatFs supports the sector size in range of from 512 to 4096 bytes. When FatFs is configured for fixed sector size (FF_MIN_SS == MAX_SS, this is the most case), the read/write function must work at that sector size. When FatFs is configured for variable sector size (FF_MIN_SS != MAX_SS), the sector size of medium is inquired with disk_ioctl function immediately following disk_initialize function.

    +

    Read/write operation to the generic storage devices, such as memory card, hadddisk and optical disk, is done in unit of block of data bytes called sector. FatFs supports the sector size in range of from 512 to 4096 bytes. When FatFs is configured for fixed sector size (FF_MIN_SS == FF_MAX_SS, this is the most case), the read/write function must work at that sector size. When FatFs is configured for variable sector size (FF_MIN_SS != FF_MAX_SS), the sector size of medium is inquired with disk_ioctl function immediately following disk_initialize function.

    The memory address specified by buff is not that always aligned to word boundary because the argument is defined as BYTE*. The unaligned read/write request can occure at direct transfer. If the bus architecture, especially DMA controller, does not allow unaligned memory access, it should be solved in this function. There are some workarounds described below to avoid this issue.

    • Convert word transfer to byte transfer in this function if needed. - Recommended.
    • On the f_read() calls, avoid long read request that includes a whole of sector. - Any direct transfer never occures.
    • -
    • On the f_read(fp, data, btw, bw) calls, make sure that (((UINT)data & 3) == (f_tell(fp) & 3)) is true. - Word alignment of buff is guaranteed.
    • +
    • On the f_read(fp, dat, btw, bw) calls, make sure that (((UINT)dat & 3) == (f_tell(fp) & 3)) is true. - Word alignment of buff is guaranteed no matter dat is not word aligned.

    Generally, a multiple sector read request must not be split into single sector transactions to the storage device, or read throughput gets worse.

    diff --git a/documents/doc/dstat.html b/documents/doc/dstat.html index 2f3fe1a..c96720a 100644 --- a/documents/doc/dstat.html +++ b/documents/doc/dstat.html @@ -1,7 +1,7 @@ - + @@ -35,11 +35,11 @@ DSTATUS disk_status (

    The current drive status is returned in combination of status flags described below. FatFs refers only STA_NOINIT and STA_PROTECT.

    STA_NOINIT
    -
    Indicates that the device has not been initialized and not ready to work. This flag is set on system reset, media removal or failure of disk_initialize function. It is cleared on disk_initialize function succeeded. Any media change that occurs asynchronously must be captured and reflect it to the status flags, or auto-mount function will not work correctly. If the system does not support media change detection, application program needs to explicitly re-mount the volume with f_mount function after each media change.
    +
    Indicates that the device has not been initialized and not ready to work. This flag is set on system reset, media removal or failure of disk_initialize function. It is cleared on disk_initialize function succeeded. Any media change that occurs asynchronously must be captured and reflect it to the status flags, or auto-mount function will not work correctly. If the system does not support media change detection, application program needs to explicitly re-mount the volume with f_mount function after each media change.
    STA_NODISK
    Indicates that no medium in the drive. This is always cleared at fixed disk drive. Note that FatFs does not refer this flag.
    STA_PROTECT
    -
    Indicates that the medium is write protected. This is always cleared at the drives without write protect function. Not valid if no medium in the drive.
    +
    Indicates that the medium is write protected. This is always cleared at the drives without write protect function. Not valid if STA_NODISK is set.
    diff --git a/documents/doc/dwrite.html b/documents/doc/dwrite.html index 7426af1..e840ad3 100644 --- a/documents/doc/dwrite.html +++ b/documents/doc/dwrite.html @@ -1,7 +1,7 @@ - + @@ -67,7 +67,7 @@ DRESULT disk_write (

    QuickInfo

    -

    This function is not needed when FF_FS_READONLY == 1.

    +

    This function is not needed when FF_FS_READONLY == 1.

    diff --git a/documents/doc/eof.html b/documents/doc/eof.html index e2c3bff..3d4fab2 100644 --- a/documents/doc/eof.html +++ b/documents/doc/eof.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/error.html b/documents/doc/error.html index 8e082d9..685b7f2 100644 --- a/documents/doc/error.html +++ b/documents/doc/error.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/expand.html b/documents/doc/expand.html index 685f7eb..9517609 100644 --- a/documents/doc/expand.html +++ b/documents/doc/expand.html @@ -1,7 +1,7 @@ - + @@ -57,7 +57,7 @@ FRESULT f_expand (
  • No free contiguous space was found.
  • Size of the file was not zero.
  • The file has been opened in read-only mode.
  • -
  • Not allowable file size. (>= 4GiB on FAT volume)
  • +
  • Not allowable file size. (>= 4 GB on FAT volume)
  • When opt is 0, the function finds a contiguous data area and set it as suggested point for next allocation instead of allocating it to the file. The next cluster allocation is started at top of the contiguous area found by this function. Thus the write file is guaranteed be contiguous and no allocation delay until the size reaches that size at least unless any other changes to the volume is performed.

    The contiguous file would have an advantage at time-critical read/write operations. It eliminates some overheads in the filesystem and the storage media caused by random access due to fragmented file data. Especially FAT access for the contiguous file on the exFAT volume is completely eliminated and storage media will be accessed sequentially.

    diff --git a/documents/doc/fattime.html b/documents/doc/fattime.html index 0657825..27ec6e2 100644 --- a/documents/doc/fattime.html +++ b/documents/doc/fattime.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/fdisk.html b/documents/doc/fdisk.html index 08231e2..d58db7e 100644 --- a/documents/doc/fdisk.html +++ b/documents/doc/fdisk.html @@ -1,7 +1,7 @@ - + @@ -31,7 +31,7 @@ FRESULT f_fdisk (
    szt
    Pointer to the first item of the partition map table.
    work
    -
    Pointer to the function work area. The size must be at least FF_MAX_SS bytes.
    +
    Pointer to the function work area. The size must be at least FF_MAX_SS bytes. When a null pointer is given, the function allocates a memory block for the working buffer (at only FF_USE_LFN == 3).
    @@ -42,7 +42,8 @@ FRESULT f_fdisk ( FR_DISK_ERR, FR_NOT_READY, FR_WRITE_PROTECTED, -FR_INVALID_PARAMETER +FR_INVALID_PARAMETER, +FR_NOT_ENOUGH_CORE

    @@ -53,7 +54,7 @@ FRESULT f_fdisk (

    QuickInfo

    -

    Available when FF_FS_READOLNY == 0, FF_USE_MKFS == 1 and FF_MULTI_PARTITION == 1.

    +

    Available when FF_FS_READOLNY == 0, FF_USE_MKFS == 1 and FF_MULTI_PARTITION == 1.

    diff --git a/documents/doc/filename.html b/documents/doc/filename.html index d731dd3..471a8c6 100644 --- a/documents/doc/filename.html +++ b/documents/doc/filename.html @@ -16,8 +16,8 @@

    Format of the path names

    The format of path name on the FatFs module is similer to the filename specs of DOS/Windos as follows:

    [drive#:][/]directory/file
    -

    The FatFs module supports long file name (LFN) and 8.3 format file name (SFN). The LFN can be used when (FF_USE_LFN != 0). The sub directories are separated with a \ or / in the same way as DOS/Windows API. Duplicated separators are skipped and ignored. Only a difference is that the logical drive is specified in a numeral with a colon. When drive prefix is omitted, the drive number is assumed as default drive (drive 0 or current drive).

    -

    Control characters ('\0' to '\x1F') are recognized as end of the path name. Leading/embedded spaces in the path name are valid as a part of the name at LFN configuration but the space is recognized as end of the path name at non-LFN configuration. Trailing spaces and dots are ignored at both configurations.

    +

    The FatFs module supports long file name (LFN) and 8.3 format file name (SFN). The LFN can be used when FF_USE_LFN != 0. The sub directories are separated with a \ or / in the same way as DOS/Windows API. Duplicated separators are skipped and ignored. Only a difference is that the logical drive is specified in a numeral with a colon. When drive prefix is omitted, the drive number is assumed as default drive (drive 0 or current drive).

    +

    Control characters (\0 to \x1F) are recognized as end of the path name. Leading/embedded spaces in the path name are valid as a part of the name at LFN configuration but the space is recognized as end of the path name at non-LFN configuration. Trailing spaces and dots are ignored at both configurations.

    In default configuration (FF_FS_RPATH == 0), it does not have a concept of current directory like OS oriented filesystem. Every object on the volume is always specified in full path name that followed from the root directory. Dot directory names (".", "..") are not allowed. Heading separator is ignored and it can be exist or omitted. The default drive is fixed to drive 0.

    When relative path is enabled (FF_FS_RPATH >= 1), specified path is followed from the root directory if a heading separator is exist. If not, it is followed from the current directory of the drive set by f_chdir function. Dot names are also allowed for the path names. The default drive is the current drive set by f_chdrive function.

    Example of optional ioctl command
    CommandDescription
    @@ -41,28 +41,29 @@

    Legal Characters and Case Sensitivity

    -

    On the FAT filesystem, legal characters for object name (file/directory name) are, 0-9 A-Z ! # $ % & ' ( ) - @ ^ _ ` { } ~ and extended characters (\x80-\xFF). Under LFN supported system, also + , ; = [ ] and space are legal for the object name and the white spaces and periods can be placed anywhere in the path name except for end of the object name.

    -

    FAT filesystem is case-insensitive to the object names on the volume. All object names are compared in case-insensitive. For example, these three names, file.txt, File.Txt and FILE.TXT, are identical. This is applied to also extended charactres. When an object is created on the FAT volume, upper converted name is recorded to the SFN entry, and the raw name is recorded to the LFN entry.

    +

    On the FAT filesystem, legal characters for object name (file/directory name) are, 0-9 A-Z ! # $ % & ' ( ) - @ ^ _ ` { } ~ and extended characters (\x80-\xFF). Under LFN supported system, also + , ; = [ ] and space are legal for the object name and the white spaces and dots can be placed anywhere in the path name except for end of the object name.

    +

    FAT filesystem is case-insensitive to the object names on the volume. All object names on the FAT volume are compared in case-insensitive. For example, these three names, file.txt, File.Txt and FILE.TXT, are identical. This is applied to also extended charactres. When an object is created on the FAT volume, upper converted name is recorded to the SFN entry, and the raw name is recorded to the LFN entry.

    As for the DBCS language MS-DOS, it was case-sensitive to the extended characters. To follow this specification, FatFs works with case-sensitive to the extended characters at only non-LFN with DBCS configuration (DOS/DBCS specs). But at LFN configuration, FatFs works with case-insensitive to all characters (WindowsNT specs). This can cause a problem on compatibility with Windows system when an object with extended characters is created on the volume at non-LFN and DBCS configuration; therfore the object names with DBCS extended characters should not be used on the FAT volume shared by those systems.

    Unicode API

    -

    The path names are input/output in either ANSI/OEM code or Unicode depends on the configuration options. The type of arguments which specify the path names are defined as TCHAR. It is an alias of char by default. The code set used to the path name string is ANSI/OEM specifid by FF_CODE_PAGE. When FF_LFN_UNICODE is set to 1, the type of the TCHAR is switched to WCHAR to support Unicode (UTF-16 encoding). In this case, the full-featured LFN specification is supported and the Unicode specific characters, such as ✝☪✡☸☭, can also be used for the path name. It also affects data types and encoding of the string I/O functions. To define literal strings, _T(s) and _TEXT(s) macro are available to select either ANSI/OEM or Unicode automatically. The code shown below is an example to define the literal strings.

    +

    The path names are input/output in either ANSI/OEM code or Unicode depends on the configuration options. The type of arguments which specifies the path names is defined as TCHAR. It is an alias of char by default and the code set used for the path name string is ANSI/OEM specifid by FF_CODE_PAGE. When FF_LFN_UNICODE is set to 1, the type of the TCHAR is switched to WCHAR to support UTF-16 encoded Unicode string. When UTF-16 or UTF-8 is specified by this option, the full-featured LFN specification is supported and the Unicode specific characters, such as ✝☪✡☸☭, can also be used for the path name. It also affects data types and encoding of the string I/O functions. To define literal strings, _T(s) and _TEXT(s) macro are available to select either ANSI/OEM or Unicode automatically. The code shown below is an example to define the literal strings.

    - f_open(fp, "filename.txt", FA_READ);      /* ANSI/OEM string */
    - f_open(fp, L"filename.txt", FA_READ);     /* Unicode string */
    - f_open(fp, _T("filename.txt"), FA_READ);  /* Changed by configuration */
    + f_open(fp, "filename.txt", FA_READ);      /* ANSI/OEM string (char) */
    + f_open(fp, L"filename.txt", FA_READ);     /* UTF-16 string (WCHAR) */
    + f_open(fp, u8"filename.txt", FA_READ);    /* UTF-8 string (char) */
    + f_open(fp, _T("filename.txt"), FA_READ);  /* Changed by configuration (TCHAR) */
     

    Volume Management

    FatFs module needs dynamic work area, filesystem object, for each volume (logical drive). It is registered/unregistered to the FatFs module by f_mount function. By default, each logical drive is bound to the physical drive with the same drive number and an FAT volume on the drive is serched by the volume mount process. It reads boot sectors and checks it if it is an FAT boot sector in order of sector 0 as SFD format, 1st partition, 2nd partition, 3rd partition and 4th partition as FDISK format.

    -

    When FF_MULTI_PARTITION == 1 is specified by configuration option, each individual logical drive is bound to the partition on the physical drive specified by volume management table. The volume management table needs to be defined by user to resolve the mappings of logical drives and partitions. Following code is an example of a volume management table.

    +

    When FF_MULTI_PARTITION = 1 is specified by configuration option, each individual logical drive is bound to the partition on the physical drive specified by volume management table. The volume management table needs to be defined by user to resolve the mappings of logical drives and partitions. Following code is an example of a volume management table.

    -Example: 0:-2: are tied to three pri-partitions on the physical drive 0 (fixed drive)
    -         3: is tied to an FAT volume on the physical drive 1 (removable drive)
    +Example: "0:", "1:" and "2:" are tied to three pri-partitions on the physical drive 0 (fixed drive)
    +         "3:" is tied to an FAT volume on the physical drive 1 (removable drive)
     
     PARTITION VolToPart[FF_VOLUMES] = {
         {0, 1},     /* "0:" ==> Physical drive 0, 1st partition */
    diff --git a/documents/doc/findfirst.html b/documents/doc/findfirst.html
    index 1cd2e1e..5e44289 100644
    --- a/documents/doc/findfirst.html
    +++ b/documents/doc/findfirst.html
    @@ -66,14 +66,14 @@ FRESULT f_findfirst (
     
    • "*.*" never matches any name without extension while it matches any name with or without extension at the standard systems.
    • Any pattern terminated with a period never matches any name while it matches any name without extensiton at the standard systems.
    • -
    • DBCS extended characters are compared in case-sensitive at LFN with non-Unicode configuration.
    • +
    • DBCS extended characters are compared in case-sensitive at LFN with ANSI/OEM API.

    QuickInfo

    -

    This is a wrapper function of f_opendir and f_readdir function. Available when FF_USE_FIND >= 1 and FF_FS_MINIMIZE <= 1.

    +

    This is a wrapper function of f_opendir and f_readdir function. Available when FF_USE_FIND >= 1 and FF_FS_MINIMIZE <= 1.

    diff --git a/documents/doc/findnext.html b/documents/doc/findnext.html index 296ff81..c39d3e3 100644 --- a/documents/doc/findnext.html +++ b/documents/doc/findnext.html @@ -55,7 +55,7 @@ FRESULT f_findnext (

    QuickInfo

    -

    This is a wrapper function of f_readdir function. Available when FF_USE_FIND == 1 and FF_FS_MINIMIZE <= 1.

    +

    This is a wrapper function of f_readdir function. Available when FF_USE_FIND == 1 and FF_FS_MINIMIZE <= 1.

    diff --git a/documents/doc/forward.html b/documents/doc/forward.html index 07ef3c5..9331aa2 100644 --- a/documents/doc/forward.html +++ b/documents/doc/forward.html @@ -1,7 +1,7 @@ - + @@ -60,7 +60,7 @@ FRESULT f_forward (

    QuickInfo

    -

    Available when FF_USE_FORWARD == 1.

    +

    Available when FF_USE_FORWARD == 1.

    diff --git a/documents/doc/getcwd.html b/documents/doc/getcwd.html index dfcb96f..56a22e5 100644 --- a/documents/doc/getcwd.html +++ b/documents/doc/getcwd.html @@ -1,7 +1,7 @@ - + @@ -50,14 +50,14 @@ FRESULT f_getcwd (

    Description

    -

    The f_getcwd function retrieves full path name of the current directory of the current drive. When FF_VOLUMES is larger than 1, a logical drive number is added to top of the path name.

    +

    The f_getcwd function retrieves full path name of the current directory of the current drive. When FF_VOLUMES is larger than 1, a logical drive number is added to top of the path name.

    Note: In this revision, this function cannot retrieve the current directory path on the exFAT volume. It always returns the root directory path.

    QuickInfo

    -

    Available when FF_FS_RPATH == 2.

    +

    Available when FF_FS_RPATH == 2.

    diff --git a/documents/doc/getfree.html b/documents/doc/getfree.html index 058c3f0..ade8a71 100644 --- a/documents/doc/getfree.html +++ b/documents/doc/getfree.html @@ -1,7 +1,7 @@ - + @@ -31,7 +31,7 @@ FRESULT f_getfree (
    nclst
    Pointer to the DWORD variable to store number of free clusters.
    fatfs
    -
    Pointer to pointer that to store a pointer to the corresponding file system object.
    +
    Pointer to pointer that to store a pointer to the corresponding filesystem object.
    @@ -53,13 +53,13 @@ FRESULT f_getfree (

    Descriptions

    -

    The f_getfree function gets number of free clusters on the volume. The member csize in the file system object indicates number of sectors per cluster, so that the free space in unit of sector can be calcurated with this information. When FSINFO structure on the FAT32 volume is not in sync, this function can return an incorrect free cluster count. To avoid this problem, FatFs can be forced full FAT scan by FF_FS_NOFSINFO option.

    +

    The f_getfree function gets number of free clusters on the volume. The member csize in the filesystem object indicates number of sectors per cluster, so that the free space in unit of sector can be calcurated with this information. When FSINFO structure on the FAT32 volume is not in sync, this function can return an incorrect free cluster count. To avoid this problem, FatFs can be forced full FAT scan by FF_FS_NOFSINFO option.

    QuickInfo

    -

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    +

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    diff --git a/documents/doc/getlabel.html b/documents/doc/getlabel.html index 743f99f..b793c1e 100644 --- a/documents/doc/getlabel.html +++ b/documents/doc/getlabel.html @@ -1,7 +1,7 @@ - + @@ -13,7 +13,7 @@

    f_getlabel

    -

    The f_getlabel function returns volume label and volume serial number of a drive.

    +

    The f_getlabel function returns volume label and volume serial number of a volume.

     FRESULT f_getlabel (
       const TCHAR* path,  /* [IN] Drive number */
    @@ -29,7 +29,15 @@ FRESULT f_getlabel (
     
    path
    Pointer to the null-terminated string that specifies the logical drive. Null-string specifies the default drive.
    label
    -
    Pointer to the buffer to store the volume label. The buffer size must be at least 24 items at FF_LFN_UNICODE == 0 or 12 items at FF_LFN_UNICODE == 1. If the volume has no label, a null-string will be returned. Set null pointer if this information is not needed.
    +
    Pointer to the buffer to store the volume label. If the volume has no label, a null-string will be returned. Set null pointer if this information is not needed. The buffer size should be shown below at least to avoid buffer overflow.
    +
    + + + + + +
    FF_FS_EXFAT == 0FF_FS_EXFAT == 1
    FF_USE_LFN == 012 items-
    FF_LFN_UNICODE == 012 items23 items
    FF_LFN_UNICODE == 112 items12 items
    FF_LFN_UNICODE == 234 items34 items
    +
    vsn
    Pointer to the DWORD variable to store the volume serial number. Set null pointer if this information is not needed.
    @@ -53,7 +61,7 @@ FRESULT f_getlabel (

    QuickInfo

    -

    Available when FF_USE_LABEL == 1.

    +

    Available when FF_USE_LABEL == 1.

    diff --git a/documents/doc/gets.html b/documents/doc/gets.html index bb20413..e82cfe0 100644 --- a/documents/doc/gets.html +++ b/documents/doc/gets.html @@ -1,7 +1,7 @@ - + @@ -29,7 +29,7 @@ TCHAR* f_gets (
    buff
    Pointer to read buffer to store the read string.
    len
    -
    Size of the read buffer in unit of character.
    +
    Size of the read buffer in unit of item.
    fp
    Pointer to the open file object structure.
    @@ -45,13 +45,13 @@ TCHAR* f_gets (

    Description

    The read operation continues until a '\n' is stored, reached end of the file or the buffer is filled with len - 1 characters. The read string is terminated with a '\0'. When no character to read or any error occured during read operation, it returns a null pointer. The status of EOF and error can be examined with f_eof and f_error function.

    -

    When FatFs is configured to Unicode API (FF_LFN_UNICODE == 1), data types on the srting fuctions, f_putc, f_puts, f_printf and f_gets, is also switched to Unicode. The character encoding on the file to be read/written via those functions is assumed depends on FF_STRF_ENCODE.

    +

    When FatFs is configured to Unicode API (FF_LFN_UNICODE >= 1), data types on the srting fuctions, f_putc, f_puts, f_printf and f_gets, is also switched to Unicode. The character encoding on the file to be read via this function is assumed as FF_STRF_ENCODE. If the character encoding on the file differs from that on the API, it is converted in this function. In this case, input characters with wrong encoding will be lost.

    QuickInfo

    -

    This is a wrapper function of f_read function. Available when FF_USE_STRFUNC is 1 or 2. When it is set to 2, '\r's contained in the file are stripped out.

    +

    This is a wrapper function of f_read function. Available when FF_USE_STRFUNC >= 1. When it is set to 2, '\r's contained in the file are stripped out.

    diff --git a/documents/doc/lseek.html b/documents/doc/lseek.html index e113487..49d6fb5 100644 --- a/documents/doc/lseek.html +++ b/documents/doc/lseek.html @@ -1,7 +1,7 @@ - + @@ -29,7 +29,7 @@ FRESULT f_lseek (
    fp
    Pointer to the open file object.
    ofs
    -
    Byte offset from top of the file to set read/write pointer. The data type FSIZE_t is an alias of either DWORD(32-bit) or QWORD(64-bit) depends on the configuration option FF_FS_EXFAT.
    +
    Byte offset from top of the file to set read/write pointer. The data type FSIZE_t is an alias of either DWORD(32-bit) or QWORD(64-bit) depends on the configuration option FF_FS_EXFAT.
    @@ -61,7 +61,7 @@ FRESULT f_lseek (

    QuickInfo

    -

    Available when FF_FS_MINIMIZE <= 2. To use fast seek function, FF_USE_FASTSEEK needs to be set 1.

    +

    Available when FF_FS_MINIMIZE <= 2. To use fast seek function, FF_USE_FASTSEEK needs to be set 1.

    diff --git a/documents/doc/mkdir.html b/documents/doc/mkdir.html index 18f27cf..905266b 100644 --- a/documents/doc/mkdir.html +++ b/documents/doc/mkdir.html @@ -1,7 +1,7 @@ - + @@ -59,7 +59,7 @@ FRESULT f_mkdir (

    QuickInfo

    -

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    +

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    diff --git a/documents/doc/mkfs.html b/documents/doc/mkfs.html index fe4d513..a74c07b 100644 --- a/documents/doc/mkfs.html +++ b/documents/doc/mkfs.html @@ -1,7 +1,7 @@ - + @@ -31,11 +31,11 @@ FRESULT f_mkfs (
    path
    Pointer to the null-terminated string specifies the logical drive to be formatted. If it has no drive number in it, it means the default drive. The logical drive may or may not be mounted for the format process.
    opt
    -
    Specifies the format option in combination of FM_FAT, FM_FAT32, FM_EXFAT and bitwise-or of these three, FM_ANY. FM_EXFAT is ignored when exFAT is not enabled. These flags specify which FAT type to be created on the volume. If two or more types are specified, one out of them will be selected depends on the volume size. The flag FM_SFD specifies to place the volume on the drive in SFD format.
    +
    Specifies the format option in combination of FM_FAT, FM_FAT32, FM_EXFAT and bitwise-or of these three, FM_ANY. FM_EXFAT is ignored when exFAT is not enabled. These flags specify which FAT type to be created on the volume. If two or more types are specified, one out of them will be selected depends on the volume size and au. The flag FM_SFD specifies to create the volume on the drive in SFD format.
    au
    -
    Specifies size of the allocation unit (cluter) in unit of byte. The valid value is n times the sector size. The n is power of 2 from 1 to 128 for FAT volume and upto 16MiB for exFAT volume. If zero is given, the default allocation unit size is selected depends on the volume size.
    +
    Specifies size of the allocation unit (cluter) in unit of byte. The valid value is n times the sector size. The n is power of 2 from 1 to 128 for FAT volume and upto 16MiB for exFAT volume. If zero is given, the default allocation unit size is selected depends on the volume size.
    work
    -
    Pointer to the working buffer used for the format process.
    +
    Pointer to the working buffer used for the format process. When a null pointer is given, the function allocates a memory block for the working buffer and len has no effect (at only FF_USE_LFN == 3).
    len
    Size of the working buffer in unit of byte. It needs to be the sector size of the corresponding physical drive at least. Plenty of working buffer reduces number of write transactions to the drive and the format process will finish quickly.
    @@ -50,22 +50,23 @@ FRESULT f_mkfs ( FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_MKFS_ABORTED, -FR_INVALID_PARAMETER +FR_INVALID_PARAMETER, +FR_NOT_ENOUGH_CORE

    Description

    -

    The FAT sub-type, FAT12/FAT16/FAT32, of FAT volume except exFAT is determined by only number of clusters on the volume and nothing else, according to the FAT specification issued by Microsoft. Thus which FAT sub-type is selected, is depends on the volume size and the specified cluster size. In case of the combination of FAT type and cluter size specified by argument cannot be valid on the volume, the function will fail with FR_MKFS_ABORTED.

    +

    The FAT sub-type, FAT12/FAT16/FAT32, of FAT volume except exFAT is determined by only number of clusters on the volume and nothing else, according to the FAT specification issued by Microsoft. Thus the FAT sub-type of created volume depends on the volume size and the cluster size. In case of the combination of FAT type and cluter size specified by argument cannot be valid on the volume, the function will fail with FR_MKFS_ABORTED. The minimum drive size is 128 sectors with FM_SFD option.

    The allocation unit, also called cluster, is a unit of disk space allocation for files. When the size of allocation unit is 32768 bytes, a file with 100 bytes in size occupies 32768 bytes of disk space. The space efficiency of disk usage gets worse as increasing size of allocation unit, but, on the other hand, the read/write performance increases as the size of allocation unit. Therefore the size of allocation unit is a trade-off between space efficiency and performance. For the large storages in GB order, 32768 bytes or larger (this is automatically selected by default) is recommended for most case unless extremely many small files are created on a volume.

    -

    There are two disk formats, FDISK and SFD. The FDISK format is usually used for harddisk, MMC, SDC, CFC and U Disk. It can divide a physical drive into one or more partitions with a partition table on the MBR (maser boot record, the first sector of the physical drive). The SFD (super-floppy disk) format is non-partitioned disk format. The FAT volume starts at the first sector of the physical drive without any disk partitioning. It is usually used for floppy disk, Microdrive, optical disk and most type of super-floppy media. Some systems support only either one of the two formats and the other is not supported.

    +

    There are two disk partitioning formats, FDISK and SFD. The FDISK format is usually used for harddisk, memory card and U disk. It can divide a physical drive into one or more partitions with a partition table on the MBR (maser boot record, the first sector of the physical drive). The SFD (super-floppy disk) format is non-partitioned disk format. The FAT volume starts at the first sector of the physical drive without any disk partitioning. It is usually used for floppy disk, optical disk and most super-floppy media. Some systems support only either one of the two disk formats and the other is not supported.

    When the logical drive to be formatted is bound to a physical drive and FM_SFD is not specified, a primary partition occupies whole drive space is created and then the FAT volume is created in it. When FM_SFD is specified, the FAT volume occupies from the first sector of the physical drive is created without partitioning.

    -

    When the logical drive to be formatted is bound to a specific partition (1-4) by support of multiple partition (FF_MULTI_PARTITION) the FAT volume is created into the partition and FM_SFD flag is ignored. The physical drive needs to be partitioned with f_fdisk function or any other partitioning tools prior to create the FAT volume with this function.

    +

    When the logical drive to be formatted is bound to a specific partition (1-4) by support of multiple partition (FF_MULTI_PARTITION == 1) the FAT volume is created into the partition and FM_SFD flag is ignored. The physical drive needs to be partitioned with f_fdisk function or any other partitioning tools prior to create the FAT volume with this function.

    QuickInfo

    -

    Available when FF_FS_READOLNY == 0 and FF_USE_MKFS == 1.

    +

    Available when FF_FS_READOLNY == 0 and FF_USE_MKFS == 1.

    diff --git a/documents/doc/mount.html b/documents/doc/mount.html index b198dc0..3f2acce 100644 --- a/documents/doc/mount.html +++ b/documents/doc/mount.html @@ -1,7 +1,7 @@ - + @@ -60,7 +60,7 @@ FRESULT f_mount (

    If forced mounting is not specified (opt = 0), this function always succeeds regardless of the physical drive status. It only clears (de-initializes) the given work area and registers its address to the internal table and no activity of the physical drive in this function. To unregister the work area, specify a NULL to the fs, and then the work area can be discarded. The volume mount processes, initialize the corresponding physical drive, find the FAT volume in it and then initialize the work area, is performed in the subsequent file/directory functions when either of following conditions is true.

      -
    • File system object has not been initialized. It is de-initialized by f_mount function.
    • +
    • Filesystem object has not been initialized. It is de-initialized by f_mount function.
    • Physical drive is not initialized. It is de-initialized by system reset or media removal.

    If the function with forced mounting (opt = 1) failed, it means that the filesystem object has been registered successfully but the volume is currently not ready to work. The volume mount process will be attempted at subsequent file/directroy functions if the filesystem object is not initialized. (delayed mounting)

    diff --git a/documents/doc/open.html b/documents/doc/open.html index 3e3e621..1dd81d8 100644 --- a/documents/doc/open.html +++ b/documents/doc/open.html @@ -1,7 +1,7 @@ - + @@ -94,7 +94,7 @@ Mode flags of POSIX fopen() corresponds to FatFs mode flags as follows:

    QuickInfo

    -

    Always available. Only FA_READ and FA_OPEN_EXISTING are supported when FF_FS_READONLY == 1.

    +

    Always available. Only FA_READ and FA_OPEN_EXISTING are available for the mode flags when FF_FS_READONLY == 1.

    diff --git a/documents/doc/opendir.html b/documents/doc/opendir.html index 264ed6a..6507249 100644 --- a/documents/doc/opendir.html +++ b/documents/doc/opendir.html @@ -1,7 +1,7 @@ - + @@ -61,7 +61,7 @@ FRESULT f_opendir (

    QuickInfo

    -

    Available when FF_FS_MINIMIZE <= 1.

    +

    Available when FF_FS_MINIMIZE <= 1.

    diff --git a/documents/doc/printf.html b/documents/doc/printf.html index 27d5c8a..e345c23 100644 --- a/documents/doc/printf.html +++ b/documents/doc/printf.html @@ -1,7 +1,7 @@ - + @@ -29,7 +29,7 @@ int f_printf (
    fp
    Pointer to the open file object structure.
    fmt
    -
    Pointer to the null terminated format string. The terminator charactor will not be written.
    +
    Pointer to the null '\0' terminated format string. The terminator character will not be output.
    ...
    Optional arguments...
    @@ -39,25 +39,28 @@ int f_printf (

    Return Values

    -

    When the function succeeded, it returns number of characters written. If the function could not write the generated string to the file due to disk full or an error, EOF (-1) will be returned.

    +

    When the string was written successfuly, it returns number of character encoding units written to the file. When the function failed due to disk full or any error, an EOF (-1) will be returned.

    Description

    -

    The format control directive is a sub-set of standard library shown as follos:

    -
      -
    • Type: c C s S d D u U x X b B
    • -
    • Size: l L
    • -
    • Flag: 0 -
    • -
    +

    The format control directive is a sub-set of standard library shown as follows:

    +
    +    %[flag][width][type]
    +
    +
    +
    flag
    Padding options. A - specifies left justified. A 0 specifies zero padded.
    +
    width
    Minimum width of the field, 1-99 or *. If the width of generated string is less than the specified value, rest field is padded with white spaces or zeros. An * specifies the value comes from an argument in int type.
    +
    type
    c s d u o x b and prefix l specify type of the argument, character, string, signed integer in decimal, unsigned integer in decimal, unsigned integer in octal, unsigned integer in hexdecimal and unsigned integer in binary respectively. If sizeof (long) is greater than sizeof (int) (this is typical of 8/16-bit systems), a prefix l needs to be explicitly specified for long integer argument. These characters except for x are case insensitive.
    +
    +

    When FatFs is configured for Unicode API (FF_LFN_UNICODE >= 1), character encoding on the string fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The Unicode characters in multiple encoding unit, such as surrogate pair and multi-byte sequence, should not be divided into two function calls, or the character will be lost. The character encoding on the file to be written via this function is selected by FF_STRF_ENCODE option. If the character encoding on the file differs from that on the API, it is converted in this function. In this case, input characters with wrong encoding or invalid for the output will be lost.

    QuickInfo

    -

    This is a wrapper function of f_write function. Available when FF_FS_READONLY == 0 and FF_USE_STRFUNC is 1 or 2. When it is set to 2, '\n's contained in the output are converted to '\r'+'\n'.

    -

    When FatFs is configured to Unicode API (FF_LFN_UNICODE == 1), data types on the srting fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The character encoding on the file to be read/written via those functions is selected by FF_STRF_ENCODE option.

    +

    This is a wrapper function of f_write function. Available when FF_FS_READONLY == 0 and FF_USE_STRFUNC >= 1. When it is set to 2, '\n's contained in the output are converted to '\r'+'\n' each.

    @@ -69,16 +72,18 @@ int f_printf ( f_printf(&fil, "%ld", 12345L); /* "12345" */ f_printf(&fil, "%06d", 25); /* "000025" */ f_printf(&fil, "%06d", -25); /* "000-25" */ + f_printf(&fil, "%*d", 5, 100); /* " 100" */ f_printf(&fil, "%-6d", 25); /* "25 " */ f_printf(&fil, "%u", -1); /* "65535" or "4294967295" */ f_printf(&fil, "%04x", 0xAB3); /* "0ab3" */ - f_printf(&fil, "%08LX", 0x123ABCL); /* "00123ABC" */ + f_printf(&fil, "%08lX", 0x123ABCL); /* "00123ABC" */ + f_printf(&fil, "%04o", 255); /* "0377" */ f_printf(&fil, "%016b", 0x550F); /* "0101010100001111" */ f_printf(&fil, "%s", "String"); /* "String" */ f_printf(&fil, "%8s", "abc"); /* " abc" */ f_printf(&fil, "%-8s", "abc"); /* "abc " */ f_printf(&fil, "%c", 'a'); /* "a" */ - f_printf(&fil, "%f", 10.0); /* f_printf lacks floating point support */ + f_printf(&fil, "%f", 10.0); /* f_printf lacks floating point support */
    diff --git a/documents/doc/putc.html b/documents/doc/putc.html index 07e037a..b7cd558 100644 --- a/documents/doc/putc.html +++ b/documents/doc/putc.html @@ -1,7 +1,7 @@ - + @@ -35,11 +35,15 @@ int f_putc (

    Return Values

    -

    When the character was written successfuly, it returns number of characters written. When the function failed due to disk full or any error, an EOF (-1) will be returned.

    -

    When FatFs is configured to Unicode API (FF_LFN_UNICODE == 1), character encoding on the string fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The character encoding on the file to be read/written via those functions is selected by FF_STRF_ENCODE option.

    +

    When the character was written successfuly, it returns number of character encoding units written to the file. When the function failed due to disk full or any error, an EOF (-1) will be returned.

    +
    +

    Description

    +

    When FatFs is configured for Unicode API (FF_LFN_UNICODE >= 1), character encoding on the string fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The character encoding on the file to be written via those functions is selected by FF_STRF_ENCODE option. The Unicode characters in multiple encoding unit, such as surrogate pair and multi-byte sequence, cannot be written by this function.

    +
    +

    QuickInfo

    This is a wrapper function of f_write function. Available when FF_FS_READONLY == 0 and FF_USE_STRFUNC is 1 or 2. When it is set to 2, a '\n' is converted to '\r'+'\n'.

    diff --git a/documents/doc/puts.html b/documents/doc/puts.html index 027b844..a644419 100644 --- a/documents/doc/puts.html +++ b/documents/doc/puts.html @@ -1,7 +1,7 @@ - + @@ -35,14 +35,19 @@ int f_puts (

    Return Value

    -

    When the function succeeded, it returns number of characters written. When the write operation is aborted due to disk full or any error, an EOF (-1) will be returned.

    -

    When FatFs is configured to Unicode API (FF_LFN_UNICODE == 1), character encoding on the srting fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The character encoding on the file to be read/written via those functions is selected by FF_STRF_ENCODE option.

    +

    When the string was written successfuly, it returns number of character encoding units written to the file. When the function failed due to disk full or any error, an EOF (-1) will be returned.

    +
    + + +
    +

    Description

    +

    When FatFs is configured for Unicode API (FF_LFN_UNICODE >= 1), character encoding on the string fuctions, f_putc, f_puts, f_printf and f_gets function, is also switched to Unicode. The input Unicode characters in multiple encoding unit, such as surrogate pair and multi-byte sequence, should not be divided into two function calls, or the character will be lost. The character encoding on the file to be written via this functions is selected by FF_STRF_ENCODE option. If the character encoding on the file differs from that on the API, it is converted in this function. In this case, input characters with wrong encoding or invalid for the output will be lost.

    QuickInfo

    -

    This is a wrapper function of f_write function. Available when FF_FS_READONLY == 0 and FF_USE_STRFUNC is 1 or 2. When it is set to 2, '\n's contained in the string are converted to '\r'+'\n'.

    +

    This is a wrapper function of f_write function. Available when FF_FS_READONLY == 0 and FF_USE_STRFUNC is 1 or 2. When it is set to 2, '\n's contained in the string are converted to '\r'+'\n'.

    diff --git a/documents/doc/rc.html b/documents/doc/rc.html index 246b988..00a7556 100644 --- a/documents/doc/rc.html +++ b/documents/doc/rc.html @@ -48,7 +48,14 @@ Note that if once this error occured at any operation to an open file, the file
    Could not find the path.
    FR_INVALID_NAME
    -
    The given string is invalid as the path name.
    +
    The given string is invalid as the path name. One of the following possibilities is suspected. +
      +
    • There is any character not allowed for the file name.
    • +
    • The string is out of 8.3 format. (at non-LFN cfg.)
    • +
    • FF_MAX_LFN is insufficient for the file name. (at LFN cfg.)
    • +
    • There is any character encoding error in the string.
    • +
    +
    FR_DENIED
    The required access was denied due to one of the following reasons: @@ -69,8 +76,8 @@ Note that if once this error occured at any operation to an open file, the file
    FR_INVALID_OBJECT
    The file/directory object is invalid or a null pointer is given. There are some reasons as follows:
      -
    • It has been closed, it has not been opened or it has been collapsed.
    • -
    • It has been invalidated by a voulme mount process. Open objects on the volume are invalidated by voulme mount process.
    • +
    • It has been closed, or collapsed.
    • +
    • It has been invalidated. Open objects on the volume are invalidated by voulme mount process.
    • Physical drive is not ready to work due to a media removal.
    diff --git a/documents/doc/read.html b/documents/doc/read.html index 469fec5..dfa883a 100644 --- a/documents/doc/read.html +++ b/documents/doc/read.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/readdir.html b/documents/doc/readdir.html index 13a554b..495703b 100644 --- a/documents/doc/readdir.html +++ b/documents/doc/readdir.html @@ -1,7 +1,7 @@ - + @@ -26,9 +26,9 @@ FRESULT f_readdir (

    Parameters

    dp
    -
    Pointer to the open directory object or null pointer.
    +
    Pointer to the open directory object.
    fno
    -
    Pointer to the file information structure to store the information about read item.
    +
    Pointer to the file information structure to store the information about read item. A null pointer rewinds the read index of the directory.
    @@ -49,19 +49,20 @@ FRESULT f_readdir (

    Description

    The f_readdir function reads a directory item, informations about the object. All items in the directory can be read in sequence by f_readdir function calls. Dot entries ("." and "..") in the sub-directory are filtered out and they will never appear in the read items. When all directory items have been read and no item to read, a nul string is stored into the fno->fname[] without any error. When a null pointer is given to the fno, the read index of the directory object is rewinded.

    -

    When support of long file name (LFN) is enabled, a member altname[] is defined in the file information structure to store the short file name of the object. In case of the some conditions listed below, short file name is stored into the fname[] and altname[] has a null string.

    +

    When support of long file name (LFN) is enabled, a member altname[] is defined in the file information structure to store the short file name of the object. If the long file name is not accessible due to some reason listed below, short file name is stored to the fname[] and altname[] has a null string.

    -

    There is a problem on reading a directory of exFAT volume. The exFAT does not support short file name. This means no name can be returned on the condition above. If it is the case, "?" is returned into the fname[] to indicate that the object is not accessible. To avoid this problem, configure FatFs FF_LFN_UNICODE = 1 and FF_MAX_LFN = 255 to support the full feature of LFN specification.

    +

    There is a problem on reading a directory of exFAT volume. The exFAT does not support short file name. This means no name can be returned on the condition above. If it is the case, "?" is returned into the fname[] to indicate that the object is not accessible. To avoid this problem, configure FatFs FF_LFN_UNICODE >= 1 and FF_MAX_LFN == 255 to support the full feature of LFN specification.

    QuickInfo

    -

    Available when FF_FS_MINIMIZE <= 1.

    +

    Available when FF_FS_MINIMIZE <= 1.

    diff --git a/documents/doc/rename.html b/documents/doc/rename.html index d11659a..eb31ed2 100644 --- a/documents/doc/rename.html +++ b/documents/doc/rename.html @@ -1,7 +1,7 @@ - + @@ -63,7 +63,7 @@ FRESULT f_rename (

    QuickInfo

    -

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    +

    Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

    diff --git a/documents/doc/sdir.html b/documents/doc/sdir.html index 5b3c3cf..699a8d6 100644 --- a/documents/doc/sdir.html +++ b/documents/doc/sdir.html @@ -1,7 +1,7 @@ - + @@ -13,7 +13,7 @@

    DIR

    -

    The DIR structure is used for the work area to read a directory by f_oepndir, f_readdir, f_findfirst and f_findnext function. Application program must not modify any member in this structure, or any file on the volume can be collapsed.

    +

    The DIR structure is used for the work area to read a directory by f_oepndir, f_readdir, f_findfirst and f_findnext function. Application program must not modify any member in this structure, or f_readdir function will not work properly.

     typedef struct {
         FFOBJID obj;        /* Object identifier */
    diff --git a/documents/doc/setcp.html b/documents/doc/setcp.html
    index 7ad035a..480393f 100644
    --- a/documents/doc/setcp.html
    +++ b/documents/doc/setcp.html
    @@ -1,7 +1,7 @@
     
     
     
    -
    +
     
     
     
    @@ -28,7 +28,7 @@ FRESULT f_setcp (
     
    OEM code page to be used for the path name. Valid values are as follows.
    - + @@ -67,13 +67,13 @@ FRESULT f_setcp (

    Description

    -

    The f_setcp function sets the active code page for the path name. Also code conversion of string functions will be affected by the code page when FF_LFN_UNICODE = 1 and FF_STRF_ENCODE = 0. Because the initial setting of the code page is 0 and API function with non-ASCII character will not work properly. Any valid code page needs to be set on the system start-up and it should not be changed on the fly.

    +

    The f_setcp function sets the active code page for the path name. Also code conversion of string functions will be affected by the setting of code page when FF_LFN_UNICODE >= 1 and FF_STRF_ENCODE == 0. Because the initial setting of the code page is 0 and API function with extended character will not work properly, a valid code page needs to be set on the system start-up and it should not be changed on the fly.

    QuickInfo

    -

    Available when FF_CODE_PAGE == 0.

    +

    Available when FF_CODE_PAGE == 0.

    diff --git a/documents/doc/setlabel.html b/documents/doc/setlabel.html index 994d647..d596469 100644 --- a/documents/doc/setlabel.html +++ b/documents/doc/setlabel.html @@ -1,7 +1,7 @@ - + @@ -51,7 +51,7 @@ FRESULT f_setlabel (

    Description

    When the string has a drive number, the volume label will be set to the volume specified by the drive number. If not, the volume label will be set to the default drive. If length of the given volume label is zero, the volume label on the volume will be removed. The format of the volume label on the FAT volume is similar to the file name but there are some differences shown below:

      -
    • Period is not allowed.
    • +
    • Dot is not allowed.
    • Spaces can be contained anywhere in the volume label. Trailing spaces are truncated off at FAT volume.
    • Up to 11 bytes long as conversion of OEM code page at FAT volume.
    • Up to 11 characters long at exFAT volume. Lower case is preserved.
    • @@ -61,7 +61,7 @@ FRESULT f_setlabel (

      QuickInfo

      -

      Available when FF_FS_READONLY == 0 and FF_USE_LABEL == 1.

      +

      Available when FF_FS_READONLY == 0 and FF_USE_LABEL == 1.

      diff --git a/documents/doc/sfatfs.html b/documents/doc/sfatfs.html index 867c4b4..a9312f5 100644 --- a/documents/doc/sfatfs.html +++ b/documents/doc/sfatfs.html @@ -1,7 +1,7 @@ - + @@ -13,7 +13,7 @@

      FATFS

      -

      The FATFS structure (filesystem object) holds dynamic work area of individual logical drives. It is given by application program and registerd/unregisterd to the FatFs module with f_mount function. Initialization of the structure is done by volume mount process whenever necessary. Application program must not modify any member in this structure, or the FAT volume can be collapsed.

      +

      The FATFS structure (filesystem object) holds dynamic work area of individual logical drives. It is given by application program and registerd/unregisterd to the FatFs module with f_mount function. Initialization of the structure is done by volume mount process whenever necessary. Application program must not modify any member in this structure, or the FAT volume will be collapsed.

       typedef struct {
           BYTE    fs_type;      /* FAT type (0, FS_FAT12, FS_FAT16, FS_FAT32 or FS_EXFAT) */
      @@ -27,7 +27,7 @@
       #if FF_MAX_SS != FF_MIN_SS
           WORD    ssize;        /* Sector size (512,1024,2048 or 4096) */
       #endif
      -#if FF_FS_EXFAT
      +#if FF_FS_EXFAT
           BYTE*   dirbuf;       /* Directory entry block scratchpad buffer */
       #endif
       #if FF_FS_REENTRANT
      @@ -39,7 +39,7 @@
       #endif
       #if FF_FS_RPATH
           DWORD   cdir;         /* Cluster number of current directory (0:root) */
      -#if FF_FS_EXFAT
      +#if FF_FS_EXFAT
           DWORD   cdc_scl;      /* Containing directory start cluster (invalid when cdir is 0) */
           DWORD   cdc_size;     /* b31-b8:Size of containing directory, b7-b0: Chain status */
           DWORD   cdc_ofs;      /* Offset in the containing directory (invalid when cdir is 0) */
      diff --git a/documents/doc/sfile.html b/documents/doc/sfile.html
      index d3731f0..599b8d2 100644
      --- a/documents/doc/sfile.html
      +++ b/documents/doc/sfile.html
      @@ -1,7 +1,7 @@
       
       
       
      -
      +
       
       
       
      @@ -13,7 +13,7 @@
       
       

      FIL

      -

      The FIL structure (file object) holds the state of an open file. It is created by f_open function and discarded by f_close function. Application program must not modify any member in this structure except for cltbl, or any data on the FAT volume can be collapsed. Note that a sector buffer is defined in this structure at non-tiny configuration (FF_FS_TINY == 0), so that the FIL structures at that configuration should not be defined as auto variable.

      +

      The FIL structure (file object) holds the state of an open file. It is created by f_open function and discarded by f_close function. Application program must not modify any member in this structure except for cltbl, or the FAT volume will be collapsed. Note that a sector buffer is defined in this structure at non-tiny configuration (FF_FS_TINY == 0), so that the FIL structures at that configuration should not be defined as auto variable.

       typedef struct {
      diff --git a/documents/doc/sfileinfo.html b/documents/doc/sfileinfo.html
      index 311c0c3..c9597a9 100644
      --- a/documents/doc/sfileinfo.html
      +++ b/documents/doc/sfileinfo.html
      @@ -1,7 +1,7 @@
       
       
       
      -
      +
       
       
       
      @@ -21,10 +21,10 @@
           WORD    ftime;               /* Last modified time */
           BYTE    fattrib;             /* Attribute */
       #if FF_USE_LFN
      -    TCHAR   altname[13];         /* Alternative object name */
      -    TCHAR   fname[FF_MAX_LFN + 1]; /* Primary object name */
      +    TCHAR   altname[FF_SFN_BUF + 1]; /* Alternative object name */
      +    TCHAR   fname[FF_LFN_BUF + 1];   /* Primary object name */
       #else
      -    TCHAR   fname[13];           /* Object name */
      +    TCHAR   fname[12 + 1];       /* Object name */
       #endif
       } FILINFO;
       
      @@ -59,7 +59,7 @@
      fattrib
      Indicates the file/directory attribute in combination of AM_DIR, AM_RDO, AM_HID, AM_SYS and AM_ARC.
      fname[]
      -
      The null-terminated object name is stored. A null string is stored when no item to read and it indicates this structure is invalid.
      +
      The null-terminated object name is stored. A null string is stored when no item to read and it indicates this structure is invalid. The size of fname[] and altname[] each can be configured at LFN configuration.
      altname[]
      Alternative object name is stored if available. This member is not available at non-LFN configuration.
      diff --git a/documents/doc/size.html b/documents/doc/size.html index 8e2a105..535df93 100644 --- a/documents/doc/size.html +++ b/documents/doc/size.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/stat.html b/documents/doc/stat.html index 8608336..b0012cf 100644 --- a/documents/doc/stat.html +++ b/documents/doc/stat.html @@ -1,7 +1,7 @@ - + @@ -60,7 +60,7 @@ FRESULT f_stat (

      QuickInfo

      -

      Available when FF_FS_MINIMIZE == 0.

      +

      Available when FF_FS_MINIMIZE == 0.

      diff --git a/documents/doc/sync.html b/documents/doc/sync.html index ca33004..df35312 100644 --- a/documents/doc/sync.html +++ b/documents/doc/sync.html @@ -1,7 +1,7 @@ - + @@ -51,7 +51,7 @@ FRESULT f_sync (

      QuickInfo

      -

      Available when FF_FS_READONLY == 0.

      +

      Available when FF_FS_READONLY == 0.

      diff --git a/documents/doc/tell.html b/documents/doc/tell.html index 03844ef..56acb47 100644 --- a/documents/doc/tell.html +++ b/documents/doc/tell.html @@ -1,7 +1,7 @@ - + diff --git a/documents/doc/truncate.html b/documents/doc/truncate.html index f72a5e3..498949a 100644 --- a/documents/doc/truncate.html +++ b/documents/doc/truncate.html @@ -1,7 +1,7 @@ - + @@ -51,7 +51,7 @@ FRESULT f_truncate (

      QuickInfo

      -

      Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

      +

      Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

      diff --git a/documents/doc/unlink.html b/documents/doc/unlink.html index fa24a76..edd6371 100644 --- a/documents/doc/unlink.html +++ b/documents/doc/unlink.html @@ -1,7 +1,7 @@ - + @@ -65,7 +65,7 @@ If condition of the object to be removed is applicable to the following terms, t

      QuickInfo

      -

      Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

      +

      Available when FF_FS_READONLY == 0 and FF_FS_MINIMIZE == 0.

      diff --git a/documents/doc/utime.html b/documents/doc/utime.html index 511e705..d33ed4d 100644 --- a/documents/doc/utime.html +++ b/documents/doc/utime.html @@ -1,7 +1,7 @@ - + @@ -85,7 +85,7 @@ FRESULT set_timestamp (

      QuickInfo

      -

      Available when FF_FS_READONLY == 0 and FF_USE_CHMOD == 1.

      +

      Available when FF_FS_READONLY == 0 and FF_USE_CHMOD == 1.

      diff --git a/documents/doc/write.html b/documents/doc/write.html index f10a059..e5401a4 100644 --- a/documents/doc/write.html +++ b/documents/doc/write.html @@ -1,7 +1,7 @@ - + @@ -60,7 +60,7 @@ FRESULT f_write (

      QuickInfo

      -

      Available when FF_FS_READONLY == 0.

      +

      Available when FF_FS_READONLY == 0.

      diff --git a/documents/updates.txt b/documents/updates.txt index 10dfe0b..f15d4d5 100644 --- a/documents/updates.txt +++ b/documents/updates.txt @@ -1,3 +1,11 @@ +R0.13a (October 14, 2017) + Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) + Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). + Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). + Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) + Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) + Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) + R0.13 (May 21, 2017) Changed heading character of configuration keywords "_" to "FF_". Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead. diff --git a/source/00history.txt b/source/00history.txt index 3d7b1f8..6789ab0 100644 --- a/source/00history.txt +++ b/source/00history.txt @@ -300,3 +300,15 @@ R0.13 (May 21, 2017) Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c) Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c) + + +R0.13a (October 14, 2017) + + Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) + Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). + Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). + Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) + Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) + Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) + + diff --git a/source/00readme.txt b/source/00readme.txt index 24369df..6eff7fc 100644 --- a/source/00readme.txt +++ b/source/00readme.txt @@ -1,4 +1,4 @@ -FatFs Module Source Files R0.13 +FatFs Module Source Files R0.13a FILES diff --git a/source/ff.c b/source/ff.c index 856d784..d29601d 100644 --- a/source/ff.c +++ b/source/ff.c @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem Module R0.13 / +/ FatFs - Generic FAT Filesystem Module R0.13a / /-----------------------------------------------------------------------------/ / / Copyright (C) 2017, ChaN, all right reserved. @@ -29,15 +29,18 @@ ---------------------------------------------------------------------------*/ -#if FF_DEFINED != 87030 /* Revision ID */ +#if FF_DEFINED != 89352 /* Revision ID */ #error Wrong include file (ff.h). #endif -/* ASCII code support macros */ -#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') -#define IsLower(c) ((c) >= 'a' && (c) <= 'z') -#define IsDigit(c) ((c) >= '0' && (c) <= '9') +/* 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') +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) /* Additional file attribute bits for internal use */ @@ -67,8 +70,8 @@ /* Limits and boundaries */ #define MAX_DIR 0x200000 /* Max size of FAT directory */ #define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ -#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */ -#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */ #define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ #define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ @@ -123,8 +126,7 @@ #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ #define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */ -#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ -#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ @@ -194,11 +196,11 @@ #define PTE_SizLba 12 /* MBR PTE: Size in LBA */ -/* Post process after fatal error on file operation */ +/* Post process on fatal error in the file operations */ #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } -/* Reentrancy related */ +/* Re-entrancy related */ #if FF_FS_REENTRANT #if FF_USE_LFN == 1 #error Static LFN work area cannot be used at thread-safe configuration @@ -209,7 +211,7 @@ #endif -/* Definitions of volume - partition conversion */ +/* Definitions of volume - physical location conversion */ #if FF_MULTI_PARTITION #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ #define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ @@ -436,21 +438,32 @@ static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ #endif + /*--------------------------------*/ /* LFN/Directory working buffer */ /*--------------------------------*/ #if FF_USE_LFN == 0 /* Non-LFN configuration */ +#if FF_FS_EXFAT +#error LFN must be enabled when enable exFAT +#endif #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res #else /* LFN configurations */ #if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 -#error Wrong FF_MAX_LFN setting +#error Wrong setting of FF_MAX_LFN +#endif +#if FF_LFN_BUF < 12 || FF_SFN_BUF < 12 || FF_LFN_BUF < FF_SFN_BUF +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF +#endif +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 2 +#error Wrong setting of FF_LFN_UNICODE #endif static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */ -#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the max name length */ +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */ #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ #if FF_FS_EXFAT @@ -460,6 +473,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ #if FF_FS_EXFAT @@ -471,6 +485,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } #define FREE_NAMBUF() #endif +#define LEAVE_MKFS(res) return res #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ #if FF_FS_EXFAT @@ -482,12 +497,15 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } #define FREE_NAMBUF() ff_memfree(lfn) #endif +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } +#define MAX_MALLOC 0x8000 #else -#error Wrong FF_USE_LFN setting +#error Wrong setting of FF_USE_LFN + +#endif /* FF_USE_LFN == 1 */ +#endif /* FF_USE_LFN == 0 */ -#endif -#endif /*--------------------------------*/ @@ -688,8 +706,8 @@ int dbc_1st (BYTE c) if (c <= DbcTbl[1]) return 1; if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; } -#else /* SBCS fixed code page */ - if (c) return 0; /* Always false */ +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ #endif return 0; } @@ -705,19 +723,163 @@ int dbc_2nd (BYTE c) if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */ if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */ } -#elif FF_CODE_PAGE >= 900 /* DBCD fixed code page */ +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ if (c >= DbcTbl[4]) { if (c <= DbcTbl[5]) return 1; if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; } -#else /* SBCS fixed code page */ - if (c) return 0; /* Always false */ +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ #endif return 0; } +#if FF_USE_LFN + +/* Get a character from TCHAR string in defined API encodeing */ +static +DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */ + const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ +) +{ + DWORD uc; + const TCHAR *p = *str; + +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + WCHAR wc; + + uc = *p++; + if (IsSurrogate(uc)) { /* Surrogate? */ + wc = *p++; /* Get low surrogate */ + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */ + uc = uc << 16 | wc; + } + +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + BYTE b; + int nf; + + uc = (BYTE)*p++; /* Get a byte */ + if (uc & 0x80) { /* Multiple byte code? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + uc &= 0x1F; nf = 1; + } else { + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else { + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; + } + } + } + do { /* Get trailing bytes */ + b = (BYTE)*p++; + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */ + uc = uc << 6 | (b & 0x3F); + } while (--nf != 0); + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x10000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + } + +#else /* ANSI/OEM input */ + BYTE b; + WCHAR wc; + + wc = (BYTE)*p++; /* Get a byte */ + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ + b = (BYTE)*p++; /* Get 2nd byte */ + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ + wc = (wc << 8) + b; /* Make a DBC */ + } + if (wc != 0) { + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ + } + uc = wc; + +#endif + *str = p; /* Next read pointer */ + return uc; +} + + +/* Output a TCHAR string in defined API encoding */ +static +BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ + DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */ + TCHAR* buf, /* Output buffer */ + UINT szb /* Size of the buffer */ +) +{ +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ + WCHAR hs, wc; + + hs = (WCHAR)(chr >> 16); + wc = (WCHAR)chr; + if (hs == 0) { /* Single encoding unit? */ + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */ + *buf = wc; + return 1; + } + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */ + *buf++ = hs; + *buf++ = wc; + return 2; + +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ + DWORD hc; + + if (chr < 0x80) { /* Single byte code? */ + if (szb < 1) return 0; /* Buffer overflow? */ + *buf = (TCHAR)chr; + return 1; + } + if (chr < 0x800) { /* 2-byte sequence? */ + if (szb < 2) return 0; /* Buffer overflow? */ + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 2; + } + if (chr < 0x10000) { /* 3-byte sequence? */ + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 3; + } + /* 4-byte sequence */ + if (szb < 4) return 0; /* Buffer overflow? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 4; + +#else /* ANSI/OEM output */ + WCHAR wc; + + wc = ff_uni2oem(chr, CODEPAGE); + if (wc >= 0x100) { /* Is this a DBC? */ + if (szb < 2) return 0; + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + return 2; + } + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ + *buf++ = (TCHAR)wc; /* Store the character */ + return 1; +#endif +} +#endif /* FF_USE_LFN */ + #if FF_FS_REENTRANT /*-----------------------------------------------------------------------*/ @@ -814,7 +976,7 @@ UINT inc_lock ( /* Increment object open counter and returns its index (0:Intern Files[i].ctr = 0; } - if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */ Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ @@ -1144,13 +1306,13 @@ DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk err if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ val = 0; bm = 0; i = SS(fs); } - if (!bv) { /* Is it a free cluster? */ + if (bv == 0) { /* Is it a free cluster? */ if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ } else { scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ } if (val == clst) return 0; /* All cluster scanned? */ - } while (bm); + } while (bm != 0); bm = 1; } while (++i < SS(fs)); } @@ -1232,7 +1394,7 @@ FRESULT fill_last_frag ( FRESULT res; - while (obj->n_frag > 0) { /* Create the last chain on the FAT */ + while (obj->n_frag > 0) { /* Create the chain of last fragment */ res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); if (res != FR_OK) return res; obj->n_frag--; @@ -1494,8 +1656,8 @@ FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ fs->winsect = sect; /* Set window to top of the cluster */ mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ - /* Allocate a temporary buffer (32 KB max) */ - for (szb = ((DWORD)fs->csize * SS(fs) >= 0x8000) ? 0x8000 : fs->csize * SS(fs); szb > SS(fs) && !(ibuf = ff_memalloc(szb)); szb /= 2) ; + /* Allocate a temporary buffer */ + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs); szb > SS(fs) && !(ibuf = ff_memalloc(szb)); szb /= 2) ; if (szb > SS(fs)) { /* Buffer allocated? */ mem_set(ibuf, 0, szb); szb /= SS(fs); /* Bytes -> Sectors */ @@ -1725,7 +1887,7 @@ int cmp_lfn ( /* 1:matched, 0:not matched */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ - if (wc) { + if (wc != 0) { if (i >= FF_MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ return 0; /* Not matched */ } @@ -1761,7 +1923,7 @@ int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ - if (wc) { + if (wc != 0) { if (i >= FF_MAX_LFN) return 0; /* Buffer overflow? */ lfnbuf[i++] = wc = uc; /* Store it */ } else { @@ -1935,7 +2097,7 @@ WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ while ((chr = *name++) != 0) { - chr = ff_wtoupper(chr); /* File name needs to be upper-case converted */ + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be upper-case converted */ sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); } @@ -1962,31 +2124,34 @@ DWORD xsum32 ( /*------------------------------------------------------*/ static -void get_xdir_info ( +void get_xfileinfo ( BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ FILINFO* fno /* Buffer to store the extracted file information */ ) { - WCHAR w; + WCHAR wc, hs; UINT di, si, nc; - /* Get file name */ - for (si = SZDIRE * 2, nc = di = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + /* Get file name from the entry block */ + si = SZDIRE * 2; /* 1st C1 entry */ + nc = hs = di = 0; + while (nc < dirb[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ - w = ld_word(dirb + si); /* Get a character */ -#if !FF_LFN_UNICODE /* ANSI/OEM API */ - w = ff_uni2oem(w, CODEPAGE); /* Convert it to OEM code */ - if (w >= 0x100) { /* Is it a double byte char? */ - fno->fname[di++] = (char)(w >> 8); /* Store 1st byte of the DBC */ + wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ } -#endif - if (w == 0 || di >= FF_MAX_LFN) { di = 0; break; } /* Invalid char or buffer overflow --> inaccessible object name */ - fno->fname[di++] = (TCHAR)w; /* Store the character */ + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ + di += wc; + hs = 0; } + if (hs != 0) di = 0; /* Broken surrogate pair? */ if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ - fno->fname[di] = 0; /* Terminate file name */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not have SFN */ - fno->altname[0] = 0; /* No SFN */ fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ @@ -2048,6 +2213,24 @@ FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ } +/*------------------------------------------------------------------*/ +/* exFAT: Initialize object allocation info with loaded entry block */ +/*------------------------------------------------------------------*/ + +static +void init_alloc_info ( + FATFS* fs, /* Filesystem object */ + FFOBJID* obj /* Object allocation information to be initialized */ +) +{ + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */ + obj->n_frag = 0; /* No last fragment info */ +} + + + #if !FF_FS_READONLY || FF_FS_RPATH != 0 /*------------------------------------------------*/ /* exFAT: Load the object's directory entry block */ @@ -2122,28 +2305,29 @@ void create_xdir ( { UINT i; BYTE nc1, nlen; - WCHAR chr; + WCHAR wc; - /* Create 85+C0 entry */ + /* Create 85,C0 entry */ mem_set(dirb, 0, 2 * SZDIRE); - dirb[0 * SZDIRE + XDIR_Type] = 0x85; - dirb[1 * SZDIRE + XDIR_Type] = 0xC0; + dirb[0 * SZDIRE + XDIR_Type] = 0x85; /* 85 entry */ + dirb[1 * SZDIRE + XDIR_Type] = 0xC0; /* C0 entry */ /* Create C1 entries */ - nlen = nc1 = 0; chr = 1; i = SZDIRE * 2; + i = SZDIRE * 2; /* Top of C1 entries */ + nlen = nc1 = 0; wc = 1; do { dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ do { /* Fill name field */ - if (chr && (chr = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ - st_word(dirb + i, chr); /* Store it */ + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ + st_word(dirb + i, wc); /* Store it */ i += 2; } while (i % SZDIRE != 0); nc1++; } while (lfn[nlen]); /* Fill next entry if any char follows */ dirb[XDIR_NumName] = nlen; /* Set name length */ - dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count */ + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ } @@ -2157,6 +2341,9 @@ void create_xdir ( /* Read an object from the directory */ /*-----------------------------------------------------------------------*/ +#define dir_read_file(dp) dir_read(dp, 0) +#define dir_read_label(dp) dir_read(dp, 1) + static FRESULT dir_read ( DIR* dp, /* Pointer to the directory object */ @@ -2208,7 +2395,7 @@ FRESULT dir_read ( /* Check LFN validity and capture it */ ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } else { /* An SFN entry is found */ - if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ } break; @@ -2256,7 +2443,7 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ UINT di, ni; WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ - while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ + while ((res = dir_read_file(dp)) == FR_OK) { /* Read an item */ #if FF_MAX_LFN < 255 if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ #endif @@ -2295,7 +2482,7 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } } else { /* An SFN entry is found */ - if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } @@ -2440,11 +2627,10 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ do { res = move_window(fs, dp->sect); if (res != FR_OK) break; - /* Mark an entry 'deleted' */ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - dp->dir[XDIR_Type] &= 0x7F; + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ } else { /* On the FAT/FAT32 volume */ - dp->dir[DIR_Name] = DDEM; + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ } fs->wflag = 1; if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ @@ -2456,7 +2642,7 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ res = move_window(fs, dp->sect); if (res == FR_OK) { - dp->dir[DIR_Name] = DDEM; + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ fs->wflag = 1; } #endif @@ -2474,17 +2660,17 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ /*-----------------------------------------------------------------------*/ static -void get_fileinfo ( /* No return code */ - DIR* dp, /* Pointer to the directory object */ - FILINFO* fno /* Pointer to the file information to be filled */ +void get_fileinfo ( + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ ) { - UINT i, j; - TCHAR c; - DWORD tm; + UINT si, di; #if FF_USE_LFN - WCHAR w, lfv; + WCHAR wc, hs; FATFS *fs = dp->obj.fs; +#else + TCHAR c; #endif @@ -2494,76 +2680,79 @@ void get_fileinfo ( /* No return code */ #if FF_USE_LFN /* LFN configuration */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - get_xdir_info(fs->dirbuf, fno); + get_xfileinfo(fs->dirbuf, fno); return; } else #endif { /* On the FAT/FAT32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ - i = j = 0; - while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ -#if !FF_LFN_UNICODE /* ANSI/OEM API */ - w = ff_uni2oem(w, CODEPAGE); /* Unicode -> OEM */ - if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ - if (w >= 0x100) { /* Put 1st byte if it is a DBC */ - fno->fname[i++] = (char)(w >> 8); + si = di = hs = 0; + while (fs->lfnbuf[si] != 0) { + wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ } -#endif - if (i >= FF_MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ - fno->fname[i++] = (TCHAR)w; + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ + di += wc; + hs = 0; } - fno->fname[i] = 0; /* Terminate the LFN */ + if (hs != 0) di = 0; /* Broken surrogate pair? */ + fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */ } } - i = j = 0; - lfv = fno->fname[i]; /* LFN is exist if non-zero */ - while (i < 11) { /* Copy name body and extension */ - c = (TCHAR)dp->dir[i++]; - if (c == ' ') continue; /* Skip padding spaces */ - if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ - if (i == 9) { /* Insert a . if extension is exist */ - if (!lfv) fno->fname[j] = '.'; - fno->altname[j++] = '.'; + si = di = 0; + while (si < 11) { /* Get SFN from SFN entry */ + wc = dp->dir[si++]; /* Get a char */ + if (wc == ' ') continue; /* Skip padding spaces */ + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */ + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */ +#if FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ + wc = wc << 8 | dp->dir[si++]; } -#if FF_LFN_UNICODE /* Unicode API */ - if (dbc_1st((BYTE)c) && i != 8 && i != 11 && dbc_2nd(dp->dir[i])) { - c = c << 8 | dp->dir[i++]; - } - c = ff_oem2uni(c, CODEPAGE); /* OEM -> Unicode */ - if (!c) c = '?'; + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ + if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in UTF-16 or UTF-8 */ + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ + di += wc; +#else /* ANSI/OEM output */ + fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ #endif - fno->altname[j] = c; - if (!lfv) { - if (IsUpper(c) && (dp->dir[DIR_NTres] & ((i >= 9) ? NS_EXT : NS_BODY))) { - c += 0x20; /* To lower */ + } + fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ + + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */ + if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */ + fno->fname[di++] = '?'; + } else { + for (si = di = 0; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */ + wc = (WCHAR)fno->altname[si]; + if (IsUpper(wc) && (dp->dir[DIR_NTres] & ((si >= 9) ? NS_EXT : NS_BODY))) wc += 0x20; + fno->fname[di] = (TCHAR)wc; } - fno->fname[j] = c; } - j++; + fno->fname[di] = 0; /* Terminate the LFN */ + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */ } - if (!lfv) { - fno->fname[j] = 0; - if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ - } - fno->altname[j] = 0; /* Terminate the SFN */ #else /* Non-LFN configuration */ - i = j = 0; - while (i < 11) { /* Copy name body and extension */ - c = (TCHAR)dp->dir[i++]; - if (c == ' ') continue; /* Skip padding spaces */ - if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ - if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ - fno->fname[j++] = c; + si = di = 0; + while (si < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[si++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ + if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ + fno->fname[di++] = c; } - fno->fname[j] = 0; + fno->fname[di] = 0; #endif - fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ - fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ - tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ - fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ } #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ @@ -2576,27 +2765,32 @@ void get_fileinfo ( /* No return code */ /*-----------------------------------------------------------------------*/ static -WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ - const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +DWORD get_achar ( /* Get a character and advances ptr */ + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ ) { - WCHAR chr; + DWORD chr; -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */ - chr = ff_wtoupper(*(*ptr)++); /* Get a Unicode char and to upper */ -#else /* ANSI/OEM API */ - chr = (BYTE)*(*ptr)++; /* Get a byte */ - if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ + +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ + chr = tchar2uni(ptr); + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */ + chr = ff_wtoupper(chr); + +#else /* ANSI/OEM input */ + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ #if FF_CODE_PAGE == 0 if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ #elif FF_CODE_PAGE < 900 if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ #endif #if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 - if (dbc_1st((BYTE)chr) && dbc_2nd((BYTE)**ptr)) { /* Get DBC 2nd byte if needed */ - chr = chr << 8 | (BYTE)*(*ptr)++; + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; } #endif + #endif return chr; } @@ -2611,21 +2805,21 @@ int pattern_matching ( /* 0:not matched, 1:matched */ ) { const TCHAR *pp, *np; - WCHAR pc, nc; + DWORD pc, nc; int nm, nx; while (skip--) { /* Pre-skip name chars */ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ } - if (!*pat && inf) return 1; /* (short circuit) */ + if (*pat == 0 && inf) return 1; /* (short circuit) */ do { pp = pat; np = nam; /* Top of pattern and name to match */ for (;;) { if (*pp == '?' || *pp == '*') { /* Wildcard? */ nm = nx = 0; - do { /* Analyze the wildcard chars */ + do { /* Analyze the wildcard block */ if (*pp++ == '?') nm++; else nx = 1; } while (*pp == '?' || *pp == '*'); if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ @@ -2658,129 +2852,121 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ { #if FF_USE_LFN /* LFN configuration */ BYTE b, cf; - WCHAR w, *lfn; + WCHAR wc, *lfn; + DWORD uc; UINT i, ni, si, di; const TCHAR *p; - /* Create LFN in Unicode */ - p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; + /* Create LFN into LFN working buffer */ + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; for (;;) { - w = p[si++]; /* Get a character */ - if (w < ' ') break; /* Break if end of the path name */ - if (w == '/' || w == '\\') { /* Break if a separator is found */ - while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ - break; - } + uc = tchar2uni(&p); /* Get a character */ + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ + wc = (WCHAR)uc; + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ -#if !FF_LFN_UNICODE /* ANSI/OEM API */ - w &= 0xFF; - if (dbc_1st((BYTE)w)) { /* Check if it is a DBC 1st byte */ - b = (BYTE)p[si++]; /* Get 2nd byte */ - w = (w << 8) + b; /* Create a DBC */ - if (!dbc_2nd(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ - } - w = ff_oem2uni(w, CODEPAGE); /* Convert ANSI/OEM to Unicode */ - if (!w) return FR_INVALID_NAME; /* Reject invalid code */ -#endif - if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ - lfn[di++] = w; /* Store the Unicode character */ + lfn[di++] = wc; /* Store the Unicode character */ } - *path = &p[si]; /* Return pointer to the next segment */ - cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + *path = p; /* Return pointer to the next segment */ + cf = (wc < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + #if FF_FS_RPATH != 0 if ((di == 1 && lfn[di - 1] == '.') || (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ lfn[di] = 0; - for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ + for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ dp->fn[i] = (i < di) ? '.' : ' '; + } dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ return FR_OK; } #endif while (di) { /* Snip off trailing spaces and dots if exist */ - w = lfn[di - 1]; - if (w != ' ' && w != '.') break; + wc = lfn[di - 1]; + if (wc != ' ' && wc != '.') break; di--; } - lfn[di] = 0; /* LFN is created */ - if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ + lfn[di] = 0; /* LFN is created into the working buffer */ + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ /* Create SFN in directory form */ - mem_set(dp->fn, ' ', 11); - for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ - if (si > 0) cf |= NS_LOSS | NS_LFN; - while (di > 0 && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ + mem_set(dp->fn, ' ', 11); i = b = 0; ni = 8; for (;;) { - w = lfn[si++]; /* Get an LFN character */ - if (!w) break; /* Break on end of the LFN */ - if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + wc = lfn[si++]; /* Get an LFN character */ + if (wc == 0) break; /* Break on end of the LFN */ + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */ cf |= NS_LOSS | NS_LFN; continue; } - if (i >= ni || si == di) { /* Entered extension or end of SFN */ - if (ni == 11) { /* Extension fileld overflow? */ + if (i >= ni || si == di) { /* End of field? */ + if (ni == 11) { /* Name extension overflow? */ cf |= NS_LOSS | NS_LFN; break; } - if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ - if (si > di) break; /* No extension */ - si = di; i = 8; ni = 11; /* Enter extension fileld */ - b <<= 2; continue; + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */ + if (si > di) break; /* No name extension? */ + si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */ + continue; } - if (w >= 0x80) { /* Is this a non-ASCII character? */ - cf |= NS_LFN; /* Force to create LFN entry */ + if (wc >= 0x80) { /* Is this a non-ASCII character? */ + cf |= NS_LFN; /* LFN entry needs to be created */ #if FF_CODE_PAGE == 0 - if (ExCvt) { /* In SBCS */ - w = ff_uni2oem(w, CODEPAGE); /* Unicode -> OEM code */ - if (w & 0x80) w = ExCvt[w & 0x7F]; /* Convert extended character to upper (SBCS) */ - } else { /* In DBCS */ - w = ff_uni2oem(ff_wtoupper(w), CODEPAGE); /* Upper converted Unicode -> OEM code */ + if (ExCvt) { /* At SBCS */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ + } else { /* At DBCS */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ } #elif FF_CODE_PAGE < 900 /* SBCS cfg */ - w = ff_uni2oem(w, CODEPAGE); /* Unicode -> OEM code */ - if (w & 0x80) w = ExCvt[w & 0x7F]; /* Convert extended character to upper (SBCS) */ -#else /* DBCS cfg */ - w = ff_uni2oem(ff_wtoupper(w), CODEPAGE); /* Upper converted Unicode -> OEM code */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ +#else /* DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ #endif } - if (w >= 0x100) { /* Is this a DBC? */ + if (wc >= 0x100) { /* Is this a DBC? */ if (i >= ni - 1) { /* Field overflow? */ cf |= NS_LOSS | NS_LFN; i = ni; continue; /* Next field */ } - dp->fn[i++] = (BYTE)(w >> 8); /* Put 1st byte */ + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ } else { /* SBC */ - if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ - w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ } else { - if (IsUpper(w)) { /* ASCII large capital */ + if (IsUpper(wc)) { /* ASCII upper case? */ b |= 2; - } else { - if (IsLower(w)) { /* ASCII small capital */ - b |= 1; w -= 0x20; - } + } + if (IsLower(wc)) { /* ASCII lower case? */ + b |= 1; wc -= 0x20; } } } - dp->fn[i++] = (BYTE)w; + dp->fn[i++] = (BYTE)wc; } if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ - if (ni == 8) b <<= 2; - if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ - if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ - if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ - if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + if (ni == 8) b <<= 2; /* Shift capital flags if no extension */ + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */ + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */ } - dp->fn[NSFLAG] = cf; /* SFN is created */ + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ return FR_OK; @@ -2808,24 +2994,24 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ } #endif for (;;) { - c = (BYTE)p[si++]; + c = (BYTE)p[si++]; /* Get a byte */ if (c <= ' ') break; /* Break if end of the path name */ if (c == '/' || c == '\\') { /* Break if a separator is found */ while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ break; } - if (c == '.' || i >= ni) { /* End of body or over size? */ - if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ - i = 8; ni = 11; /* Goto extension */ + if (c == '.' || i >= ni) { /* End of body or field overflow? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */ + i = 8; ni = 11; /* Enter file extension field */ continue; } #if FF_CODE_PAGE == 0 if (ExCvt && c >= 0x80) { /* Is SBC extended character? */ - c = ExCvt[c - 0x80]; /* To upper SBC extended character */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ } #elif FF_CODE_PAGE < 900 if (c >= 0x80) { /* Is SBC extended character? */ - c = ExCvt[c - 0x80]; /* To upper SBC extended character */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ } #endif if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */ @@ -2925,9 +3111,7 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ dp->obj.c_scl = dp->obj.sclust; dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; dp->obj.c_ofs = dp->blk_ofs; - dp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ - dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; - dp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + init_alloc_info(fs, &dp->obj); /* Open next directory */ } else #endif { @@ -2962,7 +3146,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ #endif - if (*path) { /* If the pointer is not a null */ + if (*path != 0) { /* If the pointer is not a null */ for (tt = *path; (UINT)*tt >= (FF_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find a colon in the path */ if (*tt == ':') { /* If a colon is exist in the path name */ tp = *path; @@ -3021,7 +3205,7 @@ BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk er #if FF_FS_EXFAT if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ #endif - if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { /* Valid JumpBoot code? */ + if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */ if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */ if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */ } @@ -3032,7 +3216,7 @@ BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk er /*-----------------------------------------------------------------------*/ -/* Find logical drive and check if the volume is mounted */ +/* Determine logical drive number and mount the volume if needed */ /*-----------------------------------------------------------------------*/ static @@ -3373,7 +3557,7 @@ FRESULT f_open ( if (!fp) return FR_INVALID_OBJECT; /* Get logical drive */ - mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; res = find_volume(&path, &fs, mode); if (res == FR_OK) { dj.obj.fs = fs; @@ -3414,10 +3598,7 @@ FRESULT f_open ( if (fs->fs_type == FS_EXFAT) { /* Get current allocation info */ fp->obj.fs = fs; - fp->obj.sclust = cl = ld_dword(fs->dirbuf + XDIR_FstClus); - fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); - fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; - fp->obj.n_frag = 0; + init_alloc_info(fs, &fp->obj); /* Set directory entry block initial state */ mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ @@ -3425,9 +3606,9 @@ FRESULT f_open ( st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); fs->dirbuf[XDIR_GenFlags] = 1; res = store_xdir(&dj); - if (res == FR_OK && cl != 0) { /* Remove the cluster chain if exist */ - res = remove_chain(&fp->obj, cl, 0); - fs->last_clst = cl - 1; /* Reuse the cluster hole */ + if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ } } else #endif @@ -3467,7 +3648,7 @@ FRESULT f_open ( fp->dir_ptr = dj.dir; #if FF_FS_LOCK != 0 fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */ - if (!fp->obj.lockid) res = FR_INT_ERR; + if (fp->obj.lockid == 0) res = FR_INT_ERR; #endif } #else /* R/O configuration */ @@ -3488,10 +3669,7 @@ FRESULT f_open ( fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; fp->obj.c_ofs = dj.blk_ofs; - fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ - fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); - fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; - fp->obj.n_frag = 0; + init_alloc_info(fs, &fp->obj); } else #endif { @@ -3992,7 +4170,7 @@ FRESULT f_getcwd ( res = dir_sdi(&dj, 0); if (res != FR_OK) break; do { /* Find the entry links to the child directory */ - res = dir_read(&dj, 0); + res = dir_read_file(&dj); if (res != FR_OK) break; if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ res = dir_next(&dj, 0); @@ -4226,9 +4404,7 @@ FRESULT f_opendir ( dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */ dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; dp->obj.c_ofs = dp->blk_ofs; - dp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ - dp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); - dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + init_alloc_info(fs, &dp->obj); /* Get object allocation info */ } else #endif { @@ -4314,7 +4490,7 @@ FRESULT f_readdir ( res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { INIT_NAMBUF(fs); - res = dir_read(dp, 0); /* Read an item */ + res = dir_read_file(dp); /* Read an item */ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ if (res == FR_OK) { /* A valid entry is found */ get_fileinfo(dp, fno); /* Get the object information */ @@ -4422,7 +4598,7 @@ FRESULT f_stat ( /*-----------------------------------------------------------------------*/ FRESULT f_getfree ( - const TCHAR* path, /* Path name of the logical drive number */ + const TCHAR* path, /* Logical drive number */ DWORD* nclst, /* Pointer to a variable to return number of free clusters */ FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ ) @@ -4598,9 +4774,8 @@ FRESULT f_unlink ( #if FF_FS_EXFAT obj.fs = fs; if (fs->fs_type == FS_EXFAT) { - obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); - obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); - obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + init_alloc_info(fs, &obj); + dclst = obj.sclust; } else #endif { @@ -4623,7 +4798,7 @@ FRESULT f_unlink ( #endif res = dir_sdi(&sdj, 0); if (res == FR_OK) { - res = dir_read(&sdj, 0); /* Read an item */ + res = dir_read_file(&sdj); /* Test if the directory is empty */ if (res == FR_OK) res = FR_DENIED; /* Not empty? */ if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ } @@ -4632,7 +4807,7 @@ FRESULT f_unlink ( } if (res == FR_OK) { res = dir_remove(&dj); /* Remove the directory entry */ - if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ + if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */ #if FF_FS_EXFAT res = remove_chain(&obj, dclst, 0); #else @@ -4948,18 +5123,16 @@ FRESULT f_utime ( /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( - const TCHAR* path, /* Path name of the logical drive number */ - TCHAR* label, /* Pointer to a buffer to store the volume label */ - DWORD* vsn /* Pointer to a variable to store the volume serial number */ + const TCHAR* path, /* Logical drive number */ + TCHAR* label, /* Buffer to store the volume label */ + DWORD* vsn /* Variable to store the volume serial number */ ) { FRESULT res; DIR dj; FATFS *fs; UINT si, di; -#if (FF_LFN_UNICODE && FF_USE_LFN) || FF_FS_EXFAT - WCHAR w; -#endif + WCHAR wc; /* Get logical drive */ res = find_volume(&path, &fs, 0); @@ -4969,35 +5142,40 @@ FRESULT f_getlabel ( dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { - res = dir_read(&dj, 1); /* Find a volume label entry */ + res = dir_read_label(&dj); /* Find a volume label entry */ if (res == FR_OK) { #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { - for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ - w = ld_word(dj.dir + XDIR_Label + si * 2); -#if !FF_LFN_UNICODE /* ANSI/OEM API */ - w = ff_uni2oem(w, CODEPAGE); /* Unicode -> OEM */ - if (w == 0) w = '?'; /* Replace wrong char with '?' */ - if (w >= 0x100) label[di++] = (char)(w >> 8); -#endif - label[di++] = (TCHAR)w; + WCHAR hs; + + for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + wc = ld_word(dj.dir + XDIR_Label + si * 2); + if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ + hs = wc; continue; + } + wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; + hs = 0; } + if (hs != 0) di = 0; /* Broken surrogate pair? */ label[di] = 0; } else #endif { - si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ - do { -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */ - w = (si < 11) ? dj.dir[si++] : ' '; - if (dbc_1st((BYTE)w) && si < 11 && dbc_2nd(dj.dir[si])) { - w = w << 8 | dj.dir[si++]; - } - label[di++] = ff_oem2uni(w, CODEPAGE); /* OEM -> Unicode */ -#else /* ANSI/OEM API */ - label[di++] = dj.dir[si++]; + si = di = 0; /* Extract volume label from AM_VOL entry */ + while (si < 11) { + wc = dj.dir[si++]; +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ + wc = ff_oem2uni(wc, CODEPAGE); + if (wc != 0) wc = put_utf(wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; +#else /* ANSI/OEM output */ + label[di++] = (TCHAR)wc; #endif - } while (di < 11); + } do { /* Truncate trailing spaces */ label[di] = 0; if (di == 0) break; @@ -5040,89 +5218,82 @@ FRESULT f_getlabel ( /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( - const TCHAR* label /* Pointer to the volume label to set */ + const TCHAR* label /* Volume label to set with heading logical drive number */ ) { FRESULT res; DIR dj; FATFS *fs; BYTE dirvn[22]; - UINT i, j, slen; - WCHAR w; - static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; - + UINT di; + WCHAR wc; + static const char badchr[] = "+.,;=[]\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ +#if FF_USE_LFN + DWORD dc; +#endif /* Get logical drive */ res = find_volume(&label, &fs, FA_WRITE); if (res != FR_OK) LEAVE_FF(fs, res); - dj.obj.fs = fs; - - /* Get length of given volume label */ - for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ - w = label[i++]; -#if !FF_LFN_UNICODE /* ANSI/OEM API */ - if (dbc_1st((BYTE)w)) { - w = (i < slen && dbc_2nd((BYTE)label[i])) ? w << 8 | (BYTE)label[i++] : 0; + mem_set(dirvn, 0, 22); + di = 0; + while (*label) { /* Create volume label in directory form */ + dc = tchar2uni(&label); /* Get a Unicode character */ + if (dc >= 0x10000) { + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */ + dc = 0; + } else { + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; + } } - w = ff_oem2uni(w, CODEPAGE); -#endif - if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } - st_word(dirvn + j, w); j += 2; + st_word(dirvn + di * 2, (WCHAR)dc); di++; } - slen = j; } else #endif { /* On the FAT/FAT32 volume */ - for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ - if (slen != 0) { /* Is there a volume label to be set? */ - dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ - do { -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */ - w = ff_uni2oem(ff_wtoupper(label[i++]), CODEPAGE); -#else /* ANSI/OEM API */ - w = (BYTE)label[i++]; - if (dbc_1st((BYTE)w)) { - w = (j < 10 && i < slen && dbc_2nd((BYTE)label[i])) ? w << 8 | (BYTE)label[i++] : 0; - } + mem_set(dirvn, ' ', 11); + di = 0; + while (*label) { /* Create volume label in directory form */ #if FF_USE_LFN - w = ff_uni2oem(ff_wtoupper(ff_oem2uni(w, CODEPAGE)), CODEPAGE); -#else - if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ + dc = tchar2uni(&label); + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0; +#else /* ANSI/OEM input */ + wc = (BYTE)*label++; + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0; + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */ #if FF_CODE_PAGE == 0 - if (ExCvt && w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ #elif FF_CODE_PAGE < 900 - if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ #endif #endif -#endif - if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ - LEAVE_FF(fs, FR_INVALID_NAME); - } - if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); - dirvn[j++] = (BYTE)w; - } while (i < slen); - while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ - if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); + dirvn[di++] = (BYTE)wc; } + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */ } /* Set volume label */ - dj.obj.sclust = 0; /* Open root directory */ + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { - res = dir_read(&dj, 1); /* Get volume label entry */ + res = dir_read_label(&dj); /* Get volume label entry */ if (res == FR_OK) { if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { - dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ - mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); } else { - if (slen != 0) { + if (di != 0) { mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ } else { dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ @@ -5133,14 +5304,14 @@ FRESULT f_setlabel ( } else { /* No volume label entry or an error */ if (res == FR_NO_FILE) { res = FR_OK; - if (slen != 0) { /* Create a volume label entry */ + if (di != 0) { /* Create a volume label entry */ res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { - mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ - dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); - mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + dj.dir[XDIR_NumLabel] = (BYTE)di; + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ mem_cpy(dj.dir, dirvn, 11); @@ -5331,8 +5502,8 @@ FRESULT f_mkfs ( const TCHAR* path, /* Logical drive number */ BYTE opt, /* Format option */ DWORD au, /* Size of allocation unit (cluster) [byte] */ - void* work, /* Pointer to working buffer */ - UINT len /* Size of working buffer */ + void* work, /* Pointer to working buffer (null: use heap memory) */ + UINT len /* Size of working buffer [byte] */ ) { const UINT n_fats = 1; /* Number of FATs for FAT/FAT32 volume (1 or 2) */ @@ -5340,7 +5511,7 @@ FRESULT f_mkfs ( static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ BYTE fmt, sys, *buf, *pte, pdrv, part; - WORD ss; + WORD ss; /* Sector size */ DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ @@ -5355,7 +5526,7 @@ FRESULT f_mkfs ( /* Check mounted drive and clear work area */ vol = get_ldnumber(&path); /* Get target logical drive */ if (vol < 0) return FR_INVALID_DRIVE; - if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume */ + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume if mounted */ pdrv = LD2PD(vol); /* Physical drive */ part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ @@ -5374,28 +5545,36 @@ FRESULT f_mkfs ( au /= ss; /* Cluster size in unit of sector */ /* Get working buffer */ - buf = (BYTE*)work; /* Working buffer */ - sz_buf = len / ss; /* Size of working buffer (sector) */ - szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ - if (szb_buf == 0) return FR_MKFS_ABORTED; +#if FF_USE_LFN == 3 + if (!work) { /* Use heap memory for working buffer */ + for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && !(buf = ff_memalloc(szb_buf)); szb_buf /= 2) ; + sz_buf = szb_buf / ss; /* Size of working buffer (sector) */ + } else +#endif + { + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + } + if (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE; /* Determine where the volume to be located (b_vol, sz_vol) */ if (FF_MULTI_PARTITION && part != 0) { /* Get partition information from partition table in the MBR */ - if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ - if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ pte = buf + (MBR_Table + (part - 1) * SZ_PTE); - if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ } else { /* Create a single-partition in this function */ - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ - if (sz_vol < b_vol) return FR_MKFS_ABORTED; + if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); sz_vol -= b_vol; /* Volume size */ } - if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ /* Pre-determine the FAT type */ do { @@ -5404,13 +5583,13 @@ FRESULT f_mkfs ( fmt = FS_EXFAT; break; } } - if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */ if (opt & FM_FAT32) { /* FAT32 possible? */ if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ fmt = FS_FAT32; break; } } - if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + if (!(opt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */ fmt = FS_FAT16; } while (0); @@ -5421,7 +5600,7 @@ FRESULT f_mkfs ( UINT j, st; BYTE b; - if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ #if FF_USE_TRIM tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */ disk_ioctl(pdrv, CTRL_TRIM, tbl); @@ -5435,10 +5614,10 @@ FRESULT f_mkfs ( b_fat = b_vol + 32; /* FAT start at offset 32 */ sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ - if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + if (b_data >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ - if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ - if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ @@ -5450,7 +5629,7 @@ FRESULT f_mkfs ( do { switch (st) { case 0: - ch = ff_wtoupper(si); /* Get an up-case char */ + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */ if (ch != si) { si++; break; /* Store the up-case char if exist */ } @@ -5472,9 +5651,9 @@ FRESULT f_mkfs ( sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); i += 2; szb_case += 2; - if (si == 0|| i == szb_buf) { /* Write buffered data when buffer full or end of process */ + if (si == 0 || i == szb_buf) { /* Write buffered data when buffer full or end of process */ n = (i + ss - 1) / ss; - if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; i = 0; } } while (si); @@ -5489,7 +5668,7 @@ FRESULT f_mkfs ( for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ - if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; } while (nsect); @@ -5510,7 +5689,7 @@ FRESULT f_mkfs ( if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */ } while (nb != 0 && i < szb_buf); n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ - if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; } while (nsect); @@ -5527,7 +5706,7 @@ FRESULT f_mkfs ( sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ do { /* Fill root directory sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; - if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); mem_set(buf, 0, ss); sect += n; nsect -= n; } while (nsect); @@ -5556,23 +5735,23 @@ FRESULT f_mkfs ( for (i = sum = 0; i < ss; i++) { /* VBR checksum */ if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); } - if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Extended bootstrap record (+1..+8) */ mem_set(buf, 0, ss); st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ for (j = 1; j < 9; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ - if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); } /* OEM/Reserved record (+9..+10) */ mem_set(buf, 0, ss); for ( ; j < 11; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ - if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); } /* Sum record (+11) */ for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ - if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); } } else @@ -5590,7 +5769,7 @@ FRESULT f_mkfs ( sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ sz_rsv = 32; /* Number of reserved sectors */ sz_dir = 0; /* No static directory */ - if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED); } else { /* FAT volume */ if (pau == 0) { /* au auto-selection */ n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ @@ -5619,12 +5798,12 @@ FRESULT f_mkfs ( } /* Determine number of clusters and final check of validity of the FAT sub-type */ - if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */ n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; if (fmt == FS_FAT32) { if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ if (au == 0 && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ - return FR_MKFS_ABORTED; + LEAVE_MKFS(FR_MKFS_ABORTED); } } if (fmt == FS_FAT16) { @@ -5636,14 +5815,14 @@ FRESULT f_mkfs ( fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ } if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ - return FR_MKFS_ABORTED; + LEAVE_MKFS(FR_MKFS_ABORTED); } if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ - return FR_MKFS_ABORTED; + LEAVE_MKFS(FR_MKFS_ABORTED); } } - if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ /* Ok, it is the valid cluster configuration */ break; @@ -5687,7 +5866,7 @@ FRESULT f_mkfs ( mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ } st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ - if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ /* Create FSINFO record if needed */ if (fmt == FS_FAT32) { @@ -5716,7 +5895,7 @@ FRESULT f_mkfs ( nsect = sz_fat; /* Number of FAT sectors */ do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; - if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); mem_set(buf, 0, ss); sect += n; nsect -= n; } while (nsect); @@ -5726,7 +5905,7 @@ FRESULT f_mkfs ( nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ do { n = (nsect > sz_buf) ? sz_buf : nsect; - if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; } while (nsect); } @@ -5749,9 +5928,9 @@ FRESULT f_mkfs ( /* Update partition information */ if (FF_MULTI_PARTITION && part != 0) { /* Created in the existing partition */ /* Update system ID in the partition table */ - if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ - if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ } else { /* Created as a new single partition */ if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ mem_set(buf, 0, ss); @@ -5764,17 +5943,17 @@ FRESULT f_mkfs ( pte[PTE_System] = sys; /* System type */ n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */ pte[PTE_EdHead] = 254; /* End head */ - pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdSec] = (BYTE)(((n >> 2) & 0xC0) | 63); /* End sector */ pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ - if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */ } } - if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); - return FR_OK; + LEAVE_MKFS(FR_OK); } @@ -5787,13 +5966,14 @@ FRESULT f_mkfs ( FRESULT f_fdisk ( BYTE pdrv, /* Physical drive number */ const DWORD* szt, /* Pointer to the size table for each partitions */ - void* work /* Pointer to the working buffer */ + void* work /* Pointer to the working buffer (null: use heap memory) */ ) { UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; - BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + BYTE s_hd, e_hd, *p, *buf; = (BYTE*)work; DSTATUS stat; DWORD sz_disk, sz_part, s_part; + FRESULT res; stat = disk_initialize(pdrv); @@ -5801,6 +5981,12 @@ FRESULT f_fdisk ( if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + buf = (BYTE*)work; +#if FF_USE_LFN == 3 + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */ +#endif + if (!buf) return FR_NOT_ENOUGH_CORE; + /* Determine the CHS without any consideration of the drive geometry */ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; if (n == 256) n--; @@ -5823,15 +6009,15 @@ FRESULT f_fdisk ( s_hd = 0; } e_cyl = b_cyl + p_cyl - 1; /* End cylinder */ - if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + if (e_cyl >= tot_cyl) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Set partition table */ p[1] = s_hd; /* Start head */ - p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */ p[3] = (BYTE)b_cyl; /* Start cylinder */ p[4] = 0x07; /* System type (temporary setting) */ p[5] = e_hd; /* End head */ - p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */ p[7] = (BYTE)e_cyl; /* End cylinder */ st_dword(p + 8, s_part); /* Start sector in LBA */ st_dword(p + 12, sz_part); /* Number of sectors */ @@ -5839,10 +6025,11 @@ FRESULT f_fdisk ( /* Next partition */ b_cyl += p_cyl; } - st_word(p, 0xAA55); + st_word(p, 0xAA55); /* MBR signature (always at offset 510) */ /* Write it to the MBR */ - return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; + res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + LEAVE_MKFS(res); } #endif /* FF_MULTI_PARTITION */ @@ -5852,78 +6039,147 @@ FRESULT f_fdisk ( #if FF_USE_STRFUNC +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3) +#error Wrong FF_STRF_ENCODE setting +#endif /*-----------------------------------------------------------------------*/ /* Get a String from the File */ /*-----------------------------------------------------------------------*/ TCHAR* f_gets ( TCHAR* buff, /* Pointer to the string buffer to read */ - int len, /* Size of string buffer (characters) */ + int len, /* Size of string buffer (items) */ FIL* fp /* Pointer to the file object */ ) { - int n = 0; - TCHAR c, *p = buff; + int nc = 0; + TCHAR *p = buff; BYTE s[2]; UINT rc; + WCHAR wc; +#if FF_USE_LFN && ((FF_LFN_UNICODE == 1 && FF_STRF_ENCODE == 3) || (FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3)) + DWORD dc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE == 1 && FF_STRF_ENCODE == 3 + UINT ct; +#endif - - while (n < len - 1) { /* Read characters until buffer gets filled */ -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */ -#if FF_STRF_ENCODE == 3 /* Read a character in UTF-8 */ +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* UTF-16 output */ +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + while (nc < len - 1) { f_read(fp, s, 1, &rc); if (rc != 1) break; - c = s[0]; - if (c >= 0x80) { - if (c < 0xC0) continue; /* Skip stray trailer */ - if (c < 0xE0) { /* Two-byte sequence (0x80-0x7FF) */ + wc = s[0]; + if (dbc_1st((BYTE)wc)) { + f_read(fp, s, 1, &rc); + if (rc != 1 || !dbc_2nd(s[0])) continue; + wc = wc << 8 | s[0]; + } + wc = ff_oem2uni(wc, CODEPAGE); + if (wc == 0) continue; +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ + while (nc < len - 1) { + f_read(fp, s, 2, &rc); + if (rc != 2) break; + wc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; +#elif FF_STRF_ENCODE == 3 /* Read a character in UTF-8 */ + while (nc < len - 2) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + dc = s[0]; + if (dc >= 0x80) { + ct = 0; + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } + if (ct == 0) continue; + do { f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = (c & 0x1F) << 6 | (s[0] & 0x3F); - if (c < 0x80) c = '?'; /* Reject invalid code range */ + if (rc != 1 || (s[0] & 0xC0) != 0x80) break; + dc = dc << 6 | (s[0] & 0x3F); + } while (--ct); + if (ct || dc < 0x80 || dc >= 0x110000) continue; + } + if (dc >= 0x10000) { + wc = (WCHAR)(0xD800 | ((dc >> 10) - 0x40)); + *p++ = wc; nc++; + wc = (WCHAR)(0xDC00 | (dc & 0x3FF)); + } else { + wc = (WCHAR)dc; + } +#endif + /* Output it in UTF-16 encoding */ + if (FF_USE_STRFUNC == 2 && wc == '\r') continue; + *p++ = wc; nc++; + if (wc == '\n') break; + } + +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3 /* UTF-8 output */ + while (nc < len - 4) { +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (dbc_1st((BYTE)wc)) { + f_read(fp, s, 1, &rc); + if (rc != 1 || !dbc_2nd(s[0])) continue; + wc = wc << 8 | s[0]; + } + dc = ff_oem2uni(wc, CODEPAGE); + if (dc == 0) continue; +#else /* Read a character in UTF-16LE/BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + dc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; + if (IsSurrogate(dc)) { + f_read(fp, s, 2, &rc); + if (rc != 2) break; + wc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; + if (!IsSurrogateH(dc) || !IsSurrogateL(wc)) continue; + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); + } +#endif + /* Output it in UTF-8 encoding */ + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; + if (dc < 0x80) { /* 1-byte */ + *p++ = (TCHAR)dc; + nc++; + if (dc == '\n') break; + } else { + if (dc < 0x800) { /* 2-byte */ + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 2; } else { - if (c < 0xF0) { /* Three-byte sequence (0x800-0xFFFF) */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); - if (c < 0x800) c = '?'; /* Reject invalid code range */ - } else { /* Reject four-byte sequence */ - c = '?'; + if (dc < 0x10000) { /* 3-byte */ + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 3; + } else { /* 4-byte */ + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 4; } } } -#elif FF_STRF_ENCODE == 2 /* Read a character in UTF-16BE */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = s[1] + (s[0] << 8); -#elif FF_STRF_ENCODE == 1 /* Read a character in UTF-16LE */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = s[0] + (s[1] << 8); -#else /* Read a character in ANSI/OEM */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = s[0]; - if (dbc_1st((BYTE)c)) { - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = (c << 8) + s[0]; - } - c = ff_oem2uni(c, CODEPAGE); /* OEM -> Unicode */ - if (!c) c = '?'; -#endif -#else /* ANSI/OEM API: Read a character without conversion */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = s[0]; -#endif - if (FF_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ - *p++ = c; - n++; - if (c == '\n') break; /* Break on EOL */ } + +#else /* Byte-by-byte without any conversion (ANSI/OEM API or UTF-8 to UTF-8) */ + while (nc < len - 1) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (FF_USE_STRFUNC == 2 && wc == '\r') continue; + *p++ = (TCHAR)wc; nc++; + if (wc == '\n') break; + } +#endif + *p = 0; - return n ? buff : 0; /* When no data read (eof or error), return with error. */ + return nc ? buff : 0; /* When no data read (EOF or error), return with error. */ } @@ -5935,9 +6191,15 @@ TCHAR* f_gets ( /* Put a Character to the File */ /*-----------------------------------------------------------------------*/ -typedef struct { +typedef struct { /* Putchar output buffer and work area */ FIL *fp; /* Ptr to the writing file */ - int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */ +#if FF_USE_LFN && FF_LFN_UNICODE == 1 + WCHAR hs; +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 + BYTE bs[4]; + UINT wi, ct; +#endif BYTE buf[64]; /* Write buffer */ } putbuff; @@ -5948,53 +6210,138 @@ void putc_bfd ( /* Buffered write with code conversion */ TCHAR c ) { - UINT bw; - int i; - + UINT n; + int i, nc; +#if FF_USE_LFN && (FF_LFN_UNICODE == 1 || (FF_LFN_UNICODE == 2 && (FF_STRF_ENCODE != 3))) + WCHAR hs, wc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3 + DWORD dc; + TCHAR *tp; +#endif if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ putc_bfd(pb, '\r'); } - i = pb->idx; /* Write index of pb->buf[] */ + i = pb->idx; /* Write index of pb->buf[] */ if (i < 0) return; + nc = pb->nchr; /* Write unit count */ -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */ -#if FF_STRF_ENCODE == 3 /* Write a character in UTF-8 */ - if (c < 0x80) { /* 7-bit */ - pb->buf[i++] = (BYTE)c; - } else { - if (c < 0x800) { /* 11-bit */ - pb->buf[i++] = (BYTE)(0xC0 | c >> 6); - } else { /* 16-bit */ - pb->buf[i++] = (BYTE)(0xE0 | c >> 12); - pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); - } - pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* UTF-16 input */ + if (IsSurrogateH(c)) { + pb->hs = c; return; + } + wc = c; hs = pb->hs; pb->hs = 0; + if (hs != 0) { + if (!IsSurrogateL(wc)) hs = 0; + } else { + if (IsSurrogateL(wc)) return; + } +#if FF_STRF_ENCODE == 3 /* Write it in UTF-8 */ + if (hs != 0) { /* 4-byte */ + nc += 4; + hs = (hs & 0x3FF) + 0x40; + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } else { + if (wc < 0x80) { /* 1-byte */ + nc++; + pb->buf[i++] = (BYTE)wc; + } else { + if (wc < 0x800) { /* 2-byte */ + nc += 2; + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); + } else { /* 3-byte */ + nc += 3; + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } } -#elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */ - pb->buf[i++] = (BYTE)(c >> 8); - pb->buf[i++] = (BYTE)c; -#elif FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */ - pb->buf[i++] = (BYTE)c; - pb->buf[i++] = (BYTE)(c >> 8); -#else /* Write a character in ANSI/OEM */ - c = ff_uni2oem(c, CODEPAGE); /* Unicode -> OEM */ - if (!c) c = '?'; - if (c >= 0x100) - pb->buf[i++] = (BYTE)(c >> 8); - pb->buf[i++] = (BYTE)c; #endif -#else /* ANSI/OEM API: Write a character without conversion */ - pb->buf[i++] = (BYTE)c; +#else /* UTF-8 input */ + for (;;) { + if (pb->ct == 0) { /* Out of multi-byte sequence? */ + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ + if ((BYTE)c < 0x80) break; /* 1-byte? */ + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */ + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */ + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */ + return; + } else { /* In the multi-byte sequence */ + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ + pb->ct = 0; continue; + } + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ + if (--pb->ct == 0) break; /* End of sequence? */ + return; + } + } +#if FF_STRF_ENCODE == 3 /* Write it in UTF-8 */ + pb->buf[i++] = pb->bs[0]; nc++; + if (pb->bs[0] >= 0xC0) { + pb->buf[i++] = pb->bs[1]; nc++; + } + if (pb->bs[0] >= 0xE0) { + pb->buf[i++] = pb->bs[2]; nc++; + } + if (pb->bs[0] >= 0xF0) { + pb->buf[i++] = pb->bs[3]; nc++; + } +#else /* Write it in UTF-16 or ANSI/OEM */ + tp = (TCHAR*)pb->bs; + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ + if (dc == 0xFFFFFFFF) return; + wc = (WCHAR)dc; + hs = (WCHAR)(dc >> 16); +#endif +#endif +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 && FF_STRF_ENCODE != 3 +#if FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + if (hs != 0) { + pb->buf[i++] = (BYTE)(hs >> 8); + pb->buf[i++] = (BYTE)hs; + nc++; + } + pb->buf[i++] = (BYTE)(wc >> 8); + pb->buf[i++] = (BYTE)wc; + nc++; +#elif FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + if (hs != 0) { + pb->buf[i++] = (BYTE)hs; + pb->buf[i++] = (BYTE)(hs >> 8); + nc++; + } + pb->buf[i++] = (BYTE)wc; + pb->buf[i++] = (BYTE)(wc >> 8); + nc++; +#else /* Write a character in ANSI/OEM */ + if (hs != 0) return; + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ + if (wc == 0) return;; + if (wc >= 0x100) { + pb->buf[i++] = (BYTE)(wc >> 8); nc++; + } + pb->buf[i++] = (BYTE)wc; nc++; +#endif #endif - if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ - f_write(pb->fp, pb->buf, (UINT)i, &bw); - i = (bw == (UINT)i) ? 0 : -1; +#else /* ANSI/OEM input */ + pb->buf[i++] = (BYTE)c; + nc++; +#endif + + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &n); + i = (n == (UINT)i) ? 0 : -1; } pb->idx = i; - pb->nchr++; + pb->nchr = nc; } @@ -6018,8 +6365,8 @@ void putc_init ( /* Initialize write buffer */ FIL* fp ) { + mem_set(pb, 0, sizeof (putbuff)); pb->fp = fp; - pb->nchr = pb->idx = 0; } @@ -6098,41 +6445,46 @@ int f_printf ( f = 2; c = *fmt++; } } - while (IsDigit(c)) { /* Precision */ - w = w * 10 + c - '0'; + if (c == '*') { /* Minimum width by argument */ + w = va_arg(arp, int); c = *fmt++; + } else { + while (IsDigit(c)) { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } } - if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ f |= 4; c = *fmt++; } - if (!c) break; + if (c == 0) break; d = c; if (IsLower(d)) d -= 0x20; - switch (d) { /* Type is... */ + switch (d) { /* Atgument type is... */ case 'S' : /* String */ p = va_arg(arp, TCHAR*); for (j = 0; p[j]; j++) ; - if (!(f & 2)) { /* Right pad */ - while (j++ < w) putc_bfd(&pb, ' '); + if (!(f & 2)) { /* Right padded */ + while (j++ < w) putc_bfd(&pb, ' ') ; } - while (*p) putc_bfd(&pb, *p++); /* String body */ - while (j++ < w) putc_bfd(&pb, ' '); /* Left pad */ + while (*p) putc_bfd(&pb, *p++) ; /* String body */ + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ continue; case 'C' : /* Character */ putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; - case 'B' : /* Binary */ + case 'B' : /* Unsigned binary */ r = 2; break; - case 'O' : /* Octal */ + case 'O' : /* Unsigned octal */ r = 8; break; case 'D' : /* Signed decimal */ case 'U' : /* Unsigned decimal */ r = 10; break; - case 'X' : /* Hexdecimal */ + case 'X' : /* Unsigned hexdecimal */ r = 16; break; default: /* Unknown type (pass-through) */ @@ -6187,7 +6539,7 @@ FRESULT f_setcp ( for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */ - if (validcp[i] != cp) return FR_INVALID_PARAMETER; + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */ CodePage = cp; if (cp >= 900) { /* DBCS */ @@ -6201,4 +6553,3 @@ FRESULT f_setcp ( } #endif /* FF_CODE_PAGE == 0 */ - diff --git a/source/ff.h b/source/ff.h index 524217a..da57ca8 100644 --- a/source/ff.h +++ b/source/ff.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem module R0.13 / +/ FatFs - Generic FAT Filesystem module R0.13a / /-----------------------------------------------------------------------------/ / / Copyright (C) 2017, ChaN, all right reserved. @@ -20,7 +20,7 @@ #ifndef FF_DEFINED -#define FF_DEFINED 87030 /* Revision ID */ +#define FF_DEFINED 89352 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -49,20 +49,25 @@ extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ /* Type of path name strings on FatFs API */ -#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode (UTF-16) string */ #ifndef _INC_TCHAR +#define _INC_TCHAR + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ typedef WCHAR TCHAR; #define _T(x) L ## x #define _TEXT(x) L ## x -#define _INC_TCHAR -#endif -#else /* ANSI/OEM string */ -#ifndef _INC_TCHAR +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ +typedef char TCHAR; +#define _T(x) u8 ## x +#define _TEXT(x) u8 ## x +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 2) +#error Wrong FF_LFN_UNICODE setting +#else /* ANSI/OEM code in SBCS/DBCS */ typedef char TCHAR; #define _T(x) x #define _TEXT(x) x -#define _INC_TCHAR #endif + #endif @@ -70,9 +75,6 @@ typedef char TCHAR; /* Type of file size variables */ #if FF_FS_EXFAT -#if !FF_USE_LFN -#error LFN must be enabled when enable exFAT -#endif typedef QWORD FSIZE_t; #else typedef DWORD FSIZE_t; @@ -200,10 +202,10 @@ typedef struct { WORD ftime; /* Modified time */ BYTE fattrib; /* File attribute */ #if FF_USE_LFN - TCHAR altname[13]; /* Altenative file name */ - TCHAR fname[FF_MAX_LFN + 1]; /* Primary file name */ + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ #else - TCHAR fname[13]; /* File name */ + TCHAR fname[12 + 1]; /* File name */ #endif } FILINFO; @@ -299,10 +301,10 @@ DWORD get_fattime (void); #endif /* LFN support functions */ -#if FF_USE_LFN /* Code conversion (defined in unicode.c) */ +#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ -WCHAR ff_uni2oem (WCHAR uni, WORD cp); /* Unicode to OEM code conversion */ -WCHAR ff_wtoupper (WCHAR uni); /* Unicode upper-case conversion */ +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ #endif #if FF_USE_LFN == 3 /* Dynamic memory allocation */ void* ff_memalloc (UINT msize); /* Allocate memory block */ diff --git a/source/ffconf.h b/source/ffconf.h index a851024..567ddf5 100644 --- a/source/ffconf.h +++ b/source/ffconf.h @@ -2,7 +2,7 @@ / FatFs - Configuration file /---------------------------------------------------------------------------*/ -#define FFCONF_DEF 87030 /* Revision ID */ +#define FFCONF_DEF 89352 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -18,7 +18,7 @@ #define FF_FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / -/ 0: All basic functions are enabled. +/ 0: Basic functions are fully enabled. / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() / are removed. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. @@ -106,32 +106,46 @@ / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. / -/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added -/ to the project. The working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and -/ additional 608 bytes at exFAT enabled. FF_MAX_LFN can be in range from 12 to 255. -/ It should be set 255 to support full featured LFN operations. +/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can +/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN +/ specification. / When use stack for the working buffer, take care on stack overflow. When use heap / memory for the working buffer, memory management functions, ff_memalloc() and -/ ff_memfree(), must be added to the project. */ +/ ff_memfree() in ffsystem.c, need to be added to the project. */ #define FF_LFN_UNICODE 0 -/* This option switches character encoding on the API, 0:ANSI/OEM or 1:UTF-16, -/ when LFN is enabled. Also behavior of string I/O functions will be affected by -/ this option. When LFN is not enabled, this option has no effect. -*/ +/* This option switches the character encoding on the API when LFN is enabled. +/ +/ 0: ANSI/OEM in current CP (TCHAR = char) +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) +/ 2: Unicode in UTF-8 (TCHAR = char) +/ +/ Also behavior of string I/O functions will be affected by this option. +/ When LFN is not enabled, this option has no effect. */ + + +#define FF_LFN_BUF 255 +#define FF_SFN_BUF 12 +/* This set of options defines size of file name members in the FILINFO structure +/ which is used to read out directory items. These values should be suffcient for +/ the file names to read. The maximum possible length of the read file name depends +/ on character encoding. When LFN is not enabled, these options have no effect. */ #define FF_STRF_ENCODE 3 -/* When FF_LFN_UNICODE = 1 with LFN enabled, string I/O functions, f_gets(), +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), / f_putc(), f_puts and f_printf() convert the character encoding in it. / This option selects assumption of character encoding ON THE FILE to be / read/written via those functions. / -/ 0: ANSI/OEM -/ 1: UTF-16LE -/ 2: UTF-16BE -/ 3: UTF-8 +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 */ @@ -217,7 +231,7 @@ #define FF_FS_NORTC 0 -#define FF_NORTC_MON 5 +#define FF_NORTC_MON 1 #define FF_NORTC_MDAY 1 #define FF_NORTC_YEAR 2017 /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have diff --git a/source/ffsystem.c b/source/ffsystem.c index 59a796c..b0170a8 100644 --- a/source/ffsystem.c +++ b/source/ffsystem.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------*/ -/* Sample code of OS dependent controls for FatFs */ +/* Sample Code of OS Dependent Functions for FatFs */ /* (C)ChaN, 2017 */ /*------------------------------------------------------------------------*/ @@ -27,7 +27,7 @@ void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on no /*------------------------------------------------------------------------*/ void ff_memfree ( - void* mblock /* Pointer to the memory block to free */ + void* mblock /* Pointer to the memory block to free (nothing to do for null) */ ) { free(mblock); /* Free the memory block with POSIX API */ @@ -40,7 +40,7 @@ void ff_memfree ( #if FF_FS_REENTRANT /* Mutal exclusion */ /*------------------------------------------------------------------------*/ -/* Create a Synchronization Object +/* Create a Synchronization Object */ /*------------------------------------------------------------------------*/ /* This function is called in f_mount() function to create a new / synchronization object for the volume, such as semaphore and mutex. diff --git a/source/ffunicode.c b/source/ffunicode.c index cf50e8c..901affe 100644 --- a/source/ffunicode.c +++ b/source/ffunicode.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------*/ -/* Unicode handling functions for FatFs R0.13+ */ +/* Unicode handling functions for FatFs R0.13a */ /*------------------------------------------------------------------------*/ /* This module will occupy a huge memory in the .const section when the / / FatFs is configured for LFN with DBCS. If the system has any Unicode / @@ -25,7 +25,11 @@ #include "ff.h" -#if FF_USE_LFN +#if FF_USE_LFN /* This module is blanked when non-LFN configuration */ + +#if FF_DEFINED != 89352 /* Revision ID */ +#error Wrong include file (ff.h). +#endif #define MERGE2(a, b) a ## b #define CVTBL(tbl, cp) MERGE2(tbl, cp) @@ -15245,7 +15249,7 @@ const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */ #if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900 WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ - WCHAR uni, /* Unicode character to be converted */ + DWORD uni, /* UTF-16 encoded character to be converted */ WORD cp /* Code page for the conversion */ ) { @@ -15253,15 +15257,16 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); - if (uni < 0x80) { /* ASCII char */ - c = uni; + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; - } else { /* Non-ASCII char */ - if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */ + } else { /* Non-ASCII */ + if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it a valid code? */ for (c = 0; c < 0x80 && uni != p[c]; c++) ; c = (c + 0x80) & 0xFF; } } + return c; } @@ -15274,7 +15279,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); - if (oem < 0x80) { /* ASCII char */ + if (oem < 0x80) { /* ASCII? */ c = oem; } else { /* Extended char */ @@ -15282,6 +15287,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ if (oem < 0x100) c = p[oem - 0x80]; } } + return c; } @@ -15294,37 +15300,41 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ /* DBCS fixed code page */ /*------------------------------------------------------------------------*/ -#if FF_CODE_PAGE != 0 && FF_CODE_PAGE >= 900 +#if FF_CODE_PAGE >= 900 WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ - WCHAR uni, /* Unicode character to be converted */ + DWORD uni, /* UTF-16 encoded character to be converted */ WORD cp /* Code page for the conversion */ ) { const WCHAR *p; - WCHAR c = 0; + WCHAR c = 0, uc; UINT i, n, li, hi; - if (uni < 0x80) { /* ASCII char */ - c = uni; + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; - } else { /* Non-ASCII char */ - if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */ - p = CVTBL(uni2oem, FF_CODE_PAGE); - hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1; - li = 0; - for (n = 16; n; n--) { - i = li + (hi - li) / 2; - if (uni == p[i * 2]) break; - if (uni > p[i * 2]) { - li = i; - } else { - hi = i; + } else { /* Non-ASCII */ + if (uni < 0x10000) { /* Is it in BMP? */ + if (cp == FF_CODE_PAGE) { /* Is it a valid code? */ + uc = (WCHAR)uni; + p = CVTBL(uni2oem, FF_CODE_PAGE); + hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1; + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } } + if (n != 0) c = p[i * 2 + 1]; } - if (n != 0) c = p[i * 2 + 1]; } } + return c; } @@ -15339,7 +15349,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ UINT i, n, li, hi; - if (oem < 0x80) { /* ASCII char */ + if (oem < 0x80) { /* ASCII? */ c = oem; } else { /* Extended char */ @@ -15359,6 +15369,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ if (n != 0) c = p[i * 2 + 1]; } } + return c; } #endif @@ -15376,55 +15387,59 @@ static const WCHAR *const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850 WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ - WCHAR uni, /* Unicode character to be converted */ + DWORD uni, /* UTF-16 encoded character to be converted */ WORD cp /* Code page for the conversion */ ) { const WCHAR *p; - WCHAR c = 0; + WCHAR c = 0, uc; UINT i, n, li, hi; - if (uni < 0x80) { /* ASCII char */ - c = uni; + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; - } else { /* Non-ASCII char */ - p = 0; - if (cp < 900) { /* SBCS */ - for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */ - p = cp_table[i]; - if (p) { /* Is it a valid CP ? */ - for (c = 0; c < 0x80 && uni != p[c]; c++) ; /* Find OEM code in the table */ - c = (c + 0x80) & 0xFF; - } - } else { /* DBCS */ - switch (cp) { - case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break; - case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break; - case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break; - case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break; - } - if (p) { /* Is it a valid code page? */ - li = 0; - for (n = 16; n; n--) { /* Find OEM code */ - i = li + (hi - li) / 2; - if (uni == p[i * 2]) break; - if (uni > p[i * 2]) { - li = i; - } else { - hi = i; - } + } else { /* Non-ASCII */ + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WCHAR)uni; + p = 0; + if (cp < 900) { /* SBCS */ + for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */ + p = cp_table[i]; + if (p) { /* Is it a valid CP ? */ + for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */ + c = (c + 0x80) & 0xFF; + } + } else { /* DBCS */ + switch (cp) { + case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break; + case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break; + case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break; + case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break; + } + if (p) { /* Is it a valid code page? */ + li = 0; + for (n = 16; n; n--) { /* Find OEM code */ + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; } - if (n != 0) c = p[i * 2 + 1]; } } } + return c; } WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ - WCHAR oem, /* OEM code to be converted */ + WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */ WORD cp /* Code page for the conversion */ ) { @@ -15433,7 +15448,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ UINT i, n, li, hi; - if (oem < 0x80) { /* ASCII char */ + if (oem < 0x80) { /* ASCII? */ c = oem; } else { /* Extended char */ @@ -15466,6 +15481,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ } } } + return c; } #endif @@ -15476,12 +15492,12 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ /* Unicode up-case conversion */ /*------------------------------------------------------------------------*/ -WCHAR ff_wtoupper ( /* Returns up-converted character */ - WCHAR uni /* Unicode character to be upper converted (BMP only) */ +DWORD ff_wtoupper ( /* Returns up-converted code point */ + DWORD uni /* Unicode code point to be up-converted */ ) { /* Compressed upper conversion table */ - static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + static const WORD cvt1[] = { /* U+0000 - U+0FFF */ /* Basic Latin */ 0x0061,0x031A, /* Latin-1 Supplement */ @@ -15505,7 +15521,7 @@ WCHAR ff_wtoupper ( /* Returns up-converted character */ 0x0000 }; - static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + static const WORD cvt2[] = { /* U+1000 - U+FFFF */ /* Phonetic Extensions */ 0x1D7D,0x0001,0x2C63, /* Latin Extended Additional */ @@ -15533,34 +15549,38 @@ WCHAR ff_wtoupper ( /* Returns up-converted character */ 0x0000 }; - const WCHAR *p; - WCHAR bc, nc, cmd; + const WORD *p; + WORD uc, bc, nc, cmd; - p = uni < 0x1000 ? cvt1 : cvt2; - for (;;) { - bc = *p++; /* Get block base */ - if (!bc || uni < bc) break; - nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ - if (uni < bc + nc) { /* In the block? */ - switch (cmd) { - case 0: uni = p[uni - bc]; break; /* Table conversion */ - case 1: uni -= (uni - bc) & 1; break; /* Case pairs */ - case 2: uni -= 16; break; /* Shift -16 */ - case 3: uni -= 32; break; /* Shift -32 */ - case 4: uni -= 48; break; /* Shift -48 */ - case 5: uni -= 26; break; /* Shift -26 */ - case 6: uni += 8; break; /* Shift +8 */ - case 7: uni -= 80; break; /* Shift -80 */ - case 8: uni -= 0x1C60; break; /* Shift -0x1C60 */ + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WORD)uni; + p = uc < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || uc < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (uc < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: uc = p[uc - bc]; break; /* Table conversion */ + case 1: uc -= (uc - bc) & 1; break; /* Case pairs */ + case 2: uc -= 16; break; /* Shift -16 */ + case 3: uc -= 32; break; /* Shift -32 */ + case 4: uc -= 48; break; /* Shift -48 */ + case 5: uc -= 26; break; /* Shift -26 */ + case 6: uc += 8; break; /* Shift +8 */ + case 7: uc -= 80; break; /* Shift -80 */ + case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; } - break; + if (!cmd) p += nc; } - if (!cmd) p += nc; + uni = uc; } return uni; } -#endif /* #if _USE_LFN */ +#endif /* #if FF_USE_LFN */
    ValueMeaning
    0Initial value (non-ASCII character cannot be used)
    0Initial value (any extended character cannot be used)
    437U.S.
    720Arabic
    737Greek