fs/fat: fix incorrect representation of SFN files created on Linux

Linux creates all files as long file name entries even if they
fit short 8.3 format. This caused issues when deleting or renaming
files as the long file name entry was not recognized and only deleted
it's short file name entry. This basically kept breaking the file
system as subsequent long file name files were not correctly stored.
The typical representation of this issue was long file name being
represented as it's short name alias.

This commit adjusts the LFN/SFN logic a bit - the code now always
fills in long file name and then checks if this could possibly be
a short file entry (we still have to do that because Windows stores
8.3 files as short file entry). This ensures compatibility with
files created both on Linux and Windows.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
This commit is contained in:
Michal Lenc
2026-04-09 18:24:21 +02:00
committed by Xiang Xiao
parent 587ca7b537
commit bc9e1ffb01
+58 -18
View File
@@ -369,8 +369,19 @@ static inline int fat_parsesfname(FAR const char **path,
node++;
}
*terminator = ch;
*path = node;
if (terminator != NULL)
{
/* Don't update the variables if terminator is NULL
*
* Call with terminator == NULL is used to obtain short
* file name from already given long file name that fits
* into 8.3 format
*/
*terminator = ch;
*path = node;
}
return OK;
}
@@ -1109,6 +1120,12 @@ static inline int fat_uniquealias(FAR struct fat_mountpt_s *fs,
* Description: Convert a user filename into a properly formatted FAT
* (short 8.3) filename as it would appear in a directory entry.
*
* Long file name is returned if CONFIG_FAT_LFN is defined. This applies
* even for entries that would fit standard short 8.3 format. These have
* both long file name and short file name filled. This is done mainly
* because of compatibility with Linux that creates long file name entries
* even if the file name fits into 8.3 format.
*
****************************************************************************/
static int fat_path2dirname(FAR const char **path,
@@ -1121,17 +1138,35 @@ static int fat_path2dirname(FAR const char **path,
/* Assume no long file name */
dirinfo->fd_lfname[0] = '\0';
dirinfo->fd_name[0] = '\0';
/* Then parse the (assumed) 8+3 short file name */
/* Parse long file name */
ret = fat_parsesfname(path, dirinfo, terminator);
ret = fat_parselfname(path, dirinfo, terminator);
if (ret < 0)
{
/* No, the name is not a valid short 8+3 file name. Try parsing
* the long file name.
return ret;
}
else
{
/* Does the long file name fit into short file name? This means
* this is actually a short file name.
*
* There are following possibilities:
* - file was created on Linux and is written as long file name
* - file was created on Windows and is written as short file name
* - we are creating file on NuttX -> write it as short file name
*/
ret = fat_parselfname(path, dirinfo, terminator);
if (strlen((FAR const char *)dirinfo->fd_lfname) <= DIR_MAXFNAME)
{
/* Get short file name for given path */
char name[DIR_MAXFNAME];
memcpy(name, dirinfo->fd_lfname, DIR_MAXFNAME);
FAR const char *tmp = (FAR const char *)name;
fat_parsesfname(&tmp, dirinfo, NULL);
}
}
return ret;
@@ -2609,20 +2644,25 @@ int fat_finddirentry(FAR struct fat_mountpt_s *fs,
return ret;
}
/* Is this a path segment a long or a short file. Was a long file
* name parsed?
*/
#ifdef CONFIG_FAT_LFN
if (dirinfo->fd_lfname[0] != '\0')
{
/* Yes.. Search for the sequence of long file name directory
* entries. NOTE: As a side effect, this function returns with
* the sector containing the short file name directory entry
* in the cache.
/* We have to check for both LFN and SFN entry because of
* Linux/Windows differences
*
* - file created on Linux is written as LFN even if it fits 8.3
* - file created on Windows is written as SFN if it fits 8.3
*/
struct fat_dirinfo_s d = *dirinfo;
ret = fat_findlfnentry(fs, dirinfo);
if (ret < 0)
{
/* There is no LFN entry for given file -> check SFN */
*dirinfo = d;
ret = fat_findsfnentry(fs, dirinfo);
}
}
else
#endif
@@ -2752,7 +2792,7 @@ int fat_allocatedirentry(FAR struct fat_mountpt_s *fs,
*/
#ifdef CONFIG_FAT_LFN
if (dirinfo->fd_lfname[0] != '\0')
if (dirinfo->fd_lfname[0] != '\0' && dirinfo->fd_name[0] == '\0')
{
/* Yes.. Allocate for the sequence of long file name directory
* entries plus a short file name directory entry.
@@ -3008,7 +3048,7 @@ int fat_dirnamewrite(FAR struct fat_mountpt_s *fs,
/* Is this a long file name? */
if (dirinfo->fd_lfname[0] != '\0')
if (dirinfo->fd_lfname[0] != '\0' && dirinfo->fd_name[0] == '\0')
{
/* Write the sequence of long file name directory entries (this
* function also creates the short file name alias).
@@ -3054,7 +3094,7 @@ int fat_dirwrite(FAR struct fat_mountpt_s *fs,
/* Does this directory entry have a long file name? */
if (dirinfo->fd_lfname[0] != '\0')
if (dirinfo->fd_lfname[0] != '\0' && dirinfo->fd_name[0] == '\0')
{
/* Write the sequence of long file name directory entries (this
* function also creates the short file name alias).