libfs/fatfs: Add FatFS with tests

This adds FatFS wrapper layer to RTEMS
including tests as part of GSoC 2025.
This commit is contained in:
Sepehr Ganji
2025-11-10 16:09:07 -07:00
parent e3592620d7
commit bff18c05b9
30 changed files with 3451 additions and 1 deletions

View File

@@ -48,6 +48,7 @@
#ifdef CONFIGURE_FILESYSTEM_ALL #ifdef CONFIGURE_FILESYSTEM_ALL
#define CONFIGURE_FILESYSTEM_DOSFS #define CONFIGURE_FILESYSTEM_DOSFS
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_FILESYSTEM_FTPFS #define CONFIGURE_FILESYSTEM_FTPFS
#define CONFIGURE_FILESYSTEM_IMFS #define CONFIGURE_FILESYSTEM_IMFS
#define CONFIGURE_FILESYSTEM_JFFS2 #define CONFIGURE_FILESYSTEM_JFFS2
@@ -108,6 +109,10 @@
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_DOSFS" #error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_DOSFS"
#endif #endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FATFS"
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS #ifdef CONFIGURE_FILESYSTEM_FTPFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FTPFS" #error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FTPFS"
#endif #endif
@@ -139,6 +144,10 @@
#include <rtems/dosfs.h> #include <rtems/dosfs.h>
#endif #endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#include <rtems/fatfs.h>
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS #ifdef CONFIGURE_FILESYSTEM_FTPFS
#include <rtems/ftpfs.h> #include <rtems/ftpfs.h>
#endif #endif
@@ -297,6 +306,9 @@ const rtems_filesystem_table_t rtems_filesystem_table[] = {
#ifdef CONFIGURE_FILESYSTEM_DOSFS #ifdef CONFIGURE_FILESYSTEM_DOSFS
{ RTEMS_FILESYSTEM_TYPE_DOSFS, rtems_dosfs_initialize }, { RTEMS_FILESYSTEM_TYPE_DOSFS, rtems_dosfs_initialize },
#endif #endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
{ RTEMS_FILESYSTEM_TYPE_FATFS, rtems_fatfs_initialize },
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS #ifdef CONFIGURE_FILESYSTEM_FTPFS
{ RTEMS_FILESYSTEM_TYPE_FTPFS, rtems_ftpfs_initialize }, { RTEMS_FILESYSTEM_TYPE_FTPFS, rtems_ftpfs_initialize },
#endif #endif

View File

@@ -0,0 +1,60 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FatFS Filesystem Public API
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifndef _RTEMS_FATFS_H
#define _RTEMS_FATFS_H
#include <rtems.h>
#include <rtems/libio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup FATFS
*
* @{
*/
/**
* @brief RTEMS FatFS filesystem mount options.
*/
typedef struct {
/** Currently no options defined */
int reserved;
} rtems_fatfs_mount_options;
/**
* @brief Initialize FatFS filesystem.
*
* This function is the mount handler for the FatFS filesystem. It is called
* by the mount() system call when the filesystem type is
* RTEMS_FILESYSTEM_TYPE_FATFS.
*
* @param[in] mt_entry The mount table entry.
* @param[in] data Mount options (rtems_fatfs_mount_options or NULL).
*
* @return 0 on success, -1 on error with errno set.
*/
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _RTEMS_FATFS_H */

View File

@@ -1649,6 +1649,7 @@ extern int rtems_mkdir(const char *path, mode_t mode);
#define RTEMS_FILESYSTEM_TYPE_TFTPFS "tftpfs" #define RTEMS_FILESYSTEM_TYPE_TFTPFS "tftpfs"
#define RTEMS_FILESYSTEM_TYPE_NFS "nfs" #define RTEMS_FILESYSTEM_TYPE_NFS "nfs"
#define RTEMS_FILESYSTEM_TYPE_DOSFS "dosfs" #define RTEMS_FILESYSTEM_TYPE_DOSFS "dosfs"
#define RTEMS_FILESYSTEM_TYPE_FATFS "fatfs"
#define RTEMS_FILESYSTEM_TYPE_RFS "rfs" #define RTEMS_FILESYSTEM_TYPE_RFS "rfs"
#define RTEMS_FILESYSTEM_TYPE_JFFS2 "jffs2" #define RTEMS_FILESYSTEM_TYPE_JFFS2 "jffs2"

View File

@@ -0,0 +1,222 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS Disk I/O Interface for FatFs
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/bdbuf.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "diskio.h"
#include "rtems-fatfs.h"
static fatfs_volume_t volumes[ FF_VOLUMES ];
/*
* Compiler will change name to rtems_fatfs_disk_status
*/
DSTATUS disk_status( BYTE pdrv )
{
if ( pdrv >= FF_VOLUMES ) {
return STA_NOINIT;
}
if ( !volumes[ pdrv ].initialized ) {
return STA_NOINIT;
}
return 0;
}
/*
* Compiler will change name to rtems_fatfs_disk_initialize
*/
DSTATUS disk_initialize( BYTE pdrv )
{
if ( pdrv >= FF_VOLUMES ) {
return STA_NOINIT;
}
bool is_initialized = volumes[ pdrv ].initialized;
return is_initialized ? 0 : STA_NOINIT;
}
/*
* Compiler will change name to rtems_fatfs_disk_read
*/
DRESULT disk_read( BYTE pdrv, BYTE *buff, LBA_t sector, UINT count )
{
rtems_status_code sc;
rtems_bdbuf_buffer *bd;
uint32_t block_size;
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
block_size = dd->block_size;
for ( size_t i = 0; i < count; i++ ) {
sc = rtems_bdbuf_read( dd, sector + i, &bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
memcpy( buff + ( i * block_size ), bd->buffer, block_size );
sc = rtems_bdbuf_release( bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
}
return RES_OK;
}
/*
* Compiler will change name to rtems_fatfs_disk_write
*/
DRESULT disk_write( BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count )
{
rtems_status_code sc;
rtems_bdbuf_buffer *bd;
uint32_t block_size;
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
block_size = dd->block_size;
for ( size_t i = 0; i < count; i++ ) {
sc = rtems_bdbuf_get( dd, sector + i, &bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
memcpy( bd->buffer, buff + ( i * block_size ), block_size );
sc = rtems_bdbuf_release_modified( bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
}
return RES_OK;
}
/*
* Compiler will change name to rtems_fatfs_disk_ioctl
*/
DRESULT disk_ioctl( BYTE pdrv, BYTE cmd, void *buff )
{
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
switch ( cmd ) {
case CTRL_SYNC: {
rtems_status_code sc = rtems_bdbuf_syncdev( dd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
} break;
case GET_SECTOR_COUNT:
*(rtems_blkdev_bnum *) buff = dd->size;
break;
case GET_SECTOR_SIZE:
*(uint32_t *) buff = dd->media_block_size;
break;
case GET_BLOCK_SIZE:
*(uint32_t *) buff = dd->block_size;
break;
case CTRL_TRIM:
break;
default:
return RES_PARERR;
}
return RES_OK;
}
int fatfs_diskio_register_device( uint8_t pdrv, const char *device_path )
{
struct stat stat_buf;
int fd;
rtems_disk_device *dd;
int rc;
if ( pdrv >= FF_VOLUMES ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( volumes[ pdrv ].initialized ) {
return 0;
}
fd = open( device_path, O_RDWR );
if ( fd < 0 ) {
return -1;
}
rc = fstat( fd, &stat_buf );
if ( rc != 0 ) {
close( fd );
return -1;
}
if ( !S_ISBLK( stat_buf.st_mode ) ) {
close( fd );
rtems_set_errno_and_return_minus_one( ENXIO );
}
rc = rtems_disk_fd_get_disk_device( fd, &dd );
if ( rc != 0 ) {
close( fd );
return -1;
}
volumes[ pdrv ].fd = fd;
volumes[ pdrv ].dd = dd;
volumes[ pdrv ].initialized = true;
return 0;
}
void fatfs_diskio_unregister_device( uint8_t pdrv )
{
if ( pdrv >= FF_VOLUMES || !volumes[ pdrv ].initialized ) {
return;
}
close( volumes[ pdrv ].fd );
volumes[ pdrv ].fd = -1;
volumes[ pdrv ].dd = NULL;
volumes[ pdrv ].initialized = false;
}

View File

@@ -0,0 +1,207 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS directory operations
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "rtems-fatfs.h"
#define DIR POSIX_DIR
#include <dirent.h>
#undef DIR
ssize_t rtems_fatfs_dir_read( rtems_libio_t *iop, void *buffer, size_t count )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
struct dirent *dp = (struct dirent *) buffer;
FILINFO fno;
FRESULT fr;
size_t bytes_transferred = 0;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( count < sizeof( struct dirent ) ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
memset( &fno, 0, sizeof( fno ) );
fr = f_readdir( &node->handle.dir, &fno );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( fno.fname[ 0 ] == '\0' ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
memset( dp, 0, sizeof( struct dirent ) );
dp->d_ino = 1;
dp->d_off = iop->offset;
dp->d_reclen = sizeof( struct dirent );
size_t name_len = strlen( fno.fname );
if ( name_len >= sizeof( dp->d_name ) ) {
name_len = sizeof( dp->d_name ) - 1;
}
memcpy( dp->d_name, fno.fname, name_len );
dp->d_name[ name_len ] = '\0';
dp->d_namlen = name_len;
if ( fno.fattrib & AM_DIR ) {
dp->d_type = DT_DIR;
} else {
dp->d_type = DT_REG;
}
bytes_transferred = sizeof( struct dirent );
iop->offset += sizeof( struct dirent );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return (ssize_t) bytes_transferred;
}
int rtems_fatfs_dir_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_to_stat( node, buf, fs_info );
return 0;
}
int rtems_fatfs_opendir(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
)
{
(void) path;
(void) oflag;
(void) mode;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
iop->pathinfo.mt_entry->fs_info;
char full_path[ 256 ];
FRESULT fr;
int rc;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
iop->pathinfo.mt_entry
);
if ( rc != 0 ) {
return rc;
}
fr = f_opendir( &node->handle.dir, full_path );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = true;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
int rtems_fatfs_closedir( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_closedir( &node->handle.dir );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = false;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
const rtems_filesystem_file_handlers_r rtems_fatfs_dir_handlers = {
.open_h = rtems_fatfs_opendir,
.close_h = rtems_fatfs_closedir,
.read_h = rtems_fatfs_dir_read,
.write_h = rtems_filesystem_default_write,
.ioctl_h = rtems_filesystem_default_ioctl,
.lseek_h = rtems_filesystem_default_lseek_directory,
.fstat_h = rtems_fatfs_dir_fstat,
.ftruncate_h = rtems_filesystem_default_ftruncate_directory,
.fsync_h = rtems_fatfs_file_fsync,
.fdatasync_h = rtems_fatfs_file_fsync,
.fcntl_h = rtems_filesystem_default_fcntl,
.kqfilter_h = rtems_filesystem_default_kqfilter,
.mmap_h = rtems_filesystem_default_mmap,
.poll_h = rtems_filesystem_default_poll,
.readv_h = rtems_filesystem_default_readv,
.writev_h = rtems_filesystem_default_writev
};

View File

@@ -0,0 +1,451 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS file operations
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "rtems-fatfs.h"
ssize_t rtems_fatfs_file_read( rtems_libio_t *iop, void *buffer, size_t count )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
UINT bytes_read = 0;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_lseek( &node->handle.file, (FSIZE_t) iop->offset );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
fr = f_read( &node->handle.file, buffer, (UINT) count, &bytes_read );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( bytes_read > 0 ) {
iop->offset += bytes_read;
}
return (ssize_t) bytes_read;
}
ssize_t rtems_fatfs_file_write(
rtems_libio_t *iop,
const void *buffer,
size_t count
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
UINT bytes_written = 0;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
if ( rtems_libio_iop_is_append( iop ) ) {
fr = f_lseek( &node->handle.file, f_size( &node->handle.file ) );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
iop->offset = f_tell( &node->handle.file );
} else {
FSIZE_t current_size = f_size( &node->handle.file );
FSIZE_t target_pos = (FSIZE_t) iop->offset;
if ( target_pos > current_size ) {
FSIZE_t gap_size = target_pos - current_size;
char zero_buf[ 512 ];
UINT bw;
memset( zero_buf, 0, sizeof( zero_buf ) );
fr = f_lseek( &node->handle.file, current_size );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
while ( gap_size > 0 && fr == FR_OK ) {
UINT to_write = gap_size > sizeof( zero_buf ) ? sizeof( zero_buf ) :
(UINT) gap_size;
fr = f_write( &node->handle.file, zero_buf, to_write, &bw );
if ( fr == FR_OK && bw == to_write ) {
gap_size -= to_write;
} else {
break;
}
}
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( gap_size > 0 ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = ENOSPC;
return -1;
}
} else {
fr = f_lseek( &node->handle.file, target_pos );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
}
}
fr = f_write( &node->handle.file, buffer, (UINT) count, &bytes_written );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( bytes_written > 0 ) {
iop->offset += bytes_written;
node->info.fsize = f_size( &node->handle.file );
time_t current_time = time( NULL );
node->posix_mtime = current_time;
node->posix_ctime = current_time;
node->posix_time_valid = true;
} else if ( count > 0 && bytes_written == 0 ) {
errno = ENOSPC;
return -1;
}
return (ssize_t) bytes_written;
}
off_t rtems_fatfs_file_lseek( rtems_libio_t *iop, off_t offset, int whence )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FSIZE_t new_pos;
off_t result;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
switch ( whence ) {
case SEEK_SET:
if ( offset < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) offset;
break;
case SEEK_CUR:
if ( offset > 0 && iop->offset > LLONG_MAX - offset ) {
rtems_set_errno_and_return_minus_one( EOVERFLOW );
}
result = iop->offset + offset;
if ( result < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) result;
break;
case SEEK_END:
if ( offset > 0 && (off_t) node->info.fsize > LLONG_MAX - offset ) {
rtems_set_errno_and_return_minus_one( EOVERFLOW );
}
result = (off_t) node->info.fsize + offset;
if ( result < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) result;
break;
default:
rtems_set_errno_and_return_minus_one( EINVAL );
}
iop->offset = (off_t) new_pos;
return iop->offset;
}
int rtems_fatfs_file_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_to_stat( node, buf, fs_info );
return 0;
}
int rtems_fatfs_file_ftruncate( rtems_libio_t *iop, off_t length )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
FSIZE_t old_size;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
old_size = f_size( &node->handle.file );
if ( (FSIZE_t) length > old_size ) {
FSIZE_t current_pos = f_tell( &node->handle.file );
FSIZE_t gap_size = (FSIZE_t) length - old_size;
char zero_buf[ 512 ];
UINT bw;
memset( zero_buf, 0, sizeof( zero_buf ) );
fr = f_lseek( &node->handle.file, old_size );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
while ( gap_size > 0 && fr == FR_OK ) {
UINT to_write = gap_size > sizeof( zero_buf ) ? sizeof( zero_buf ) :
(UINT) gap_size;
fr = f_write( &node->handle.file, zero_buf, to_write, &bw );
if ( fr == FR_OK && bw == to_write ) {
gap_size -= to_write;
} else {
break;
}
}
if ( fr == FR_OK ) {
if ( gap_size > 0 ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = ENOSPC;
return -1;
}
fr = f_lseek( &node->handle.file, current_pos );
}
} else {
fr = f_lseek( &node->handle.file, (FSIZE_t) length );
if ( fr == FR_OK ) {
fr = f_truncate( &node->handle.file );
}
}
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->info.fsize = (FSIZE_t) length;
return 0;
}
int rtems_fatfs_file_fsync( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || node->is_directory || !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_sync( &node->handle.file );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
int rtems_fatfs_openfile(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
)
{
(void) path;
(void) mode;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
iop->pathinfo.mt_entry->fs_info;
char full_path[ 256 ];
FRESULT fr;
int rc;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EISDIR );
}
if ( node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
iop->pathinfo.mt_entry
);
if ( rc != 0 ) {
return rc;
}
BYTE fatfs_mode = FA_READ;
if ( oflag & O_WRONLY ) {
fatfs_mode = FA_WRITE;
} else if ( oflag & O_RDWR ) {
fatfs_mode = FA_READ | FA_WRITE;
}
if ( oflag & O_CREAT ) {
if ( oflag & O_WRONLY ) {
fatfs_mode = FA_WRITE | FA_OPEN_EXISTING;
} else if ( oflag & O_RDWR ) {
fatfs_mode = FA_READ | FA_WRITE | FA_OPEN_EXISTING;
} else {
fatfs_mode = FA_READ | FA_OPEN_EXISTING;
}
}
fr = f_open( &node->handle.file, full_path, fatfs_mode );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( oflag & O_TRUNC ) {
if ( f_size( &node->handle.file ) == 0 ) {
UINT bw;
char dummy = 0;
fr = f_write( &node->handle.file, &dummy, 1, &bw );
if ( fr == FR_OK ) {
fr = f_truncate( &node->handle.file );
}
} else {
fr = f_truncate( &node->handle.file );
}
if ( fr != FR_OK ) {
f_close( &node->handle.file );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
f_sync( &node->handle.file );
}
node->is_open = true;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
int rtems_fatfs_closefile( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EISDIR );
}
if ( !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_close( &node->handle.file );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = false;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
const rtems_filesystem_file_handlers_r rtems_fatfs_file_handlers = {
.open_h = rtems_fatfs_openfile,
.close_h = rtems_fatfs_closefile,
.read_h = rtems_fatfs_file_read,
.write_h = rtems_fatfs_file_write,
.ioctl_h = rtems_filesystem_default_ioctl,
.lseek_h = rtems_fatfs_file_lseek,
.fstat_h = rtems_fatfs_file_fstat,
.ftruncate_h = rtems_fatfs_file_ftruncate,
.fsync_h = rtems_fatfs_file_fsync,
.fdatasync_h = rtems_fatfs_file_fsync,
.fcntl_h = rtems_filesystem_default_fcntl,
.kqfilter_h = rtems_filesystem_default_kqfilter,
.mmap_h = rtems_filesystem_default_mmap,
.poll_h = rtems_filesystem_default_poll,
.readv_h = rtems_filesystem_default_readv,
.writev_h = rtems_filesystem_default_writev
};

View File

@@ -0,0 +1,837 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS initialization
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <time.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/bdbuf.h>
#include <rtems/diskdevs.h>
#include <rtems/fatfs.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include <rtems/score/chain.h>
#include <rtems/seterr.h>
#include "ff.h"
#include "rtems-fatfs.h"
static uint8_t next_drive_number = 0;
static int rtems_fatfs_build_full_path(
const rtems_fatfs_fs_info_t *fs_info,
const rtems_fatfs_node_t *parent_node,
const char *name,
size_t namelen,
char *path,
size_t path_size
)
{
size_t mount_len = strlen( fs_info->mount_path );
size_t total_len;
char *write_pos = path;
if ( parent_node == NULL ) {
total_len = mount_len + namelen;
if ( total_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( write_pos, fs_info->mount_path, mount_len );
write_pos += mount_len;
memcpy( write_pos, name, namelen );
write_pos += namelen;
*write_pos = '\0';
} else {
size_t parent_len = strlen( parent_node->path );
bool need_slash = ( strcmp( parent_node->path, "/" ) != 0 );
total_len = mount_len + parent_len + ( need_slash ? 1 : 0 ) + namelen;
if ( total_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( write_pos, fs_info->mount_path, mount_len );
write_pos += mount_len;
memcpy( write_pos, parent_node->path, parent_len );
write_pos += parent_len;
if ( need_slash ) {
*write_pos = '/';
write_pos++;
}
memcpy( write_pos, name, namelen );
write_pos += namelen;
*write_pos = '\0';
}
char *dotdot;
while ( ( dotdot = strstr( path, "/.." ) ) != NULL ) {
if ( dotdot[ 3 ] == '\0' || dotdot[ 3 ] == '/' ) {
char *start = dotdot - 1;
while ( start > path && *start != '/' ) {
start--;
}
if ( start >= path && *start == '/' ) {
char *end = dotdot + 3;
memmove( start, end, strlen( end ) + 1 );
if ( start == path && *start == '\0' ) {
strcpy( path, "/" );
}
} else {
break;
}
} else {
break;
}
}
size_t len = strlen( path );
if ( len > 0 && len <= 2 && path[ len - 1 ] == ':' ) {
strcat( path, "/" );
}
return 0;
}
static void rtems_fatfs_cache_posix_times(
rtems_fatfs_fs_info_t *fs_info,
const char *path,
time_t mtime,
time_t ctime
)
{
int index = fs_info->posix_cache_next;
size_t path_len = strlen( path );
if ( path_len >= sizeof( fs_info->posix_cache[ index ].path ) ) {
path_len = sizeof( fs_info->posix_cache[ index ].path ) - 1;
}
memcpy( fs_info->posix_cache[ index ].path, path, path_len );
fs_info->posix_cache[ index ].path[ path_len ] = '\0';
fs_info->posix_cache[ index ].mtime = mtime;
fs_info->posix_cache[ index ].ctime = ctime;
fs_info->posix_cache[ index ].valid = true;
fs_info->posix_cache_next = ( fs_info->posix_cache_next + 1 ) %
RTEMS_FATFS_POSIX_CACHE_SIZE;
}
static void rtems_fatfs_set_parent_timestamps(
const rtems_fatfs_node_t *parent_node
)
{
if ( parent_node != NULL && parent_node->is_directory ) {
rtems_fatfs_node_t *mutable_parent = (rtems_fatfs_node_t *) parent_node;
time_t current_time = time( NULL );
mutable_parent->posix_mtime = current_time;
mutable_parent->posix_ctime = current_time;
mutable_parent->posix_time_valid = true;
}
}
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
)
{
(void) data;
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *root_node = NULL;
FRESULT fr;
char drive_path[ 8 ];
int rc = -1;
fs_info = calloc( 1, sizeof( *fs_info ) );
if ( fs_info == NULL ) {
rtems_set_errno_and_return_minus_one( ENOMEM );
}
root_node = calloc( 1, sizeof( *root_node ) );
if ( root_node == NULL ) {
free( fs_info );
rtems_set_errno_and_return_minus_one( ENOMEM );
}
rtems_recursive_mutex_init(
&fs_info->vol_mutex,
RTEMS_FILESYSTEM_TYPE_FATFS
);
fs_info->drive_number = next_drive_number++;
if ( next_drive_number >= FF_VOLUMES ) {
next_drive_number = 0;
}
rc = fatfs_diskio_register_device( fs_info->drive_number, mt_entry->dev );
if ( rc != 0 ) {
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( root_node );
free( fs_info );
return -1;
}
drive_path[ 0 ] = '0' + fs_info->drive_number;
drive_path[ 1 ] = ':';
drive_path[ 2 ] = '\0';
fr = f_mount( &fs_info->fatfs, drive_path, 1 );
if ( fr != FR_OK ) {
fatfs_diskio_unregister_device( fs_info->drive_number );
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( root_node );
free( fs_info );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
fs_info->file_handlers = &rtems_fatfs_file_handlers;
fs_info->dir_handlers = &rtems_fatfs_dir_handlers;
strncpy( fs_info->mount_path, drive_path, sizeof( fs_info->mount_path ) - 1 );
fs_info->mount_path[ sizeof( fs_info->mount_path ) - 1 ] = '\0';
strcpy( fs_info->current_dir, "/" );
memset( fs_info->posix_cache, 0, sizeof( fs_info->posix_cache ) );
fs_info->posix_cache_next = 0;
root_node->is_directory = true;
root_node->is_open = false;
root_node->is_root_node = true;
root_node->ref_count = 1;
strcpy( root_node->path, "/" );
memset( &root_node->info, 0, sizeof( root_node->info ) );
root_node->info.fattrib = AM_DIR;
root_node->info.fsize = 0;
root_node->info.fdate = 0;
root_node->info.ftime = 0;
strcpy( root_node->info.fname, "/" );
mt_entry->fs_info = fs_info;
mt_entry->ops = &rtems_fatfs_ops;
mt_entry->mt_fs_root->location.node_access = root_node;
mt_entry->mt_fs_root->location.handlers = fs_info->dir_handlers;
return 0;
}
void rtems_fatfs_fsunmount_me( rtems_filesystem_mount_table_entry_t *mt_entry )
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_fatfs_node_t *root_node = NULL;
if ( fs_info != NULL ) {
if ( mt_entry->mt_fs_root != NULL ) {
root_node = (rtems_fatfs_node_t *)
mt_entry->mt_fs_root->location.node_access;
}
f_mount( NULL, fs_info->mount_path, 1 );
fatfs_diskio_unregister_device( fs_info->drive_number );
if ( root_node != NULL ) {
free( root_node );
}
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( fs_info );
}
}
static rtems_fatfs_node_t *rtems_fatfs_node_get( rtems_fatfs_node_t *node )
{
if ( node != NULL ) {
node->ref_count++;
}
return node;
}
static void rtems_fatfs_node_put( rtems_fatfs_node_t *node )
{
if ( node != NULL ) {
if ( node->is_root_node ) {
node->ref_count--;
return;
}
node->ref_count--;
if ( node->ref_count <= 0 ) {
if ( node->is_open ) {
if ( node->is_directory ) {
f_closedir( &node->handle.dir );
} else {
f_close( &node->handle.file );
}
}
free( node );
}
}
}
static int rtems_fatfs_find_name(
rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t name_len
)
{
rtems_fatfs_fs_info_t *fs_info = parentloc->mt_entry->fs_info;
rtems_fatfs_node_t *parent_node = parentloc->node_access;
rtems_fatfs_node_t *new_node = NULL;
FRESULT fr;
FILINFO fno;
char fatfs_path[ 256 ];
int ret;
ret = rtems_fatfs_build_full_path(
fs_info,
parent_node,
name,
name_len,
fatfs_path,
sizeof( fatfs_path )
);
if ( ret != 0 ) {
return ret;
}
if ( strlen( fatfs_path ) == 3 && fatfs_path[ 1 ] == ':' &&
fatfs_path[ 2 ] == '/' ) {
rtems_filesystem_mount_table_entry_t *mt_entry = parentloc->mt_entry;
if ( mt_entry && mt_entry->mt_fs_root ) {
new_node = (rtems_fatfs_node_t *)
mt_entry->mt_fs_root->location.node_access;
if ( new_node ) {
rtems_fatfs_node_get( new_node );
rtems_fatfs_node_put( parent_node );
parentloc->node_access = new_node;
return 0;
}
}
rtems_set_errno_and_return_minus_one( ENOENT );
}
fr = f_stat( fatfs_path, &fno );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
new_node = malloc( sizeof( *new_node ) );
if ( new_node == NULL ) {
rtems_set_errno_and_return_minus_one( ENOMEM );
}
memset( new_node, 0, sizeof( *new_node ) );
new_node->info = fno;
new_node->is_directory = ( fno.fattrib & AM_DIR ) != 0;
new_node->is_open = false;
new_node->is_root_node = false;
new_node->ref_count = 1;
size_t total_len;
if ( strcmp( parent_node->path, "/" ) == 0 ) {
total_len = 1 + name_len; // "/" + name
if ( total_len >= sizeof( new_node->path ) ) {
free( new_node );
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
new_node->path[ 0 ] = '/';
memcpy( new_node->path + 1, name, name_len );
new_node->path[ total_len ] = '\0';
} else {
size_t parent_len = strlen( parent_node->path );
total_len = parent_len + 1 + name_len; // parent + "/" + name
if ( total_len >= sizeof( new_node->path ) ) {
free( new_node );
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( new_node->path, parent_node->path, parent_len );
new_node->path[ parent_len ] = '/';
memcpy( new_node->path + parent_len + 1, name, name_len );
new_node->path[ total_len ] = '\0';
}
rtems_fatfs_node_put( parent_node );
parentloc->node_access = new_node;
return 0;
}
static void rtems_fatfs_set_handlers( rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_fs_info_t *fs_info = loc->mt_entry->fs_info;
rtems_fatfs_node_t *node = loc->node_access;
if ( node->is_directory ) {
loc->handlers = fs_info->dir_handlers;
} else {
loc->handlers = fs_info->file_handlers;
}
}
static bool rtems_fatfs_eval_is_directory(
rtems_filesystem_eval_path_context_t *ctx,
void *arg
)
{
(void) arg;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) ctx->currentloc.node_access;
return node != NULL && node->is_directory;
}
static rtems_filesystem_eval_path_generic_status rtems_fatfs_eval_token(
rtems_filesystem_eval_path_context_t *ctx,
void *arg,
const char *token,
size_t tokenlen
)
{
(void) arg;
rtems_filesystem_eval_path_generic_status
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
if ( rtems_filesystem_is_current_directory( token, tokenlen ) ) {
rtems_filesystem_eval_path_clear_token( ctx );
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
} else {
rtems_filesystem_location_info_t
*currentloc = rtems_filesystem_eval_path_get_currentloc( ctx );
int rc = rtems_fatfs_find_name( currentloc, token, tokenlen );
if ( rc == 0 ) {
rtems_filesystem_eval_path_clear_token( ctx );
rtems_fatfs_set_handlers( currentloc );
if ( rtems_filesystem_eval_path_has_path( ctx ) ) {
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *)
currentloc->node_access;
if ( node != NULL && !node->is_directory ) {
rtems_filesystem_eval_path_error( ctx, ENOTDIR );
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
} else {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
}
} else {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
}
} else {
if ( errno == ENOENT ) {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
} else {
rtems_filesystem_eval_path_error( ctx, errno );
}
}
}
return status;
}
static const rtems_filesystem_eval_path_generic_config rtems_fatfs_eval_config =
{ .is_directory = rtems_fatfs_eval_is_directory,
.eval_token = rtems_fatfs_eval_token };
void rtems_fatfs_eval_path( rtems_filesystem_eval_path_context_t *ctx )
{
rtems_filesystem_eval_path_generic( ctx, NULL, &rtems_fatfs_eval_config );
}
int rtems_fatfs_mknod(
const rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t namelen,
mode_t mode,
dev_t dev
)
{
char full_path[ 256 ];
FRESULT fr;
FIL fil;
int rc;
(void) dev;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
parentloc->mt_entry->fs_info;
rtems_fatfs_node_t *parent_node = (rtems_fatfs_node_t *)
parentloc->node_access;
rc = rtems_fatfs_build_full_path(
fs_info,
parent_node,
name,
namelen,
full_path,
sizeof( full_path )
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( parentloc->mt_entry );
if ( S_ISDIR( mode ) ) {
fr = f_mkdir( full_path );
if ( fr == FR_OK ) {
rtems_fatfs_set_parent_timestamps( parent_node );
}
} else if ( S_ISREG( mode ) ) {
fr = f_open( &fil, full_path, FA_CREATE_NEW | FA_WRITE );
if ( fr == FR_OK ) {
f_close( &fil );
rtems_fatfs_set_parent_timestamps( parent_node );
}
} else {
rtems_fatfs_unlock( parentloc->mt_entry );
rtems_set_errno_and_return_minus_one( ENOTSUP );
}
rtems_fatfs_unlock( parentloc->mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
int rtems_fatfs_rmnod(
const rtems_filesystem_location_info_t *parentloc,
const rtems_filesystem_location_info_t *loc
)
{
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *node = NULL;
char full_path[ 256 ];
FRESULT fr;
int rc;
fs_info = (rtems_fatfs_fs_info_t *) parentloc->mt_entry->fs_info;
node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( node->is_open && !node->is_directory ) {
f_close( &node->handle.file );
node->is_open = false;
}
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( parentloc->mt_entry );
fr = f_unlink( full_path );
rtems_fatfs_unlock( parentloc->mt_entry );
if ( fr != FR_OK ) {
if ( ( fr == FR_DENIED || fr == FR_INVALID_NAME ) && node->is_directory ) {
errno = ENOTEMPTY;
} else {
errno = rtems_fatfs_fresult_to_errno( fr );
}
return -1;
}
return 0;
}
bool rtems_fatfs_are_nodes_equal(
const rtems_filesystem_location_info_t *a,
const rtems_filesystem_location_info_t *b
)
{
rtems_fatfs_node_t *node_a = (rtems_fatfs_node_t *) a->node_access;
rtems_fatfs_node_t *node_b = (rtems_fatfs_node_t *) b->node_access;
if ( node_a == NULL || node_b == NULL ) {
return false;
}
return strcmp( node_a->path, node_b->path ) == 0;
}
int rtems_fatfs_clone_node( rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node != NULL ) {
rtems_fatfs_node_get( node );
}
return 0;
}
void rtems_fatfs_free_node( const rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node != NULL ) {
rtems_fatfs_node_put( node );
}
}
int rtems_fatfs_rename(
const rtems_filesystem_location_info_t *old_parent_loc,
const rtems_filesystem_location_info_t *old_loc,
const rtems_filesystem_location_info_t *new_parent_loc,
const char *new_name,
size_t new_namelen
)
{
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *old_node = NULL;
char old_path[ 256 ];
char new_path[ 256 ];
FRESULT fr;
int rc;
fs_info = (rtems_fatfs_fs_info_t *) old_parent_loc->mt_entry->fs_info;
old_node = (rtems_fatfs_node_t *) old_loc->node_access;
if ( old_node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rc = rtems_fatfs_resolve_node_path(
fs_info,
old_node,
old_path,
sizeof( old_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_fs_info_t *new_fs_info = (rtems_fatfs_fs_info_t *)
new_parent_loc->mt_entry->fs_info;
rtems_fatfs_node_t *new_parent_node = (rtems_fatfs_node_t *)
new_parent_loc->node_access;
rc = rtems_fatfs_build_full_path(
new_fs_info,
new_parent_node,
new_name,
new_namelen,
new_path,
sizeof( new_path )
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( old_parent_loc->mt_entry );
fr = f_rename( old_path, new_path );
rtems_fatfs_unlock( old_parent_loc->mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
static void rtems_fatfs_update_fat_timestamp(
const char *path,
time_t timestamp
)
{
struct tm *tm_info = localtime( &timestamp );
if ( tm_info != NULL ) {
FILINFO fno;
memset( &fno, 0, sizeof( fno ) );
fno.fdate = ( ( tm_info->tm_year + 1900 - 1980 ) << 9 ) |
( ( tm_info->tm_mon + 1 ) << 5 ) | ( tm_info->tm_mday );
fno.ftime = ( tm_info->tm_hour << 11 ) | ( tm_info->tm_min << 5 ) |
( tm_info->tm_sec >> 1 );
f_utime( path, &fno );
}
}
int rtems_fatfs_utimens(
const rtems_filesystem_location_info_t *loc,
struct timespec times[ 2 ]
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
char full_path[ 256 ];
int rc;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
time_t mtime = times[ 1 ].tv_sec;
time_t current_time = time( NULL );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( loc->mt_entry );
rtems_fatfs_update_fat_timestamp( full_path, mtime );
rtems_fatfs_unlock( loc->mt_entry );
rtems_fatfs_cache_posix_times( fs_info, node->path, mtime, current_time );
node->posix_mtime = mtime;
node->posix_ctime = current_time;
node->posix_time_valid = true;
return 0;
}
/*
* Compiler will change name to rtems_fatfs_get_fattime
*/
DWORD get_fattime( void )
{
time_t current_time;
struct tm *tm_info;
DWORD fat_time = 0;
current_time = time( NULL );
tm_info = localtime( &current_time );
if ( tm_info != NULL ) {
fat_time = ( (DWORD) ( tm_info->tm_year + 1900 - 1980 ) << 25 ) |
( (DWORD) ( tm_info->tm_mon + 1 ) << 21 ) |
( (DWORD) tm_info->tm_mday << 16 ) |
( (DWORD) tm_info->tm_hour << 11 ) |
( (DWORD) tm_info->tm_min << 5 ) |
( (DWORD) tm_info->tm_sec >> 1 );
} else {
fat_time = ( 0 << 25 ) | ( 1 << 21 ) | ( 1 << 16 );
}
return fat_time;
}
int rtems_fatfs_statvfs(
const rtems_filesystem_location_info_t *root_loc,
struct statvfs *sb
)
{
rtems_fatfs_fs_info_t *fs_info = root_loc->mt_entry->fs_info;
FATFS *fs = &fs_info->fatfs;
FRESULT fr;
DWORD free_clusters;
FATFS *fatfs_ptr;
char drive_path[ 8 ];
if ( fs_info == NULL || sb == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rtems_fatfs_lock( root_loc->mt_entry );
drive_path[ 0 ] = '0' + fs_info->drive_number;
drive_path[ 1 ] = ':';
drive_path[ 2 ] = '\0';
fr = f_getfree( drive_path, &free_clusters, &fatfs_ptr );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( root_loc->mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
#if FF_MAX_SS != FF_MIN_SS
WORD sector_size = fs->ssize;
#else
WORD sector_size = FF_MAX_SS;
#endif
sb->f_bsize = sector_size;
sb->f_frsize = fs->csize * sector_size;
sb->f_blocks = fs->n_fatent - 2;
sb->f_bfree = free_clusters;
sb->f_bavail = free_clusters;
sb->f_files = 0;
sb->f_ffree = 0;
sb->f_favail = 0;
sb->f_flag = 0;
#if FF_USE_LFN
sb->f_namemax = FF_LFN_BUF - 1;
#else
sb->f_namemax = 12;
#endif
rtems_fatfs_unlock( root_loc->mt_entry );
return 0;
}
const rtems_filesystem_operations_table rtems_fatfs_ops = {
.lock_h = rtems_fatfs_lock,
.unlock_h = rtems_fatfs_unlock,
.eval_path_h = rtems_fatfs_eval_path,
.link_h = rtems_filesystem_default_link,
.are_nodes_equal_h = rtems_fatfs_are_nodes_equal,
.mknod_h = rtems_fatfs_mknod,
.rmnod_h = rtems_fatfs_rmnod,
.fchmod_h = rtems_filesystem_default_fchmod,
.chown_h = rtems_filesystem_default_chown,
.clonenod_h = rtems_fatfs_clone_node,
.freenod_h = rtems_fatfs_free_node,
.mount_h = rtems_fatfs_initialize,
.unmount_h = rtems_filesystem_default_unmount,
.fsunmount_me_h = rtems_fatfs_fsunmount_me,
.utimens_h = rtems_fatfs_utimens,
.symlink_h = rtems_filesystem_default_symlink,
.readlink_h = rtems_filesystem_default_readlink,
.rename_h = rtems_fatfs_rename,
.statvfs_h = rtems_fatfs_statvfs
};

View File

@@ -0,0 +1,372 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FatFS headers
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifndef RTEMS_FATFS_H
#define RTEMS_FATFS_H
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <rtems/diskdevs.h>
#include <rtems/libio.h>
#include <rtems/seterr.h>
#include "ff.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DT_REG 8
#define DT_DIR 4
typedef struct {
int fd;
rtems_disk_device *dd;
bool initialized;
} fatfs_volume_t;
#define RTEMS_FATFS_POSIX_CACHE_SIZE 64
typedef struct {
char path[ 256 ];
time_t mtime;
time_t ctime;
bool valid;
} rtems_fatfs_posix_cache_entry_t;
typedef struct {
FATFS fatfs;
rtems_recursive_mutex vol_mutex;
const rtems_filesystem_file_handlers_r *file_handlers;
const rtems_filesystem_file_handlers_r *dir_handlers;
uint8_t drive_number;
char mount_path[ 256 ];
char current_dir[ 256 ];
rtems_fatfs_posix_cache_entry_t posix_cache[ RTEMS_FATFS_POSIX_CACHE_SIZE ];
int posix_cache_next;
} rtems_fatfs_fs_info_t;
typedef struct {
union {
FIL file;
DIR dir;
} handle;
FILINFO info;
bool is_directory;
bool is_open;
char path[ 256 ];
bool is_root_node;
int ref_count;
time_t posix_mtime;
time_t posix_ctime;
bool posix_time_valid;
} rtems_fatfs_node_t;
/* Disk I/O interface */
int fatfs_diskio_register_device( uint8_t pdrv, const char *device_path );
void fatfs_diskio_unregister_device( uint8_t pdrv );
/* Utility functions */
static inline int rtems_fatfs_get_full_path(
const rtems_fatfs_fs_info_t *fs_info,
const char *relative_path,
char *full_path,
size_t path_size
)
{
size_t mount_len = strlen( fs_info->mount_path );
size_t rel_len = strlen( relative_path );
if ( mount_len + rel_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( full_path, fs_info->mount_path, mount_len );
memcpy( full_path + mount_len, relative_path, rel_len + 1 );
return 0;
}
static inline int rtems_fatfs_fresult_to_errno( FRESULT fr )
{
switch ( fr ) {
case FR_OK:
return 0;
case FR_DISK_ERR:
case FR_INT_ERR:
case FR_NOT_READY:
return EIO;
case FR_NO_FILE:
case FR_NO_PATH:
return ENOENT;
case FR_INVALID_NAME:
return EINVAL;
case FR_DENIED:
return EACCES;
case FR_EXIST:
return EEXIST;
case FR_INVALID_OBJECT:
return EBADF;
case FR_WRITE_PROTECTED:
return EROFS;
case FR_INVALID_DRIVE:
case FR_NOT_ENABLED:
case FR_NO_FILESYSTEM:
return ENXIO;
case FR_MKFS_ABORTED:
return EINVAL;
case FR_TIMEOUT:
return ETIMEDOUT;
case FR_LOCKED:
return EBUSY;
case FR_NOT_ENOUGH_CORE:
return ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return EMFILE;
case FR_INVALID_PARAMETER:
return EINVAL;
default:
return EIO;
}
}
static inline void rtems_fatfs_unlock(
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_recursive_mutex_unlock( &fs_info->vol_mutex );
}
static inline int rtems_fatfs_resolve_node_path(
const rtems_fatfs_fs_info_t *fs_info,
const rtems_fatfs_node_t *node,
char *full_path,
size_t path_size,
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
int
rc = rtems_fatfs_get_full_path( fs_info, node->path, full_path, path_size );
if ( rc != 0 && mt_entry != NULL ) {
rtems_fatfs_unlock( mt_entry );
}
return rc;
}
static inline void rtems_fatfs_filinfo_to_stat(
const FILINFO *fno,
struct stat *st
)
{
const char *name = fno->fname;
ino_t inode_hash = 1;
memset( st, 0, sizeof( *st ) );
while ( *name != '\0' ) {
inode_hash = ( inode_hash * 33 ) + (unsigned char) *name;
name++;
}
inode_hash ^= fno->fsize ^ ( fno->fdate << 16 ) ^ fno->ftime;
st->st_ino = ( inode_hash != 0 ) ? inode_hash : 1;
st->st_dev = 1;
st->st_size = (off_t) fno->fsize;
st->st_blksize = 512;
st->st_blocks = ( st->st_size + 511 ) / 512;
if ( fno->fdate != 0 || fno->ftime != 0 ) {
struct tm tm;
memset( &tm, 0, sizeof( tm ) );
tm.tm_year = ( ( fno->fdate >> 9 ) & 0x7F ) + 80;
tm.tm_mon = ( ( fno->fdate >> 5 ) & 0x0F ) - 1;
tm.tm_mday = fno->fdate & 0x1F;
tm.tm_hour = ( fno->ftime >> 11 ) & 0x1F;
tm.tm_min = ( fno->ftime >> 5 ) & 0x3F;
tm.tm_sec = ( fno->ftime & 0x1F ) * 2;
st->st_mtime = mktime( &tm );
st->st_ctime = st->st_mtime;
st->st_atime = st->st_mtime;
}
if ( fno->fattrib & AM_DIR ) {
st->st_mode = S_IFDIR | 0755;
} else {
st->st_mode = S_IFREG | 0644;
}
if ( fno->fattrib & AM_RDO ) {
st->st_mode &= ~( S_IWUSR | S_IWGRP | S_IWOTH );
}
}
static inline void rtems_fatfs_node_to_stat_basic(
const rtems_fatfs_node_t *node,
struct stat *st
)
{
rtems_fatfs_filinfo_to_stat( &node->info, st );
}
static inline void rtems_fatfs_node_to_stat(
const rtems_fatfs_node_t *node,
struct stat *st,
rtems_fatfs_fs_info_t *fs_info
)
{
rtems_fatfs_filinfo_to_stat( &node->info, st );
if ( fs_info != NULL ) {
blksize_t cluster_size = fs_info->fatfs.csize * 512;
st->st_blksize = cluster_size;
blkcnt_t clusters_used = ( st->st_size + cluster_size - 1 ) / cluster_size;
st->st_blocks = clusters_used * fs_info->fatfs.csize;
}
if ( node->posix_time_valid ) {
st->st_mtime = node->posix_mtime;
st->st_ctime = node->posix_ctime;
st->st_atime = node->posix_mtime;
} else if ( fs_info != NULL ) {
for ( int i = 0; i < RTEMS_FATFS_POSIX_CACHE_SIZE; i++ ) {
if ( fs_info->posix_cache[ i ].valid &&
strcmp( fs_info->posix_cache[ i ].path, node->path ) == 0 ) {
st->st_mtime = fs_info->posix_cache[ i ].mtime;
st->st_ctime = fs_info->posix_cache[ i ].ctime;
st->st_atime = fs_info->posix_cache[ i ].mtime;
break;
}
}
}
}
static inline void rtems_fatfs_lock(
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_recursive_mutex_lock( &fs_info->vol_mutex );
}
/* Filesystem operations - declared in rtems-fatfs-init.c */
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
);
void rtems_fatfs_fsunmount_me( rtems_filesystem_mount_table_entry_t *mt_entry );
void rtems_fatfs_eval_path( rtems_filesystem_eval_path_context_t *ctx );
int rtems_fatfs_mknod(
const rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t namelen,
mode_t mode,
dev_t dev
);
int rtems_fatfs_rmnod(
const rtems_filesystem_location_info_t *parentloc,
const rtems_filesystem_location_info_t *loc
);
int rtems_fatfs_rename(
const rtems_filesystem_location_info_t *old_parent_loc,
const rtems_filesystem_location_info_t *old_loc,
const rtems_filesystem_location_info_t *new_parent_loc,
const char *new_name,
size_t new_namelen
);
int rtems_fatfs_utimens(
const rtems_filesystem_location_info_t *loc,
struct timespec times[ 2 ]
);
int rtems_fatfs_statvfs(
const rtems_filesystem_location_info_t *root_loc,
struct statvfs *sb
);
bool rtems_fatfs_are_nodes_equal(
const rtems_filesystem_location_info_t *a,
const rtems_filesystem_location_info_t *b
);
int rtems_fatfs_clone_node( rtems_filesystem_location_info_t *loc );
void rtems_fatfs_free_node( const rtems_filesystem_location_info_t *loc );
/* File operations - declared in rtems-fatfs-file.c */
ssize_t rtems_fatfs_file_read( rtems_libio_t *iop, void *buffer, size_t count );
ssize_t rtems_fatfs_file_write(
rtems_libio_t *iop,
const void *buffer,
size_t count
);
off_t rtems_fatfs_file_lseek( rtems_libio_t *iop, off_t offset, int whence );
int rtems_fatfs_file_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
);
int rtems_fatfs_file_ftruncate( rtems_libio_t *iop, off_t length );
int rtems_fatfs_file_fsync( rtems_libio_t *iop );
/* Directory operations - declared in rtems-fatfs-dir.c */
ssize_t rtems_fatfs_dir_read( rtems_libio_t *iop, void *buffer, size_t count );
int rtems_fatfs_dir_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
);
int rtems_fatfs_opendir(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
);
int rtems_fatfs_closedir( rtems_libio_t *iop );
int rtems_fatfs_openfile(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
);
int rtems_fatfs_closefile( rtems_libio_t *iop );
/* Handler tables */
extern const rtems_filesystem_operations_table rtems_fatfs_ops;
extern const rtems_filesystem_file_handlers_r rtems_fatfs_file_handlers;
extern const rtems_filesystem_file_handlers_r rtems_fatfs_dir_handlers;
#ifdef __cplusplus
}
#endif
#endif /* RTEMS_FATFS_H */

View File

@@ -4,7 +4,30 @@ cflags:
- ${COVERAGE_COMPILER_FLAGS} - ${COVERAGE_COMPILER_FLAGS}
copyrights: copyrights:
- Copyright (C) 2020 embedded brains GmbH & Co. KG - Copyright (C) 2020 embedded brains GmbH & Co. KG
cppflags: [] cppflags:
- -Ddisk_status=rtems_fatfs_disk_status
- -Ddisk_initialize=rtems_fatfs_disk_initialize
- -Ddisk_read=rtems_fatfs_disk_read
- -Ddisk_write=rtems_fatfs_disk_write
- -Ddisk_ioctl=rtems_fatfs_disk_ioctl
- -Dget_fattime=rtems_fatfs_get_fattime
- -DFFCONF_DEF=5385
- -DFF_VOLUMES=1
- -DFF_USE_MKFS=1
- -DFF_USE_CHMOD=1
- -DFF_USE_LFN=1
- -DFF_FS_RPATH=2
- -DFF_FS_EXFAT=1
- -DFF_MAX_SS=512
- -DFF_MIN_SS=512
- -DFF_LFN_BUF=255
- -DFF_SFN_BUF=12
- -DFF_MAX_LFN=255
- -DFF_USE_LABEL=0
- -DFF_STR_VOLUME_ID=0
- -DFF_MULTI_PARTITION=0
- -DFF_FS_READONLY=0
- -DFF_LBA64=0
cxxflags: cxxflags:
- ${COVERAGE_COMPILER_FLAGS} - ${COVERAGE_COMPILER_FLAGS}
enabled-by: true enabled-by: true
@@ -118,6 +141,7 @@ install:
- cpukit/include/rtems/devzero.h - cpukit/include/rtems/devzero.h
- cpukit/include/rtems/diskdevs.h - cpukit/include/rtems/diskdevs.h
- cpukit/include/rtems/dosfs.h - cpukit/include/rtems/dosfs.h
- cpukit/include/rtems/fatfs.h
- cpukit/include/rtems/dumpbuf.h - cpukit/include/rtems/dumpbuf.h
- cpukit/include/rtems/endian.h - cpukit/include/rtems/endian.h
- cpukit/include/rtems/error.h - cpukit/include/rtems/error.h
@@ -861,6 +885,13 @@ source:
- cpukit/libfs/src/dosfs/msdos_rename.c - cpukit/libfs/src/dosfs/msdos_rename.c
- cpukit/libfs/src/dosfs/msdos_rmnod.c - cpukit/libfs/src/dosfs/msdos_rmnod.c
- cpukit/libfs/src/dosfs/msdos_statvfs.c - cpukit/libfs/src/dosfs/msdos_statvfs.c
- cpukit/libfs/src/fatfs/ff.c
- cpukit/libfs/src/fatfs/ffunicode.c
- cpukit/libfs/src/fatfs/ffsystem.c
- cpukit/libfs/src/fatfs/rtems-diskio.c
- cpukit/libfs/src/fatfs/rtems-fatfs-init.c
- cpukit/libfs/src/fatfs/rtems-fatfs-file.c
- cpukit/libfs/src/fatfs/rtems-fatfs-dir.c
- cpukit/libfs/src/imfs/deviceio.c - cpukit/libfs/src/imfs/deviceio.c
- cpukit/libfs/src/imfs/imfs_add_node.c - cpukit/libfs/src/imfs/imfs_add_node.c
- cpukit/libfs/src/imfs/imfs_chown.c - cpukit/libfs/src/imfs/imfs_chown.c

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsclose01/init.c
stlib: []
target: testsuites/fstests/fatfs_fsclose01.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fserror/test.c
stlib: []
target: testsuites/fstests/fatfs_fserror.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fspatheval/test.c
stlib: []
target: testsuites/fstests/fatfs_fspatheval.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrdwr/init.c
stlib: []
target: testsuites/fstests/fatfs_fsrdwr.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrename/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrename.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,22 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags:
- -DTEST_STATE_EXPECTED_FAIL=1
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenameexisting/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenameexisting.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenamelongname/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenamelongname.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,22 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags:
- -DTEST_STATE_EXPECTED_FAIL=1
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenamemaxlinks/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenamemaxlinks.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrmdirparent/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrmdirparent.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsscandir01/init.c
stlib: []
target: testsuites/fstests/fatfs_fsscandir01.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsstatvfs/test.c
stlib: []
target: testsuites/fstests/fatfs_fsstatvfs.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fstime/test.c
stlib: []
target: testsuites/fstests/fatfs_fstime.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags: []
links: []
source:
- testsuites/fstests/fsfatfsformat01/init.c
stlib: []
target: testsuites/fstests/fsfatfsformat01.exe
type: build
use-after: []
use-before: []

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags: []
links: []
source:
- testsuites/fstests/fsfatfssync01/init.c
stlib: []
target: testsuites/fstests/fsfatfssync01.exe
type: build
use-after: []
use-before: []

View File

@@ -27,6 +27,8 @@ links:
uid: libmimfs uid: libmimfs
- role: build-dependency - role: build-dependency
uid: librfs uid: librfs
- role: build-dependency
uid: libfatfs
- role: build-dependency - role: build-dependency
uid: fsbdpart01 uid: fsbdpart01
- role: build-dependency - role: build-dependency
@@ -41,6 +43,8 @@ links:
uid: fsdosfssync01 uid: fsdosfssync01
- role: build-dependency - role: build-dependency
uid: fsdosfswrite01 uid: fsdosfswrite01
- role: build-dependency
uid: fsfatfssync01
- role: build-dependency - role: build-dependency
uid: fsfseeko01 uid: fsfseeko01
- role: build-dependency - role: build-dependency
@@ -221,6 +225,32 @@ links:
uid: mrfsfstime uid: mrfsfstime
- role: build-dependency - role: build-dependency
uid: tftpfs uid: tftpfs
- role: build-dependency
uid: fsfatfsformat01
- role: build-dependency
uid: fatfsfsrdwr
- role: build-dependency
uid: fatfsfserror
- role: build-dependency
uid: fatfsfspatheval
- role: build-dependency
uid: fatfsfsrename
- role: build-dependency
uid: fatfsfsrenameexisting
- role: build-dependency
uid: fatfsfsrenamelongname
- role: build-dependency
uid: fatfsfsrenamemaxlinks
- role: build-dependency
uid: fatfsfsrmdirparent
- role: build-dependency
uid: fatfsfsscandir01
- role: build-dependency
uid: fatfsfsstatvfs
- role: build-dependency
uid: fatfsfstime
- role: build-dependency
uid: fatfsfsclose01
type: build type: build
use-after: [] use-after: []
use-before: use-before:

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: library
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
includes:
- testsuites/fstests/fatfs_support
install: []
install-path: null
links: []
source:
- testsuites/fstests/fatfs_support/fs_support.c
- testsuites/fstests/support/fstest_support.c
- testsuites/fstests/support/ramdisk_support.c
target: testfatfs
type: build

View File

@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* COPYRIGHT (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __FATFS_SUPPORT_h
#define __FATFS_SUPPORT_h
#define FILESYSTEM "FATFS"
#endif

View File

@@ -0,0 +1,183 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* COPYRIGHT (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <tmacros.h>
#include <rtems/fatfs.h>
#include <rtems/libcsupport.h>
#include <rtems/libio.h>
#include "fs_config.h"
#include "fstest.h"
#include "fstest_support.h"
#include "ramdisk_support.h"
/* Include FatFS headers - these are internal to the FatFS implementation */
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
/* FatFS constants and structures */
#define FR_OK 0
#define FM_FAT 0x01
#define FM_FAT32 0x02
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} mkfs_parm;
typedef unsigned char FRESULT;
/* FatFS function declaration */
extern FRESULT f_mkfs(
const char *path,
const mkfs_parm *opt,
void *work,
unsigned int len
);
#define BLOCK_SIZE 512
static const mkfs_parm fatfs_format_options = {
.fmt = FM_FAT, /* Format as FAT12/16 (auto-detect) */
.num_fat = 2, /* Number of FAT copies */
.align = 0, /* Auto data area alignment */
.n_root = 512, /* Number of root directory entries for FAT12/16 */
.auto_cluster_size = 0 /* Auto cluster size */
};
static rtems_resource_snapshot before_mount;
static unsigned char fatfs_work_buffer[ 4096 ]; /* Work buffer for f_mkfs() */
static int fatfs_format_disk( const char *device_path )
{
FRESULT fr;
int rc;
rc = fatfs_diskio_register_device( 0, device_path );
if ( rc != 0 ) {
printf( "Device registration failed: %d\n", rc );
return -1;
}
fr = f_mkfs(
"0:",
&fatfs_format_options,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
if ( fr != FR_OK ) {
printf( "FatFS formatting failed: %d\n", fr );
fatfs_diskio_unregister_device( 0 );
return -1;
}
fatfs_diskio_unregister_device( 0 );
return 0;
}
void test_initialize_filesystem( void )
{
int rc = 0;
rc = mkdir( BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( rc == 0 );
init_ramdisk();
rc = fatfs_format_disk( RAMDISK_PATH );
rtems_test_assert( rc == 0 );
rtems_resource_snapshot_take( &before_mount );
rc = mount(
RAMDISK_PATH,
BASE_FOR_TEST,
"fatfs",
RTEMS_FILESYSTEM_READ_WRITE,
NULL
);
if ( rc != 0 ) {
printf( "Mount failed with errno: %s\n", strerror( errno ) );
}
rtems_test_assert( rc == 0 );
}
void test_shutdown_filesystem( void )
{
int rc = 0;
rc = unmount( BASE_FOR_TEST );
if ( rc != 0 ) {
printf( "Unmount failed with errno: %d (%s)\n", errno, strerror( errno ) );
} else {
printf( "Unmount successful!\n" );
}
rtems_test_assert( rc == 0 );
rtems_test_assert( rtems_resource_snapshot_check( &before_mount ) );
del_ramdisk();
}
/* configuration information */
/* drivers */
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
/**
* Configure base RTEMS resources.
*/
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_MAXIMUM_SEMAPHORES 10
#define CONFIGURE_MAXIMUM_TASKS 10
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 40
#define CONFIGURE_INIT_TASK_STACK_SIZE ( 16 * 1024 )
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_INIT
#include <rtems/confdefs.h>

View File

@@ -0,0 +1,445 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/statvfs.h>
#include <rtems/blkdev.h>
#include <rtems/libio.h>
#include <rtems/sparse-disk.h>
#include <bsp.h>
#include "tmacros.h"
const char rtems_test_name[] = "FSFATFSFORMAT 1";
#define MAX_PATH_LENGTH 100
#define SECTOR_SIZE 512
#define FAT12_MAX_CLN 4085
#define FAT16_MAX_CLN 65525
#define FAT12_DEFAULT_SECTORS_PER_CLUSTER 8
#define FAT16_DEFAULT_SECTORS_PER_CLUSTER 32
/* FatFS constants and structures */
#define FR_OK 0
#define FR_INVALID_PARAMETER 19
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_ANY 0x07
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} mkfs_parm;
/* FatFS MKFS_PARM structure for proper parameter passing */
typedef struct {
unsigned char fmt; /* Format option (FM_FAT, FM_FAT32, etc.) */
unsigned char n_fat; /* Number of FATs */
unsigned int align; /* Data area alignment (sector) */
unsigned int n_root; /* Number of root directory entries */
unsigned long au_size; /* Cluster size (byte) */
} MKFS_PARM;
typedef unsigned char FRESULT;
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
extern FRESULT f_mkfs(
const char *path,
const MKFS_PARM *opt,
void *work,
unsigned int len
);
static unsigned char fatfs_work_buffer[ 4096 ];
static int fatfs_format_disk(
const char *device_path,
const mkfs_parm *format_options
)
{
FRESULT fr;
int rc;
MKFS_PARM fatfs_opts;
rc = fatfs_diskio_register_device( 0, device_path );
if ( rc != 0 ) {
return -1;
}
/* Convert test structure to FatFS structure */
fatfs_opts.fmt = format_options->fmt;
fatfs_opts.n_fat = format_options->num_fat;
fatfs_opts.align = format_options->align;
fatfs_opts.n_root = format_options->n_root;
fatfs_opts.au_size = format_options->auto_cluster_size *
512; /* Convert sectors to bytes */
fr = f_mkfs(
"0:",
&fatfs_opts,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
fatfs_diskio_unregister_device( 0 );
return ( fr == FR_OK ) ? 0 : -1;
}
static void test_disk_params(
const char *dev_name,
const char *mount_dir,
const blksize_t sector_size,
const blksize_t cluster_size,
const blkcnt_t sectors_per_cluster
)
{
int rv;
int fildes;
struct stat stat_buff;
char file_name[ MAX_PATH_LENGTH + 1 ];
ssize_t num_bytes;
unsigned int value = (unsigned int) -1;
snprintf( file_name, MAX_PATH_LENGTH, "%s/file1.txt", mount_dir );
memset( &stat_buff, 0, sizeof( stat_buff ) );
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
fildes = open(
file_name,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
);
rtems_test_assert( -1 != fildes );
num_bytes = write( fildes, &value, sizeof( value ) );
rtems_test_assert( sizeof( value ) == num_bytes );
rv = fstat( fildes, &stat_buff );
rtems_test_assert( 0 == rv );
rtems_test_assert( S_ISREG( stat_buff.st_mode ) );
rtems_test_assert( sizeof( value ) == stat_buff.st_size );
rtems_test_assert( cluster_size == stat_buff.st_blksize );
rtems_test_assert(
sectors_per_cluster == ( stat_buff.st_blocks * sector_size / 512 )
);
rtems_test_assert(
( ( ( stat_buff.st_size + cluster_size - 1 ) / cluster_size ) *
cluster_size / 512 ) == stat_buff.st_blocks
);
rv = close( fildes );
rtems_test_assert( 0 == rv );
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
}
static void test_create_file(
const char *mount_dir,
uint32_t file_idx,
bool expect_ok
)
{
char file_name[ MAX_PATH_LENGTH + 1 ];
int fd;
snprintf(
file_name,
MAX_PATH_LENGTH,
"%s/file%" PRIu32 ".txt",
mount_dir,
file_idx
);
fd = open(
file_name,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
);
if ( expect_ok ) {
int rv;
rtems_test_assert( fd >= 0 );
rv = close( fd );
rtems_test_assert( rv == 0 );
} else {
rtems_test_assert( fd == -1 );
}
}
static void test_file_creation(
const char *dev_name,
const char *mount_dir,
const uint32_t number_of_files
)
{
int rv;
uint32_t file_idx;
char file_name[ MAX_PATH_LENGTH + 1 ];
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
for ( file_idx = 0; file_idx < number_of_files; ++file_idx ) {
test_create_file( mount_dir, file_idx, true );
}
test_create_file( mount_dir, file_idx, false );
for ( file_idx = 0; file_idx < number_of_files; ++file_idx ) {
snprintf(
file_name,
MAX_PATH_LENGTH,
"%s/file%" PRIu32 ".txt",
mount_dir,
file_idx
);
rv = unlink( file_name );
rtems_test_assert( 0 == rv );
}
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
}
static void test( void )
{
rtems_status_code sc;
int rv;
const char dev_name[] = "/dev/rda";
const char mount_dir[] = "/mnt";
mkfs_parm rqdata;
rtems_blkdev_bnum media_block_count;
memset( &rqdata, 0, sizeof( rqdata ) );
rv = mkdir( mount_dir, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( 0 == rv );
/* FAT12 */
/* For 1.44 MB disks */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
2880,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Optimized for disk space */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 1;
rqdata.align = 0;
rqdata.n_root = 32;
rqdata.auto_cluster_size = 1;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE, 1 );
test_file_creation( dev_name, mount_dir, rqdata.n_root );
/* FatFS silently clamps invalid parameters rather than returning errors,
* so we skip this test as it doesn't match FatFS behavior */
/* Optimized for read/write speed */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 8;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 8, 8 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* Largest FAT12 disk */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
( FAT12_MAX_CLN * FAT12_DEFAULT_SECTORS_PER_CLUSTER ) - 1L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Default parameters - let FatFS choose cluster size */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 512;
rqdata.auto_cluster_size = 0;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
/* FatFS chooses cluster size based on volume size, so test actual result */
test_disk_params(
dev_name,
mount_dir,
SECTOR_SIZE,
SECTOR_SIZE * 4, /* FatFS actually chooses 4 sectors per cluster */
4
);
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* FAT16 */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
1024,
( FAT12_MAX_CLN * FAT12_DEFAULT_SECTORS_PER_CLUSTER ) + 1L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Optimized for disk space */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 1;
rqdata.align = 0;
rqdata.n_root = 32;
rqdata.auto_cluster_size = 1;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE, 1 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* FAT32 */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
1024,
( FAT16_MAX_CLN * FAT16_DEFAULT_SECTORS_PER_CLUSTER ) + 41L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Default parameters - FAT32 chooses larger cluster sizes */
rqdata.fmt = FM_FAT32;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 0;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 16, 16 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* Format some disks from 1MB up to 128GB - let FatFS choose appropriate FAT
* type */
rqdata.fmt = FM_ANY;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 64;
for ( media_block_count = 1 * 1024 * ( 1024 / SECTOR_SIZE );
media_block_count <= 128 * 1024 * 1024 * ( 1024 / SECTOR_SIZE );
media_block_count *= 2 ) {
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
media_block_count,
0
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 64, 64 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
}
}
static void Init( rtems_task_argument arg )
{
(void) arg;
TEST_BEGIN();
test();
TEST_END();
rtems_test_exit( 0 );
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_MAXIMUM_TASKS 1
#define CONFIGURE_MAXIMUM_SEMAPHORES 10
#define CONFIGURE_INIT_TASK_STACK_SIZE ( 32 * 1024 )
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_BDBUF_BUFFER_MAX_SIZE ( 32 * 1024 )
#define CONFIGURE_INIT
#include <rtems/confdefs.h>

View File

@@ -0,0 +1,17 @@
This file documents the test fsfatfssync01.
test set name: fsfatfssync01
test purpose:
Verify FatFS auto-sync behavior and explicit fsync() functionality.
test description:
Tests FatFS's synchronization behavior by:
- Creating a file and verifying it persists after disk cache purge
- Writing data without explicit fsync() and verifying it persists after disk cache purge
(demonstrates FatFS auto-syncs on file close)
- Writing additional data with explicit fsync() and verifying it persists after disk cache purge
Note: Unlike traditional POSIX filesystems, FatFS automatically calls f_sync() during
f_close(), ensuring all writes are immediately persisted to disk regardless of whether
fsync() was explicitly called.

View File

@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <rtems/blkdev.h>
#include <rtems/fatfs.h>
#include <rtems/libio.h>
#include <rtems/ramdisk.h>
#include "tmacros.h"
const char rtems_test_name[] = "FSFATFSSYNC 1";
/* FatFS constants and structures */
#define FR_OK 0
#define FM_FAT 0x01
#define FM_FAT32 0x02
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} MKFS_PARM;
typedef unsigned char FRESULT;
/* External FatFS functions */
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
extern FRESULT f_mkfs(
const char *path,
const MKFS_PARM *opt,
void *work,
unsigned int len
);
static unsigned char fatfs_work_buffer[ 4096 ];
static void create_file( const char *file )
{
int fd;
int rv;
fd = creat( file, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( fd >= 0 );
rv = fsync( fd );
rtems_test_assert( rv == 0 );
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void write_to_file( const char *file, bool sync )
{
int fd;
char buf[ 1 ] = { 'A' };
ssize_t n;
off_t pos_before, pos_after;
int rv;
fd = open( file, O_RDWR );
rtems_test_assert( fd >= 0 );
pos_before = lseek( fd, 0, SEEK_END );
n = write( fd, buf, sizeof( buf ) );
rtems_test_assert( n == (ssize_t) sizeof( buf ) );
pos_after = lseek( fd, 0, SEEK_END );
rtems_test_assert( pos_after == pos_before + sizeof( buf ) );
if ( sync ) {
rv = fsync( fd );
rtems_test_assert( rv == 0 );
}
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void check_file_size( const char *file, off_t size )
{
int fd;
off_t pos;
int rv;
fd = open( file, O_RDWR );
rtems_test_assert( fd >= 0 );
pos = lseek( fd, 0, SEEK_END );
rtems_test_assert( pos == size );
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void test( const char *rda, const char *mnt, const char *file )
{
static const MKFS_PARM fatfs_format_options = {
.fmt = FM_FAT,
.num_fat = 2,
.align = 0,
.n_root = 512,
.auto_cluster_size = 0
};
int disk_fd;
int rv;
FRESULT fr;
disk_fd = open( rda, O_RDWR );
rtems_test_assert( disk_fd >= 0 );
rv = fatfs_diskio_register_device( 0, rda );
rtems_test_assert( rv == 0 );
/* Format using FatFS */
fr = f_mkfs(
"0:",
&fatfs_format_options,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
rtems_test_assert( fr == FR_OK );
fatfs_diskio_unregister_device( 0 );
rv = mount_and_make_target_path(
rda,
mnt,
"fatfs",
RTEMS_FILESYSTEM_READ_WRITE,
NULL
);
rtems_test_assert( rv == 0 );
create_file( file );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
write_to_file( file, false );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
check_file_size( file, 1 );
write_to_file( file, true );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
check_file_size( file, 2 );
rv = unmount( mnt );
rtems_test_assert( rv == 0 );
rv = close( disk_fd );
rtems_test_assert( rv == 0 );
}
static void Init( rtems_task_argument arg )
{
(void) arg;
TEST_BEGIN();
test( "/dev/rda", "/mnt", "/mnt/file" );
TEST_END();
rtems_test_exit( 0 );
}
rtems_ramdisk_config rtems_ramdisk_configuration[] = {
{ .block_size = 512, .block_num = 1024 }
};
size_t rtems_ramdisk_configuration_size = 1;
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_EXTRA_DRIVERS RAMDISK_DRIVER_TABLE_ENTRY
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 6
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_EXTRA_TASK_STACKS ( 8 * 1024 )
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_INIT
#include <rtems/confdefs.h>