mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-18 09:19:17 +01:00
9e79c9d99b
* code cleanup
1026 lines
33 KiB
C++
1026 lines
33 KiB
C++
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/iosupport.h>
|
|
#include <sys/errno.h>
|
|
#include <fcntl.h>
|
|
#include <ogcsys.h>
|
|
#include <new>
|
|
#include "ramdisk.h"
|
|
|
|
#define NAMELENMAX 0x80
|
|
#define MAXPATHLEN 0x100
|
|
|
|
|
|
|
|
|
|
class RAMDISK_PARTITION;
|
|
class RAMDISK_LINK_ENTRY;
|
|
class RAMDISK_DIR_ENTRY;
|
|
class RAMDISK_FILE_ENTRY;
|
|
class RAMDISK_BASE_ENTRY
|
|
{
|
|
public:
|
|
RAMDISK_BASE_ENTRY( const char *Name, RAMDISK_DIR_ENTRY *Parent );
|
|
virtual ~RAMDISK_BASE_ENTRY();
|
|
void Rename( const char *Name );
|
|
RAMDISK_LINK_ENTRY *IsLink();
|
|
RAMDISK_DIR_ENTRY *IsDir();
|
|
RAMDISK_FILE_ENTRY *IsFile();
|
|
RAMDISK_PARTITION *GetPartition();
|
|
char *name;
|
|
RAMDISK_DIR_ENTRY *parent;
|
|
RAMDISK_BASE_ENTRY *next;
|
|
};
|
|
class RAMDISK_LINK_ENTRY : public RAMDISK_BASE_ENTRY
|
|
{
|
|
public:
|
|
RAMDISK_LINK_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent, RAMDISK_BASE_ENTRY *Link );
|
|
RAMDISK_BASE_ENTRY *link;
|
|
};
|
|
class RAMDISK_DIR_ENTRY : public RAMDISK_BASE_ENTRY
|
|
{
|
|
public:
|
|
RAMDISK_DIR_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent );
|
|
~RAMDISK_DIR_ENTRY();
|
|
RAMDISK_BASE_ENTRY *FindEntry( const char* Path );
|
|
RAMDISK_FILE_ENTRY *CreateFile( const char *Filename );
|
|
RAMDISK_DIR_ENTRY *CreateDir( const char *Filename );
|
|
void RemoveEntry( RAMDISK_BASE_ENTRY *Entry );
|
|
RAMDISK_BASE_ENTRY *first;
|
|
RAMDISK_BASE_ENTRY *last;
|
|
};
|
|
typedef struct
|
|
{
|
|
RAMDISK_DIR_ENTRY *dir;
|
|
RAMDISK_BASE_ENTRY *current_entry;
|
|
} DIR_STRUCT;
|
|
|
|
|
|
class RAMDISK_PARTITION : public RAMDISK_DIR_ENTRY
|
|
{
|
|
public:
|
|
RAMDISK_PARTITION( const char *Mountpoint, bool AutoMount );
|
|
RAMDISK_BASE_ENTRY *FindEntry( const char* Path );
|
|
RAMDISK_DIR_ENTRY *FindPath( const char* Path, const char **Basename = NULL );
|
|
RAMDISK_DIR_ENTRY *cwd;
|
|
bool automount;
|
|
};
|
|
|
|
class FILE_DATA
|
|
{
|
|
public:
|
|
FILE_DATA( size_t Len );
|
|
~FILE_DATA();
|
|
u8 *data;
|
|
size_t len;
|
|
FILE_DATA *next;
|
|
};
|
|
typedef struct
|
|
{
|
|
RAMDISK_FILE_ENTRY *file;
|
|
bool isLink;
|
|
u32 current_pos;
|
|
bool read;
|
|
bool write;
|
|
} FILE_STRUCT;
|
|
class RAMDISK_FILE_ENTRY : public RAMDISK_BASE_ENTRY
|
|
{
|
|
public:
|
|
RAMDISK_FILE_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent );
|
|
~RAMDISK_FILE_ENTRY();
|
|
bool Truncate( size_t newLen );
|
|
bool AddCluster( size_t size );
|
|
size_t Read( struct _reent *r, FILE_STRUCT *fileStruct, char *ptr, size_t len );
|
|
size_t Write( struct _reent *r, FILE_STRUCT *fileStruct, const char *ptr, size_t len );
|
|
size_t file_len;
|
|
size_t cluster_len;
|
|
FILE_DATA *first_cluster;
|
|
FILE_DATA *last_cluster;
|
|
};
|
|
|
|
RAMDISK_LINK_ENTRY::RAMDISK_LINK_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent, RAMDISK_BASE_ENTRY *Link )
|
|
:
|
|
RAMDISK_BASE_ENTRY( Name, Parent ),
|
|
link( Link )
|
|
{
|
|
|
|
}
|
|
|
|
|
|
RAMDISK_BASE_ENTRY::RAMDISK_BASE_ENTRY( const char *Name, RAMDISK_DIR_ENTRY *Parent )
|
|
:
|
|
name( strdup( Name ) ),
|
|
parent( Parent ),
|
|
next( NULL )
|
|
{}
|
|
RAMDISK_BASE_ENTRY::~RAMDISK_BASE_ENTRY()
|
|
{
|
|
free( name );
|
|
if ( parent ) parent->RemoveEntry( this );
|
|
}
|
|
void RAMDISK_BASE_ENTRY::Rename( const char *Name )
|
|
{
|
|
free( name );
|
|
name = strdup( Name );
|
|
}
|
|
|
|
inline RAMDISK_LINK_ENTRY *RAMDISK_BASE_ENTRY::IsLink()
|
|
{
|
|
return dynamic_cast<RAMDISK_LINK_ENTRY*>( this );
|
|
}
|
|
inline RAMDISK_DIR_ENTRY *RAMDISK_BASE_ENTRY::IsDir()
|
|
{
|
|
RAMDISK_LINK_ENTRY *lentry = dynamic_cast<RAMDISK_LINK_ENTRY*>( this );
|
|
return dynamic_cast<RAMDISK_DIR_ENTRY*>( lentry ? lentry->link : this );
|
|
}
|
|
inline RAMDISK_FILE_ENTRY *RAMDISK_BASE_ENTRY::IsFile()
|
|
{
|
|
RAMDISK_LINK_ENTRY *lentry = dynamic_cast<RAMDISK_LINK_ENTRY*>( this );
|
|
return dynamic_cast<RAMDISK_FILE_ENTRY*>( lentry ? lentry->link : this );
|
|
}
|
|
RAMDISK_PARTITION *RAMDISK_BASE_ENTRY::GetPartition()
|
|
{
|
|
for ( RAMDISK_BASE_ENTRY *entry = this; entry; entry = entry->parent )
|
|
if ( entry->parent == NULL )
|
|
return dynamic_cast<RAMDISK_PARTITION *>( entry );
|
|
return NULL;
|
|
}
|
|
|
|
|
|
RAMDISK_DIR_ENTRY::RAMDISK_DIR_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent )
|
|
:
|
|
RAMDISK_BASE_ENTRY( Name, Parent )
|
|
{
|
|
RAMDISK_BASE_ENTRY* entry = new RAMDISK_LINK_ENTRY( ".", this, this );
|
|
first = entry;
|
|
last = entry;
|
|
if ( parent )
|
|
{
|
|
entry = new RAMDISK_LINK_ENTRY( "..", this, parent );
|
|
first->next = entry;
|
|
last = entry;
|
|
}
|
|
}
|
|
RAMDISK_DIR_ENTRY::~RAMDISK_DIR_ENTRY()
|
|
{
|
|
while ( first )
|
|
{
|
|
first->parent = NULL; // ~RAMDISK_BASE_ENTRY() no calls RemoveEntry()
|
|
RAMDISK_BASE_ENTRY* next = first->next;
|
|
delete first;
|
|
first = next;
|
|
}
|
|
}
|
|
|
|
RAMDISK_BASE_ENTRY *RAMDISK_DIR_ENTRY::FindEntry( const char* Path )
|
|
{
|
|
const char* dirpath = Path;
|
|
const char* cptr;
|
|
while ( dirpath[0] == '/' ) dirpath++; // move past leading '/'
|
|
|
|
if ( dirpath[0] == '\0' ) // this path is found
|
|
return this;
|
|
|
|
cptr = strchr( dirpath, '/' ); // find next '/'
|
|
if ( cptr == NULL )
|
|
cptr = strchr( dirpath, '\0' ); // cptr at end
|
|
for ( RAMDISK_BASE_ENTRY *curr = first; curr; curr = curr->next )
|
|
{
|
|
if ( strncmp( curr->name, dirpath, cptr - dirpath ) == 0 )
|
|
{
|
|
if ( RAMDISK_DIR_ENTRY *dir = curr->IsDir() )
|
|
return dir->FindEntry( cptr );
|
|
else
|
|
return curr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
RAMDISK_FILE_ENTRY *RAMDISK_DIR_ENTRY::CreateFile( const char *Filename )
|
|
{
|
|
try
|
|
{
|
|
RAMDISK_FILE_ENTRY* file = new RAMDISK_FILE_ENTRY( Filename, this );
|
|
if ( !first ) first = file;
|
|
last->next = file;
|
|
last = file;
|
|
return file;
|
|
}
|
|
catch ( ... ) { return NULL; }
|
|
}
|
|
RAMDISK_DIR_ENTRY *RAMDISK_DIR_ENTRY::CreateDir( const char *Filename )
|
|
{
|
|
try
|
|
{
|
|
RAMDISK_DIR_ENTRY* dir = new RAMDISK_DIR_ENTRY( Filename, this );
|
|
if ( !first ) first = dir;
|
|
last->next = dir;
|
|
last = dir;
|
|
return dir;
|
|
}
|
|
catch ( ... ) { return NULL; }
|
|
}
|
|
void RAMDISK_DIR_ENTRY::RemoveEntry( RAMDISK_BASE_ENTRY *Entry )
|
|
{
|
|
RAMDISK_BASE_ENTRY **p_last = NULL;
|
|
for ( RAMDISK_BASE_ENTRY **curr = &first; *curr; curr = &( ( *curr )->next ) )
|
|
{
|
|
if ( *curr == Entry )
|
|
{
|
|
*curr = Entry->next;
|
|
if ( Entry->next == NULL ) // entry is last
|
|
last = *p_last;
|
|
break;
|
|
}
|
|
p_last = curr;
|
|
}
|
|
}
|
|
|
|
RAMDISK_PARTITION::RAMDISK_PARTITION( const char *Mountpoint, bool AutoMount ) :
|
|
RAMDISK_DIR_ENTRY( Mountpoint, NULL ),
|
|
cwd( this ),
|
|
automount( AutoMount )
|
|
{
|
|
}
|
|
|
|
RAMDISK_BASE_ENTRY * RAMDISK_PARTITION::FindEntry( const char* path )
|
|
{
|
|
char *cptr;
|
|
if ( ( cptr = strchr( path, ':' ) ) )
|
|
path = cptr + 1; //move path past any device names
|
|
if ( strchr( path, ':' ) != NULL )
|
|
{
|
|
// r->_errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
if ( *path == '/' ) // if first character is '/' use absolute root path
|
|
return RAMDISK_DIR_ENTRY::FindEntry( path );
|
|
else // else use current working dir
|
|
return cwd->FindEntry( path );
|
|
}
|
|
RAMDISK_DIR_ENTRY * RAMDISK_PARTITION::FindPath( const char* path, const char **basename )
|
|
{
|
|
int pathLen = strlen( path );
|
|
char dirfilename[pathLen+1]; // to hold a full path
|
|
const char *cptr = path + pathLen; // find the end...
|
|
const char *filename = NULL; // to hold filename
|
|
while ( cptr-- > path ) //search till start
|
|
{
|
|
if ( ( *cptr == '/' ) || ( *cptr == ':' ) ) // split at either / or : (whichever comes first form the end!)
|
|
{
|
|
cptr++;
|
|
strlcpy( dirfilename, path, 1 + cptr - path ); //copy string up till and including / or :
|
|
filename = cptr; //filename = now remainder of string
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !filename )
|
|
{
|
|
filename = path; //filename = complete path
|
|
dirfilename[0] = 0; //make directory path ""
|
|
}
|
|
RAMDISK_BASE_ENTRY *entry = FindEntry( dirfilename );
|
|
if ( entry )
|
|
{
|
|
if ( basename ) *basename = filename;
|
|
return entry->IsDir();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
FILE_DATA::FILE_DATA( size_t Len ) :
|
|
next( NULL )
|
|
{
|
|
data = new u8[Len];
|
|
len = Len;
|
|
memset( data, 0, len );
|
|
}
|
|
FILE_DATA::~FILE_DATA()
|
|
{
|
|
delete [] data;
|
|
delete next;
|
|
}
|
|
|
|
|
|
RAMDISK_FILE_ENTRY::RAMDISK_FILE_ENTRY( const char* Name, RAMDISK_DIR_ENTRY *Parent )
|
|
:
|
|
RAMDISK_BASE_ENTRY( Name, Parent ),
|
|
file_len( 0 ),
|
|
cluster_len( 0 ),
|
|
first_cluster( NULL ),
|
|
last_cluster( NULL )
|
|
{}
|
|
RAMDISK_FILE_ENTRY::~RAMDISK_FILE_ENTRY()
|
|
{
|
|
Truncate( 0 );
|
|
}
|
|
#define CLUSTER_SIZE 4*1024
|
|
|
|
bool RAMDISK_FILE_ENTRY::Truncate( size_t newSize )
|
|
{
|
|
if ( newSize > cluster_len )
|
|
{
|
|
// Expanding the file over cluster_len
|
|
return AddCluster( newSize - cluster_len );
|
|
}
|
|
else if ( newSize < file_len )
|
|
{
|
|
// Shrinking the file
|
|
FILE_DATA *prev_cluster = NULL;
|
|
size_t len = 0;
|
|
for ( FILE_DATA **p_cluster = &first_cluster; *p_cluster; p_cluster = &( *p_cluster )->next )
|
|
{
|
|
if ( len >= newSize )
|
|
{
|
|
last_cluster = prev_cluster;
|
|
delete *p_cluster;
|
|
( *p_cluster ) = NULL;
|
|
break;
|
|
}
|
|
len += ( *p_cluster )->len;
|
|
prev_cluster = *p_cluster;
|
|
}
|
|
}
|
|
file_len = newSize;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool RAMDISK_FILE_ENTRY::AddCluster( size_t len )
|
|
{
|
|
if ( len < CLUSTER_SIZE )
|
|
len = CLUSTER_SIZE;
|
|
try
|
|
{
|
|
*( last_cluster ? &last_cluster->next : &first_cluster ) = last_cluster = new FILE_DATA( len );
|
|
cluster_len += len;
|
|
return true;
|
|
}
|
|
catch ( ... ) { return false; }
|
|
}
|
|
|
|
size_t RAMDISK_FILE_ENTRY::Read( struct _reent *r, FILE_STRUCT *fileStruct, char *ptr, size_t len )
|
|
{
|
|
if ( !fileStruct->read )
|
|
{
|
|
r->_errno = EBADF;
|
|
return 0;
|
|
}
|
|
// Short circuit cases where len is 0 (or less)
|
|
if ( len <= 0 )
|
|
return 0;
|
|
// Don't try to read if the read pointer is past the end of file
|
|
if ( fileStruct->current_pos >= file_len )
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
return 0;
|
|
}
|
|
|
|
// Don't read past end of file
|
|
if ( len + fileStruct->current_pos > file_len )
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
len = fileStruct->current_pos - file_len;
|
|
}
|
|
|
|
off_t pos = fileStruct->current_pos;
|
|
size_t readed = 0;
|
|
size_t max_len = file_len;
|
|
for ( FILE_DATA *cluster = first_cluster; cluster; cluster = cluster->next )
|
|
{
|
|
if ( pos > cluster->len )
|
|
{
|
|
pos -= cluster->len;
|
|
max_len -= cluster->len;
|
|
}
|
|
else
|
|
{
|
|
size_t read = max_len;
|
|
if ( read > cluster->len )
|
|
read = cluster->len;
|
|
read -= pos;
|
|
if ( read > len )
|
|
read = len;
|
|
memcpy( ptr, &( cluster->data[pos] ), read );
|
|
readed += read;
|
|
ptr += read;
|
|
len -= read;
|
|
if ( len == 0 )
|
|
break;
|
|
pos -= cluster->len;
|
|
max_len -= cluster->len;
|
|
|
|
}
|
|
}
|
|
fileStruct->current_pos += readed;
|
|
return readed;
|
|
}
|
|
size_t RAMDISK_FILE_ENTRY::Write( struct _reent *r, FILE_STRUCT *fileStruct, const char *ptr, size_t len )
|
|
{
|
|
|
|
if ( !fileStruct->write )
|
|
{
|
|
r->_errno = EBADF;
|
|
return 0;
|
|
}
|
|
// Short circuit cases where len is 0 (or less)
|
|
if ( len <= 0 )
|
|
return 0;
|
|
|
|
off_t pos = fileStruct->current_pos;
|
|
if ( cluster_len < ( pos + len ) && !AddCluster( ( pos + len ) - cluster_len ) )
|
|
{
|
|
// Couldn't get a cluster, so abort
|
|
r->_errno = ENOSPC;
|
|
return 0;
|
|
}
|
|
if ( file_len < ( pos + len ) )
|
|
file_len = ( pos + len );
|
|
|
|
size_t written = 0;
|
|
size_t max_len = cluster_len;
|
|
for ( FILE_DATA *cluster = first_cluster; cluster; cluster = cluster->next )
|
|
{
|
|
if ( pos > cluster->len )
|
|
{
|
|
pos -= cluster->len;
|
|
max_len -= cluster->len;
|
|
}
|
|
else
|
|
{
|
|
size_t write = cluster->len - pos;
|
|
if ( write > len ) write = len;
|
|
memcpy( &( cluster->data[pos] ), ptr, write );
|
|
written += write;
|
|
ptr += write;
|
|
len -= write;
|
|
if ( len == 0 )
|
|
break;
|
|
pos -= cluster->len;
|
|
max_len -= cluster->len;
|
|
}
|
|
}
|
|
fileStruct->current_pos += written;
|
|
return written;
|
|
}
|
|
|
|
|
|
static int ramdiskFS_open_r( struct _reent *r, void *fileStruct, const char *path, int flags, int mode );
|
|
static int ramdiskFS_close_r( struct _reent *r, int fd );
|
|
static int ramdiskFS_write_r( struct _reent *r, int fd, const char *ptr, size_t len );
|
|
static int ramdiskFS_read_r( struct _reent *r, int fd, char *ptr, size_t len );
|
|
static off_t ramdiskFS_seek_r( struct _reent *r, int fd, off_t pos, int dir );
|
|
static int ramdiskFS_fstat_r( struct _reent *r, int fd, struct stat *st );
|
|
static int ramdiskFS_stat_r( struct _reent *r, const char *file, struct stat *st );
|
|
static int ramdiskFS_unlink_r( struct _reent *r, const char *name );
|
|
static int ramdiskFS_chdir_r( struct _reent *r, const char *name );
|
|
static int ramdiskFS_mkdir_r( struct _reent *r, const char *path, int mode );
|
|
|
|
static DIR_ITER* ramdiskFS_diropen_r( struct _reent *r, DIR_ITER *dirState, const char *path );
|
|
static int ramdiskFS_dirreset_r( struct _reent *r, DIR_ITER *dirState );
|
|
static int ramdiskFS_dirnext_r( struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st );
|
|
static int ramdiskFS_dirclose_r( struct _reent *r, DIR_ITER *dirState );
|
|
|
|
static int ramdiskFS_ftruncate_r( struct _reent *r, int fd, off_t len );
|
|
|
|
|
|
static devoptab_t ramdiskFS_devoptab =
|
|
{
|
|
"ramdisk",
|
|
sizeof( FILE_STRUCT ), // int structSize;
|
|
&ramdiskFS_open_r, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int
|
|
// flags,int mode);
|
|
&ramdiskFS_close_r, // int (*close_r)(struct _reent *r, int fd);
|
|
&ramdiskFS_write_r, // int (*write_r)(struct _reent *r, int fd, const char *ptr, int len);
|
|
&ramdiskFS_read_r, // int (*read_r)(struct _reent *r, int fd, char *ptr, int len);
|
|
&ramdiskFS_seek_r, // off_t (*seek_r)(struct _reent *r, off_t fd, int pos, int dir);
|
|
&ramdiskFS_fstat_r, // int (*fstat_r)(struct _reent *r, int fd, struct stat *st);
|
|
&ramdiskFS_stat_r, // int (*stat_r)(struct _reent *r, const char *file, struct stat *st);
|
|
NULL, // int (*link_r)(struct _reent *r, const char *existing, const char *newLink);
|
|
&ramdiskFS_unlink_r, // int (*unlink_r)(struct _reent *r, const char *name);
|
|
&ramdiskFS_chdir_r, // int (*chdir_r)(struct _reent *r, const char *name);
|
|
NULL, // int (*rename_r) (struct _reent *r, const char *oldName, const char *newName);
|
|
ramdiskFS_mkdir_r, // int (*mkdir_r) (struct _reent *r, const char *path, int mode);
|
|
sizeof( DIR_STRUCT ), // int dirStateSize;
|
|
&ramdiskFS_diropen_r, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
|
|
&ramdiskFS_dirreset_r, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
|
|
&ramdiskFS_dirnext_r, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename,
|
|
// struct stat *filestat);
|
|
&ramdiskFS_dirclose_r, // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
|
|
NULL, // statvfs_r
|
|
&ramdiskFS_ftruncate_r, // int (*ftruncate_r)(struct _reent *r, int fd, off_t len);
|
|
|
|
NULL, // fsync_r,
|
|
NULL // Device data
|
|
};
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static inline RAMDISK_PARTITION* ramdiskFS_getPartitionFromPath( const char* path )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
const devoptab_t *devops = GetDeviceOpTab( path );
|
|
if ( !devops )
|
|
return NULL;
|
|
return *( ( RAMDISK_PARTITION** )devops->deviceData );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
// File functions
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_open_r( struct _reent *r, void *file_Struct, const char *path, int flags, int mode )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )file_Struct;
|
|
|
|
|
|
RAMDISK_PARTITION *partition = ramdiskFS_getPartitionFromPath( path );
|
|
if ( partition == NULL )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if ( ( flags & 0x03 ) == O_RDONLY )
|
|
{
|
|
// Open the file for read-only access
|
|
fileStruct->read = true;
|
|
fileStruct->write = false;
|
|
}
|
|
else if ( ( flags & 0x03 ) == O_WRONLY )
|
|
{
|
|
// Open file for write only access
|
|
fileStruct->read = false;
|
|
fileStruct->write = true;
|
|
}
|
|
else if ( ( flags & 0x03 ) == O_RDWR )
|
|
{
|
|
// Open file for read/write access
|
|
fileStruct->read = true;
|
|
fileStruct->write = true;
|
|
}
|
|
else
|
|
{
|
|
r->_errno = EACCES;
|
|
return -1;
|
|
}
|
|
RAMDISK_BASE_ENTRY *entry = partition->FindEntry( path );
|
|
|
|
// The file shouldn't exist if we are trying to create it
|
|
if ( entry && ( flags & O_CREAT ) && ( flags & O_EXCL ) )
|
|
{
|
|
r->_errno = EEXIST;
|
|
return -1;
|
|
}
|
|
// It should not be a directory if we're openning a file,
|
|
if ( entry && entry->IsDir() )
|
|
{
|
|
r->_errno = EISDIR;
|
|
return -1;
|
|
}
|
|
|
|
fileStruct->isLink = entry ? entry->IsLink() : false;
|
|
fileStruct->file = entry ? entry->IsFile() : NULL;
|
|
if ( !fileStruct->file ) // entry not exists
|
|
{
|
|
if ( flags & O_CREAT )
|
|
{
|
|
const char *filename;
|
|
RAMDISK_DIR_ENTRY *dir = partition->FindPath( path, &filename );
|
|
if ( !dir )
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
fileStruct->file = dir->CreateFile( filename );
|
|
}
|
|
else
|
|
{
|
|
// file doesn't exist, and we aren't creating it
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
}
|
|
if ( fileStruct->file )
|
|
{
|
|
fileStruct->current_pos = 0;
|
|
// Truncate the file if requested
|
|
if ( ( flags & O_TRUNC ) && fileStruct->write )
|
|
fileStruct->file->Truncate( 0 );
|
|
if ( flags & O_APPEND )
|
|
fileStruct->current_pos = fileStruct->file->file_len;
|
|
return 0;
|
|
}
|
|
r->_errno = ENOENT;
|
|
return( -1 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_close_r( struct _reent *r, int fd )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
return( 0 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_read_r( struct _reent *r, int fd, char *ptr, size_t len )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )fd;
|
|
return fileStruct->file->Read( r, fileStruct, ptr, len );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_write_r( struct _reent *r, int fd, const char *ptr, size_t len )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )fd;
|
|
return fileStruct->file->Write( r, fileStruct, ptr, len );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static off_t ramdiskFS_seek_r( struct _reent *r, int fd, off_t pos, int dir )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
//need check for eof here...
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )fd;
|
|
|
|
switch ( dir )
|
|
{
|
|
case SEEK_SET:
|
|
break;
|
|
case SEEK_CUR:
|
|
pos += fileStruct->current_pos;
|
|
break;
|
|
case SEEK_END:
|
|
pos += fileStruct->file->file_len; // set start to end of file
|
|
break;
|
|
default:
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
return fileStruct->current_pos = pos;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_fstat_r( struct _reent *r, int fd, struct stat *st )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )fd;
|
|
|
|
st->st_mode = fileStruct->isLink ? S_IFLNK : S_IFREG;
|
|
st->st_size = fileStruct->file->file_len;
|
|
return( 0 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_stat_r( struct _reent *r, const char *file, struct stat *st )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT fileStruct;
|
|
DIR_STRUCT dirStruct;
|
|
DIR_ITER dirState;
|
|
dirState.dirStruct = &dirStruct; //create a temp dirstruct
|
|
int ret;
|
|
|
|
if ( ramdiskFS_open_r( r, &fileStruct, file, 0, 0 ) == 0 )
|
|
{
|
|
ret = ramdiskFS_fstat_r( r, ( int ) & fileStruct, st );
|
|
ramdiskFS_close_r( r, ( int )&fileStruct );
|
|
return( ret );
|
|
}
|
|
else if ( ( ramdiskFS_diropen_r( r, &dirState, file ) != NULL ) )
|
|
{
|
|
st->st_mode = S_IFDIR;
|
|
ramdiskFS_dirclose_r( r, &dirState );
|
|
return( 0 );
|
|
}
|
|
r->_errno = ENOENT;
|
|
return( -1 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_unlink_r( struct _reent *r, const char *name )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
RAMDISK_PARTITION *partition = ramdiskFS_getPartitionFromPath( name );
|
|
if ( partition == NULL )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
RAMDISK_BASE_ENTRY *entry = partition->FindEntry( name );
|
|
if ( !entry )
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if ( entry->IsLink() )
|
|
{
|
|
if ( entry->name[0] == '.' && ( entry->name[1] == '\0' || ( entry->name[1] == '.' && entry->name[2] == '\0' ) ) )
|
|
{
|
|
r->_errno = EPERM;
|
|
return -1;
|
|
}
|
|
delete entry;
|
|
return 0;
|
|
}
|
|
|
|
if ( RAMDISK_DIR_ENTRY *dir = entry->IsDir() )
|
|
{
|
|
for ( RAMDISK_BASE_ENTRY *entry = dir->first; entry; entry = entry->next )
|
|
{
|
|
if ( !( entry->name[0] == '.' && ( entry->name[1] == '\0'
|
|
|| ( entry->name[1] == '.' && entry->name[2] == '\0' ) ) ) )
|
|
{
|
|
r->_errno = EPERM;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
delete entry;
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_chdir_r( struct _reent *r, const char *name )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
DIR_STRUCT dirStruct;
|
|
DIR_ITER dirState;
|
|
dirState.dirStruct = &dirStruct;
|
|
if ( ( name == NULL ) )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if ( ( ramdiskFS_diropen_r( r, &dirState, name ) == NULL ) )
|
|
return -1;
|
|
|
|
RAMDISK_PARTITION *partition = dirStruct.dir->GetPartition();
|
|
if ( partition == NULL )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
partition->cwd = dirStruct.dir;
|
|
ramdiskFS_dirclose_r( r, &dirState );
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_mkdir_r( struct _reent *r, const char *path, int mode )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
RAMDISK_PARTITION *partition = ramdiskFS_getPartitionFromPath( path );
|
|
if ( partition == NULL )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
RAMDISK_BASE_ENTRY *entry = partition->FindEntry( path );
|
|
if ( entry )
|
|
{
|
|
r->_errno = EEXIST;
|
|
return -1;
|
|
}
|
|
const char *filename;
|
|
RAMDISK_DIR_ENTRY *dir = partition->FindPath( path, &filename );
|
|
if ( !dir )
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
dir->CreateDir( filename );
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
// Directory functions
|
|
//---------------------------------------------------------------------------------
|
|
static DIR_ITER* ramdiskFS_diropen_r( struct _reent *r, DIR_ITER *dirState, const char *path )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
|
|
DIR_STRUCT *dirStruct = ( DIR_STRUCT* )dirState->dirStruct;
|
|
char *cptr;
|
|
|
|
RAMDISK_PARTITION *partition = ramdiskFS_getPartitionFromPath( path );
|
|
if ( partition == NULL )
|
|
{
|
|
r->_errno = ENODEV;
|
|
return NULL;
|
|
}
|
|
|
|
if ( ( cptr = strchr( path, ':' ) ) )
|
|
path = cptr + 1; //move path past any device names
|
|
if ( strchr( path, ':' ) != NULL )
|
|
{
|
|
r->_errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if ( *path == '/' ) //if first character is '/' use absolute root path
|
|
dirStruct->dir = partition; //first root dir
|
|
else
|
|
dirStruct->dir = partition->cwd; //else use current working dir
|
|
|
|
RAMDISK_BASE_ENTRY *entry = dirStruct->dir->FindEntry( path );
|
|
if ( entry == NULL )
|
|
{
|
|
r->_errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
dirStruct->dir = entry->IsDir();
|
|
if ( dirStruct->dir == NULL )
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
return NULL;
|
|
}
|
|
dirStruct->current_entry = dirStruct->dir->first;
|
|
return dirState;
|
|
}
|
|
|
|
/*Consts containing relative system path strings*/
|
|
//reset dir to start of entry selected by dirStruct->cur_dir_id
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_dirreset_r( struct _reent *r, DIR_ITER *dirState )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
DIR_STRUCT *dirStruct = ( DIR_STRUCT* )dirState->dirStruct;
|
|
dirStruct->current_entry = dirStruct->dir->first;
|
|
return( 0 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_dirnext_r( struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
DIR_STRUCT *dirStruct = ( DIR_STRUCT* )dirState->dirStruct;
|
|
// RAMDISK_BASE_ENTRY **dirStruct = (RAMDISK_BASE_ENTRY**)dirState->dirStruct;
|
|
|
|
if ( dirStruct->current_entry )
|
|
{
|
|
strcpy( filename, dirStruct->current_entry->name );
|
|
if ( dirStruct->current_entry->IsDir() )
|
|
{
|
|
if ( st ) st->st_mode = S_IFDIR;
|
|
}
|
|
else
|
|
{
|
|
if ( st ) st->st_mode = 0;
|
|
}
|
|
dirStruct->current_entry = dirStruct->current_entry->next;
|
|
return( 0 );
|
|
}
|
|
r->_errno = ENOENT;
|
|
return( -1 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_dirclose_r( struct _reent *r, DIR_ITER *dirState )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
return( 0 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
static int ramdiskFS_ftruncate_r( struct _reent *r, int fd, off_t len )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
FILE_STRUCT *fileStruct = ( FILE_STRUCT* )fd;
|
|
|
|
if ( len < 0 )
|
|
{
|
|
// Trying to truncate to a negative size
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
/*
|
|
if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE)
|
|
{
|
|
// Trying to extend the file beyond what supports
|
|
r->_errno = EFBIG;
|
|
return -1;
|
|
}
|
|
*/
|
|
if ( !fileStruct->write )
|
|
{
|
|
// Read-only file
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if ( fileStruct->file->Truncate( len ) )
|
|
return 0;
|
|
r->_errno = ENOSPC;
|
|
return -1;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
void ramdiskFS_Unmount( const char* mountpoint )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
RAMDISK_PARTITION *partition;
|
|
devoptab_t *devops = ( devoptab_t* )GetDeviceOpTab( mountpoint );
|
|
|
|
if ( !devops )
|
|
return;
|
|
|
|
// Perform a quick check to make sure we're dealing with a ramdiskFS_ controlled device
|
|
if ( devops->open_r != ramdiskFS_devoptab.open_r )
|
|
return;
|
|
|
|
if ( RemoveDevice ( mountpoint ) == -1 )
|
|
return;
|
|
|
|
partition = *( ( RAMDISK_PARTITION ** )devops->deviceData );
|
|
if ( partition->automount )
|
|
delete partition;
|
|
free ( devops );
|
|
}
|
|
extern "C" void ramdiskUnmount( const char *mountpoint )
|
|
{
|
|
ramdiskFS_Unmount( mountpoint );
|
|
}
|
|
//---------------------------------------------------------------------------------
|
|
int ramdiskFS_Mount( const char *mountpoint, void *handle )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
devoptab_t* devops;
|
|
char* nameCopy;
|
|
RAMDISK_PARTITION** partition;
|
|
char Mountpoint[100];
|
|
char *cptr;
|
|
|
|
strlcpy( Mountpoint, mountpoint, sizeof( Mountpoint ) );
|
|
int len = strlen( Mountpoint );
|
|
cptr = strchr( Mountpoint, ':' );
|
|
if ( cptr )
|
|
{
|
|
len = cptr - Mountpoint;
|
|
*++cptr = 0;
|
|
}
|
|
else
|
|
strlcat( Mountpoint, ":", sizeof( Mountpoint ) );
|
|
ramdiskFS_Unmount( Mountpoint );
|
|
if ( handle ) ramdiskFS_Unmount( ( ( RAMDISK_PARTITION* )handle )->name );
|
|
|
|
devops = ( devoptab_t* )malloc( sizeof( devoptab_t ) + sizeof( RAMDISK_PARTITION* ) + len + 1 );
|
|
if ( !devops )
|
|
return false;
|
|
|
|
partition = ( RAMDISK_PARTITION** )( devops + 1 ); // Use the space allocated at the end of the devoptab struct
|
|
// for storing the partition
|
|
nameCopy = ( char* )( partition + 1 ); // Use the space allocated at the end of the partition struct
|
|
// for storing the name
|
|
|
|
memcpy ( devops, &ramdiskFS_devoptab, sizeof( ramdiskFS_devoptab ) ); // Add an entry for this device to the devoptab table
|
|
|
|
strlcpy ( nameCopy, Mountpoint, len + 1 );
|
|
devops->name = nameCopy;
|
|
|
|
if ( handle )
|
|
{
|
|
*partition = ( RAMDISK_PARTITION* )handle;
|
|
( *partition )->Rename( Mountpoint );
|
|
}
|
|
else
|
|
*partition = new RAMDISK_PARTITION( Mountpoint, true );
|
|
devops->deviceData = partition;
|
|
|
|
if ( AddDevice( devops ) < 0 )
|
|
{
|
|
free( devops );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
extern "C" int ramdiskMount( const char *mountpoint, void *handle )
|
|
{
|
|
return ramdiskFS_Mount( mountpoint, handle );
|
|
}
|
|
//---------------------------------------------------------------------------------
|
|
extern "C" void* ramdiskCreate()
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
return new RAMDISK_PARTITION( "", false );
|
|
}
|
|
//---------------------------------------------------------------------------------
|
|
extern "C" void ramdiskDelete( void* Handle )
|
|
{
|
|
//---------------------------------------------------------------------------------
|
|
RAMDISK_PARTITION *partition = ( RAMDISK_PARTITION* )Handle;
|
|
if ( partition->automount == false )
|
|
ramdiskFS_Unmount( partition->name );
|
|
delete partition;
|
|
}
|
|
|