+FAT - Incorrect Volume Label

sonycman wrote on Monday, June 05, 2017:

Hello!
I`am started to using FreeRTOS with +FAT file system and want to thank its authors for their hard work!

And here is an issue I have encountered.
The Volume Label acquired by FF_Mount function in pxIOManager->xPartition.pcVolumeLabel structure member is invalid/outdated.

This label is copied directly from partition sector, but there is another place for it - root directory entry with the attribute ATTR_VOLUME_ID, which holds an actual volume label.

So, in my case (Windows 10), SD card with the label “SD CARD”, has the wrong (outdated) “NO NAME” label reported by +FAT :frowning:

Regards
Vladimir

heinbali01 wrote on Tuesday, June 06, 2017:

Hi Vladimir,

You are right, +FAT only recognises the 11-byte Volume label as stored in the partition table, found at these offsets:

    #define FF_FAT_16_VOL_LABEL    0x02B
    #define FF_FAT_32_VOL_LABEL    0x047

The root directory indeed may contain a Volume label, if it has this file-entry attribute:

    #define FF_FAT_ATTR_VOLID      0x08

The FreeRTOS+FAT library is developed for embedded applications, and the choice was made to ignore these Volume ID entries in the root directory.

But, well, here is a read-only version, that needs a minimum of changes to the source code:

Replace on line in ff_dir.c around line 1799 :

-            else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
+            else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) == FF_FAT_ATTR_VOLID )
+            {
+                #if( ffconfigDIRECTORY_VOLID_SUPPORT != 0 )
+                {
+                const size_t uxLength = sizeof( pxIOManager->pcDirectoryVolID ) - ( size_t )1u;
+
+                    memcpy( pxIOManager->pcDirectoryVolID, pucEntryBuffer, uxLength );
+                    pxIOManager->pcDirectoryVolID[ uxLength ] = '\0';
+                }
+                #endif /* ffconfigDIRECTORY_VOLID_SUPPORT */
+            }
+            else

And add a new member in include\ff_ioman.h, around line 291:

     uint8_t         ucPreventFlush;   /* Flushing to disk only allowed when 0. */
     uint8_t         ucFlags;          /* Bit-Mask: identifying allocated pointers and other flags */
 #if( ffconfigHASH_CACHE != 0 )
     FF_HashTable_t  xHashCache[ ffconfigHASH_CACHE_DEPTH ];
 #endif
+#if( ffconfigDIRECTORY_VOLID_SUPPORT != 0 )
+    char            pcDirectoryVolID[ 12 ];    /* Volume Label of the partition as recorded in the root directory. */
+#endif
     void            *pvFATLockHandle;
 } FF_IOManager_t;

Any access to the root directory of the volume will include fetching the new volume ID,

sonycman wrote on Tuesday, June 06, 2017:

Hi Hein.

Thanks for the help, its very appreciated!

I think the best way is to have a dedicated function to extract the label from root directory, using FF_InitEntryFetch() and FF_FetchEntryWithContext().

Or it might be faster to modify the FF_FindEntryInDir() function, but I do not want to alter original code too much.

heinbali01 wrote on Tuesday, June 06, 2017:

Hi Vladimir, here above I published a quick method that doesn’t hurt at all. It only requires that you call :

    FF_FindData_t xFindData;
    ff_findfirst( "/", &( xFindData ) );

I think that the label is always written as the first entry. If that is true, it would do to just read the first sector of the root directory, and see if the label is present.

using FF_InitEntryFetch() and FF_FetchEntryWithContext().

You studied the source code well !
As you have seen the library is quite complex because of the user preferences and also because of the many optimisations.

sonycman wrote on Tuesday, June 06, 2017:

Hi Hein, just tried:

FF_FindData_t xFindData;
ff_findfirst( "/", &( xFindData ) );

and it returns with LFN “System Volume Information” - its a system folder, not a volume label.
In disk editor I’ve found actual volume label almost at the end of root directory…

You studied the source code well !

Thank you :slight_smile:

I have found another little bug in ff_ioman.c file, in function prvDetermineFatType(), where FAT type is being checked.
From the line 774 (there is possibly your comments below):

			if( ( ( ulFirstWord & 0x0FFFFFF8 ) != 0x0FFFFFF8 ) &&
				( ( ulFirstWord & 0x0FFFFFF8 ) != 0x0FFFFFF0 ) )
			{
				/* _HT_
				I had an SD-card which worked well in Linux/W32
				but FreeRTOS+FAT returned me this error
				So for me I left out this check (just issue a warning for now)
				*/

This condition will always fail because ulFirstWord was assigned in the code above in line 718 as a short type:

ulFirstWord = ( uint32_t ) FF_getShort(const uint8_t *pBuffer, uint32_t aOffset)

To correct this error FF_getShort should be changed to FF_getLong.
After that the condition check will successfully pass.

sonycman wrote on Friday, June 09, 2017:

Hi!

Just added a special function which searches in root directory and returns an actual Volume Label:

const char* ff_get_volume_label( FF_IOManager_t *pxIOManager )
{
	BaseType_t xIndex;
	const uint8_t *pucEntryBuffer = 0;
	uint8_t ucAttrib;
	FF_FetchContext_t xFetchContext;
	FF_Error_t pxError;

	pxError = FF_InitEntryFetch( pxIOManager, pxIOManager->xPartition.ulRootDirCluster, &xFetchContext );
	if (!FF_isERR( pxError ))
	{
		for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
		{
			/* Call FF_FetchEntryWithContext only once for every 512-byte block */
			if ( !xIndex || pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) )
			{
				pxError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, 0 );
				if ( FF_isERR( pxError ) )
				{
					break;
				}
				pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
			}
			else
			{
				/* Advance 32 bytes to get the next directory entry. */
				pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
			}

			if ( FF_isEndOfDir( pucEntryBuffer ) )
			{
				break;
			}
			if (!FF_isDeleted( pucEntryBuffer ))
			{
				ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );
				if ( ucAttrib == FF_FAT_ATTR_VOLID )
				{
					memcpy( pxIOManager->xPartition.pcVolumeLabel, pucEntryBuffer, sizeof(pxIOManager->xPartition.pcVolumeLabel) - 1 );
					break;
				}
			}
		} /* for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
	}
	FF_CleanupEntryFetch( pxIOManager, &xFetchContext );

    return (const char*)pxIOManager->xPartition.pcVolumeLabel;
}

One should call it from DiskInit routine, right after the calls to FF_Mount and FF_FS_Add.