Compare commits

...

4 Commits

Author SHA1 Message Date
Michal Lenc
21814ea3bc Merge branch 'xilinx_zynq_zedboard-compile-fix' into 'main'
bsps/arm/xilinx-zynq/start/bspsmp.c: fix unused parameter warning

See merge request rtems/rtos/rtems!822
2025-11-14 01:59:02 +00:00
Sepehr Ganji
bff18c05b9 libfs/fatfs: Add FatFS with tests
This adds FatFS wrapper layer to RTEMS
including tests as part of GSoC 2025.
2025-11-10 16:09:07 -07:00
Kinsey Moore
e3592620d7 cpukit/flashdev: Return error for missing callbacks
When a callback does not exist, the Flashdev API should return error
where possible instead of success. When the API returns success for
missing callbacks, the Flashdev API client code may end up using
uninitialized variables as if they were filled by the API.

Closes #5391
2025-11-10 12:29:32 -06:00
Kinsey Moore
54de3ac479 bsps/shared/xqspi_flash.c: Add support for sector information 2025-11-10 12:29:31 -06:00
32 changed files with 3490 additions and 16 deletions

View File

@@ -103,6 +103,28 @@ static int xqspi_page_count(
return 0;
}
static int xqspi_sector_count(
rtems_flashdev *flash,
int *sector_count
)
{
*sector_count = QspiPsu_NOR_Get_Device_Size(flash->driver) /
QspiPsu_NOR_Get_Sector_Size(flash->driver);
return 0;
}
static int xqspi_sector_info_by_off(
rtems_flashdev *flash,
off_t search_offset,
off_t *sector_offset,
size_t *sector_size
)
{
*sector_size = QspiPsu_NOR_Get_Sector_Size(flash->driver);
*sector_offset = search_offset - (search_offset%((off_t)(*sector_size)));
return 0;
}
static int xqspi_write_block_size(
rtems_flashdev *flash,
size_t *write_block_size
@@ -181,6 +203,8 @@ rtems_flashdev* xqspi_flash_init(XQspiPsu *xQspiDev)
flash->page_info_by_index = &xqspi_page_info_by_index;
flash->page_count = &xqspi_page_count;
flash->write_block_size = &xqspi_write_block_size;
flash->sector_count = &xqspi_sector_count;
flash->sector_info_by_offset = &xqspi_sector_info_by_off;
flash->region_table = ftable;
return flash;

View File

@@ -106,7 +106,7 @@ static uint32_t rtems_flashdev_ioctl_jedec_id(
rtems_flashdev *flash
);
static uint32_t rtems_flashdev_ioctl_flash_type(
static int rtems_flashdev_ioctl_flash_type(
rtems_flashdev *flash,
void *arg
);
@@ -728,7 +728,7 @@ static int rtems_flashdev_ioctl_erase(
int status;
if ( flash->erase == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
}
erase_args_1 = (rtems_flashdev_region *) arg;
@@ -884,14 +884,14 @@ static uint32_t rtems_flashdev_ioctl_jedec_id( rtems_flashdev *flash )
}
}
static uint32_t rtems_flashdev_ioctl_flash_type(
static int rtems_flashdev_ioctl_flash_type(
rtems_flashdev *flash,
void *arg
)
{
rtems_flashdev_flash_type *type = (rtems_flashdev_flash_type*)arg;
if ( flash->flash_type == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->flash_type )( flash, type );
}
@@ -908,7 +908,7 @@ static int rtems_flashdev_ioctl_pageinfo_offset(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_info_by_offset == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
page_info = (rtems_flashdev_ioctl_page_info *) arg;
return ( *flash->page_info_by_offset )( flash,
@@ -927,7 +927,7 @@ static int rtems_flashdev_ioctl_pageinfo_index( rtems_flashdev *flash,
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_info_by_index == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
page_info = (rtems_flashdev_ioctl_page_info *) arg;
return ( *flash->page_info_by_index )( flash,
@@ -943,7 +943,7 @@ static int rtems_flashdev_ioctl_page_count( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_count == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->page_count )( flash, ( (int *) arg ) );
}
@@ -958,7 +958,7 @@ static int rtems_flashdev_ioctl_write_block_size(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->write_block_size == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->write_block_size )( flash, ( (size_t *) arg ) );
}
@@ -975,7 +975,7 @@ static int rtems_flashdev_ioctl_sectorinfo_offset(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_info_by_offset == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
sector_info = (rtems_flashdev_ioctl_sector_info *) arg;
return ( *flash->sector_info_by_offset )( flash,
@@ -991,7 +991,7 @@ static int rtems_flashdev_ioctl_sector_count( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_count == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->sector_count )( flash, ( (int *) arg ) );
}
@@ -1042,7 +1042,7 @@ static int rtems_flashdev_ioctl_oob_bytes_per_page(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_bytes_per_page == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->oob_bytes_per_page )( flash, ( (size_t *) arg ) );
}
@@ -1055,7 +1055,7 @@ static int rtems_flashdev_ioctl_oob_read( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_read == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
rw_info = (rtems_flashdev_ioctl_oob_rw_info *) arg;
return ( *flash->oob_read )(
@@ -1154,7 +1154,7 @@ static int rtems_flashdev_ioctl_oob_write( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_write == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
rw_info = (rtems_flashdev_ioctl_oob_rw_info *) arg;
return ( *flash->oob_write )(
@@ -1193,7 +1193,7 @@ static int rtems_flashdev_ioctl_sector_mark_bad(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_mark_bad == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->sector_mark_bad)( flash, *(uintptr_t *) arg );
}
@@ -1229,7 +1229,7 @@ static int rtems_flashdev_ioctl_sectorhealth(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_health == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
sector_health = arg;
return ( *flash->sector_health)( flash, sector_health->location, &sector_health->sector_bad );

View File

@@ -48,6 +48,7 @@
#ifdef CONFIGURE_FILESYSTEM_ALL
#define CONFIGURE_FILESYSTEM_DOSFS
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_FILESYSTEM_FTPFS
#define CONFIGURE_FILESYSTEM_IMFS
#define CONFIGURE_FILESYSTEM_JFFS2
@@ -108,6 +109,10 @@
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_DOSFS"
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FATFS"
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FTPFS"
#endif
@@ -139,6 +144,10 @@
#include <rtems/dosfs.h>
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#include <rtems/fatfs.h>
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
#include <rtems/ftpfs.h>
#endif
@@ -297,6 +306,9 @@ const rtems_filesystem_table_t rtems_filesystem_table[] = {
#ifdef CONFIGURE_FILESYSTEM_DOSFS
{ RTEMS_FILESYSTEM_TYPE_DOSFS, rtems_dosfs_initialize },
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
{ RTEMS_FILESYSTEM_TYPE_FATFS, rtems_fatfs_initialize },
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
{ RTEMS_FILESYSTEM_TYPE_FTPFS, rtems_ftpfs_initialize },
#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_NFS "nfs"
#define RTEMS_FILESYSTEM_TYPE_DOSFS "dosfs"
#define RTEMS_FILESYSTEM_TYPE_FATFS "fatfs"
#define RTEMS_FILESYSTEM_TYPE_RFS "rfs"
#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}
copyrights:
- 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:
- ${COVERAGE_COMPILER_FLAGS}
enabled-by: true
@@ -118,6 +141,7 @@ install:
- cpukit/include/rtems/devzero.h
- cpukit/include/rtems/diskdevs.h
- cpukit/include/rtems/dosfs.h
- cpukit/include/rtems/fatfs.h
- cpukit/include/rtems/dumpbuf.h
- cpukit/include/rtems/endian.h
- cpukit/include/rtems/error.h
@@ -861,6 +885,13 @@ source:
- cpukit/libfs/src/dosfs/msdos_rename.c
- cpukit/libfs/src/dosfs/msdos_rmnod.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/imfs_add_node.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
- role: build-dependency
uid: librfs
- role: build-dependency
uid: libfatfs
- role: build-dependency
uid: fsbdpart01
- role: build-dependency
@@ -41,6 +43,8 @@ links:
uid: fsdosfssync01
- role: build-dependency
uid: fsdosfswrite01
- role: build-dependency
uid: fsfatfssync01
- role: build-dependency
uid: fsfseeko01
- role: build-dependency
@@ -221,6 +225,32 @@ links:
uid: mrfsfstime
- role: build-dependency
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
use-after: []
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>