blob: 92fd0d9615cbff95275bf9aee5b5c9106a1897bd [file] [log] [blame]
/*
* FreeRTOS+FAT SL V1.0.1 (C) 2014 HCC Embedded
*
* The FreeRTOS+FAT SL license terms are different to the FreeRTOS license
* terms.
*
* FreeRTOS+FAT SL uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+FAT SL is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals who for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a commercial license before
* incorporating FreeRTOS+FAT SL into proprietary software for distribution in
* any form. Commercial licenses can be purchased from
* http://shop.freertos.org/fat_sl and do not require any source files to be
* changed.
*
* FreeRTOS+FAT SL is distributed in the hope that it will be useful. You
* cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as
* is'. FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the
* implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all
* conditions and terms, be they implied, expressed, or statutory.
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/FreeRTOS-Plus
*
*/
#include "../../api/fat_sl.h"
#include "../../psp/include/psp_string.h"
#include "dir.h"
#include "util.h"
#include "volume.h"
#include "drv.h"
#include "fat.h"
#include "file.h"
#include "../../version/ver_fat_sl.h"
#if VER_FAT_SL_MAJOR != 5 || VER_FAT_SL_MINOR != 2
#error Incompatible FAT_SL version number!
#endif
/****************************************************************************
*
* _f_findfilewc
*
* internal function to finding file in directory entry with or without
* wildcard
*
* INPUTS
*
* name - filename
* ext - fileextension
* pos - where to start searching, and contains current position
* pde - store back the directory entry pointer
* wc - wildcard checking?
*
* RETURNS
*
* 0 - if file was not found
* 1 - if file was found
*
***************************************************************************/
unsigned char _f_findfilewc ( char * name, char * ext, F_POS * pos, F_DIRENTRY * * pde, unsigned char wc )
{
while ( pos->cluster < F_CLUSTER_RESERVED )
{
for ( ; pos->sector < pos->sectorend ; pos->sector++ )
{
F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos );
if ( _f_readglsector( pos->sector ) )
{
return 0; /*not found*/
}
for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ )
{
unsigned char b, ok;
if ( !de->name[0] )
{
return 0; /*empty*/
}
if ( (unsigned char)( de->name[0] ) == 0xe5 )
{
continue; /*deleted*/
}
if ( de->attr & F_ATTR_VOLUME )
{
continue;
}
if ( wc )
{
for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
{
if ( name[b] == '*' )
{
break;
}
if ( name[b] != '?' )
{
if ( de->name[b] != name[b] )
{
ok = 0;
break;
}
}
}
if ( ok )
{
for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
{
if ( ext[b] == '*' )
{
if ( pde )
{
*pde = de;
}
return 1;
}
if ( ext[b] != '?' )
{
if ( de->ext[b] != ext[b] )
{
ok = 0;
break;
}
}
}
if ( ok )
{
if ( pde )
{
*pde = de;
}
return 1;
}
}
}
else
{
for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
{
if ( de->name[b] != name[b] )
{
ok = 0;
break;
}
}
if ( ok )
{
for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
{
if ( de->ext[b] != ext[b] )
{
ok = 0;
break;
}
}
if ( ok )
{
if ( pde )
{
*pde = de;
}
return 1;
}
}
}
}
pos->pos = 0;
}
if ( !pos->cluster )
{
if ( gl_volume.mediatype == F_FAT32_MEDIA )
{
pos->cluster = gl_volume.bootrecord.rootcluster;
}
else
{
return 0;
}
}
{
unsigned long nextcluster;
gl_volume.fatsector = (unsigned long)-1;
if ( _f_getclustervalue( pos->cluster, &nextcluster ) )
{
return 0; /*not found*/
}
if ( nextcluster >= F_CLUSTER_RESERVED )
{
return 0; /*eof*/
}
_f_clustertopos( nextcluster, pos );
}
} /* _f_findfilewc */
return 0;
}
/****************************************************************************
*
* _f_getfilename
*
* create a complete filename from name and extension
*
* INPUTS
*
* dest - where to store filename
* name - name of the file
* ext - extension of the file
*
***************************************************************************/
static void _f_getfilename ( char * dest, char * name, char * ext )
{
unsigned char a, len;
for ( len = a = F_MAXNAME ; a ; a--, len-- )
{
if ( name[a - 1] != ' ' )
{
break;
}
}
for ( a = 0 ; a < len ; a++ )
{
*dest++ = *name++;
}
for ( len = a = F_MAXEXT ; a ; a--, len-- )
{
if ( ext[a - 1] != ' ' )
{
break;
}
}
if ( len )
{
*dest++ = '.';
}
for ( a = 0 ; a < len ; a++ )
{
*dest++ = *ext++;
}
*dest = 0; /*terminateit*/
} /* _f_getfilename */
/****************************************************************************
*
* _f_getdecluster
*
* get a directory entry structure start cluster value
*
* INPUTS
*
* de - directory entry
*
* RETURNS
*
* directory entry cluster value
*
***************************************************************************/
unsigned long _f_getdecluster ( F_DIRENTRY * de )
{
unsigned long cluster;
if ( gl_volume.mediatype == F_FAT32_MEDIA )
{
cluster = _f_getword( &de->clusterhi );
cluster <<= 16;
cluster |= _f_getword( &de->clusterlo );
return cluster;
}
return _f_getword( &de->clusterlo );
}
/****************************************************************************
*
* _f_setdecluster
*
* set a directory entry structure start cluster value
*
* INPUTS
*
* de - directory entry
* cluster - value of the start cluster
*
***************************************************************************/
void _f_setdecluster ( F_DIRENTRY * de, unsigned long cluster )
{
_f_setword( &de->clusterlo, (unsigned short)( cluster & 0xffff ) );
if ( gl_volume.mediatype == F_FAT32_MEDIA )
{
_f_setword( &de->clusterhi, (unsigned short)( cluster >> 16 ) );
}
else
{
_f_setword( &de->clusterhi, (unsigned short)0 );
}
}
/****************************************************************************
*
* _f_findpath
*
* finding out if path is valid in F_NAME and
* correct path info with absolute path (removes relatives)
*
* INPUTS
*
* fsname - filled structure with path,drive
* pos - where to start searching, and contains current position
*
* RETURNS
*
* 0 - if path was not found or invalid
* 1 - if path was found
*
***************************************************************************/
unsigned char _f_findpath ( F_NAME * fsname, F_POS * pos )
{
char * path = fsname->path;
char * mpath = path;
F_DIRENTRY * de;
_f_clustertopos( 0, pos );
for ( ; *path ; )
{
char name[F_MAXNAME];
char ext[F_MAXEXT];
unsigned char len = _f_setnameext( path, name, ext );
if ( ( pos->cluster == 0 ) && ( len == 1 ) && ( name[0] == '.' ) )
{
_f_clustertopos( 0, pos );
}
else
{
if ( !_f_findfilewc( name, ext, pos, &de, 0 ) )
{
return 0;
}
if ( !( de->attr & F_ATTR_DIR ) )
{
return 0;
}
_f_clustertopos( _f_getdecluster( de ), pos );
}
if ( name[0] == '.' )
{
if ( len == 1 )
{
path += len;
if ( !( *path ) )
{
if ( mpath != fsname->path )
{
mpath--; /*if we are now at the top*/
}
break;
}
path++;
continue;
}
if ( name[1] != '.' )
{
return 0; /*invalid name*/
}
if ( len != 2 )
{
return 0; /*invalid name !*/
}
path += len;
if ( mpath == fsname->path )
{
return 0; /*we are in the top*/
}
mpath--; /*no on separator*/
for ( ; ; )
{
if ( mpath == fsname->path )
{
break; /*we are now at the top*/
}
mpath--;
if ( *mpath == '/' )
{
mpath++;
break;
}
}
if ( !( *path ) )
{
if ( mpath != fsname->path )
{
mpath--; /*if we are now at the top*/
}
break;
}
path++;
continue;
}
else
{
if ( path == mpath ) /*if no was dots just step*/
{
path += len;
mpath += len;
}
else
{
unsigned char a;
for ( a = 0 ; a < len ; a++ )
{
*mpath++ = *path++; /*copy if in different pos*/
}
}
}
if ( !( *path ) )
{
break;
}
path++;
*mpath++ = '/'; /*add separator*/
}
*mpath = 0; /*terminate it*/
return 1;
} /* _f_findpath */
/****************************************************************************
*
* fn_getcwd
*
* getting a current working directory of current drive
*
* INPUTS
*
* buffer - where to store current working folder
* maxlen - buffer length (possible size is F_MAXPATH)
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char fn_getcwd ( char * buffer, unsigned char maxlen, char root )
{
unsigned char a;
if ( !maxlen )
{
return F_NO_ERROR;
}
maxlen--; /*need for termination*/
if ( root && maxlen )
{
*buffer++ = '/';
maxlen--;
}
for ( a = 0 ; a < maxlen ; a++ )
{
char ch = gl_volume.cwd[a];
buffer[a] = ch;
if ( !ch )
{
break;
}
}
buffer[a] = 0; /*add terminator at the end*/
return F_NO_ERROR;
} /* fn_getcwd */
/****************************************************************************
*
* fn_findfirst
*
* find a file(s) or directory(s) in directory
*
* INPUTS
*
* filename - filename (with or without wildcards)
* find - where to store found file information
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char fn_findfirst ( const char * filename, F_FIND * find )
{
unsigned char ret;
if ( _f_setfsname( filename, &find->findfsname ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
if ( _f_checkname( find->findfsname.filename, find->findfsname.fileext ) )
{
return F_ERR_INVALIDNAME; /*invalid name, wildcard is ok*/
}
ret = _f_getvolume();
if ( ret )
{
return ret;
}
if ( !_f_findpath( &find->findfsname, &find->pos ) )
{
return F_ERR_INVALIDDIR; /*search for path*/
}
return fn_findnext( find );
} /* fn_findfirst */
/****************************************************************************
*
* fn_findnext
*
* find further file(s) or directory(s) in directory
*
* INPUTS
*
* find - where to store found file information (findfirst should call 1st)
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char fn_findnext ( F_FIND * find )
{
F_DIRENTRY * de;
unsigned char a;
unsigned char ret;
ret = _f_getvolume();
if ( ret )
{
return ret;
}
if ( !_f_findfilewc( find->findfsname.filename, find->findfsname.fileext, &find->pos, &de, 1 ) )
{
return F_ERR_NOTFOUND;
}
for ( a = 0 ; a < F_MAXNAME ; a++ )
{
find->name[a] = de->name[a];
}
for ( a = 0 ; a < F_MAXEXT ; a++ )
{
find->ext[a] = de->ext[a];
}
_f_getfilename( find->filename, (char *)de->name, (char *)de->ext );
find->attr = de->attr;
find->cdate = _f_getword( &de->cdate );
find->ctime = _f_getword( &de->ctime );
find->filesize = (long)_f_getlong( &de->filesize );
find->cluster = _f_getdecluster( de );
find->pos.pos++; /*goto next position*/
return 0;
} /* fn_findnext */
/****************************************************************************
*
* fn_chdir
*
* change current working directory
*
* INPUTS
*
* dirname - new working directory name
*
* RETURNS
*
* 0 - if successfully
* other - if any error
*
***************************************************************************/
unsigned char fn_chdir ( const char * dirname )
{
F_POS pos;
F_NAME fsname;
unsigned char len;
unsigned char a;
unsigned char ret;
ret = _f_setfsname( dirname, &fsname );
if ( ret == 1 )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
ret = _f_getvolume();
if ( ret )
{
return ret;
}
for ( len = 0 ; fsname.path[len] ; )
{
len++;
}
if ( len && ( ( fsname.filename[0] != 32 ) || ( fsname.fileext[0] != 32 ) ) )
{
fsname.path[len++] = '/';
}
_f_getfilename( fsname.path + len, fsname.filename, fsname.fileext );
if ( !( _f_findpath( &fsname, &pos ) ) )
{
return F_ERR_NOTFOUND;
}
for ( a = 0 ; a < F_MAXPATH ; a++ )
{
gl_volume.cwd[a] = fsname.path[a];
}
return F_NO_ERROR;
} /* fn_chdir */
/****************************************************************************
*
* _f_initentry
*
* init directory entry, this function is called if a new entry is coming
*
* INPUTS
*
* de - directory entry which needs to be initialized
* name - fil ename (8)
* ext - file extension (3)
*
***************************************************************************/
static void _f_initentry ( F_DIRENTRY * de, char * name, char * ext )
{
unsigned short date;
unsigned short time;
psp_memset( de, 0, sizeof( F_DIRENTRY ) ); /*reset all entries*/
psp_memcpy( de->name, name, sizeof( de->name ) );
psp_memcpy( de->ext, ext, sizeof( de->ext ) );
f_igettimedate( &time, &date );
_f_setword( &de->cdate, date ); /*if there is realtime clock then creation date could be set from*/
_f_setword( &de->ctime, time ); /*if there is realtime clock then creation time could be set from*/
}
/****************************************************************************
*
* _f_addentry
*
* Add a new directory entry into driectory list
*
* INPUTS
*
* fs_name - filled structure what to add into directory list
* pos - where directory cluster chains starts
* pde - F_DIRENTRY pointer where to store the entry where it was added
*
* RETURNS
*
* 0 - if successfully added
* other - if any error (see FS_xxx errorcodes)
*
***************************************************************************/
unsigned char _f_addentry ( F_NAME * fsname, F_POS * pos, F_DIRENTRY * * pde )
{
unsigned char ret;
unsigned short date;
unsigned short time;
if ( !fsname->filename[0] )
{
return F_ERR_INVALIDNAME;
}
if ( fsname->filename[0] == '.' )
{
return F_ERR_INVALIDNAME;
}
while ( pos->cluster < F_CLUSTER_RESERVED )
{
for ( ; pos->sector < pos->sectorend ; pos->sector++ )
{
F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos );
ret = _f_readglsector( pos->sector );
if ( ret )
{
return ret;
}
for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ )
{
if ( ( !de->name[0] ) || ( (unsigned char)( de->name[0] ) == 0xe5 ) )
{
_f_initentry( de, fsname->filename, fsname->fileext );
if ( gl_volume.mediatype == F_FAT32_MEDIA )
{
f_igettimedate( &time, &date );
_f_setword( &de->crtdate, date ); /*if there is realtime clock then creation date could be set from*/
_f_setword( &de->crttime, time ); /*if there is realtime clock then creation time could be set from*/
_f_setword( &de->lastaccessdate, date ); /*if there is realtime clock then creation date could be set from*/
}
if ( pde )
{
*pde = de;
}
return F_NO_ERROR;
}
}
pos->pos = 0;
}
if ( !pos->cluster )
{
if ( gl_volume.mediatype == F_FAT32_MEDIA )
{
pos->cluster = gl_volume.bootrecord.rootcluster;
}
else
{
return F_ERR_NOMOREENTRY;
}
}
{
unsigned long cluster;
gl_volume.fatsector = (unsigned long)-1;
ret = _f_getclustervalue( pos->cluster, &cluster ); /*try to get next cluster*/
if ( ret )
{
return ret;
}
if ( cluster < F_CLUSTER_RESERVED )
{
_f_clustertopos( cluster, pos );
}
else
{
ret = _f_alloccluster( &cluster ); /*get a new one*/
if ( ret )
{
return ret;
}
if ( cluster < F_CLUSTER_RESERVED )
{
if ( gl_file.mode != F_FILE_CLOSE )
{
return F_ERR_NOMOREENTRY;
}
_f_clustertopos( cluster, &gl_file.pos );
ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
if ( ret )
{
return ret;
}
ret = _f_setclustervalue( pos->cluster, gl_file.pos.cluster );
if ( ret )
{
return ret;
}
ret = _f_writefatsector();
if ( ret )
{
return ret;
}
gl_volume.fatsector = (unsigned long)-1;
psp_memset( gl_sector, 0, F_SECTOR_SIZE );
while ( gl_file.pos.sector < gl_file.pos.sectorend )
{
ret = _f_writeglsector( gl_file.pos.sector );
if ( ret )
{
return ret;
}
gl_file.pos.sector++;
}
_f_clustertopos( gl_file.pos.cluster, pos );
}
else
{
return F_ERR_NOMOREENTRY;
}
}
}
} /* _f_addentry */
return F_ERR_NOMOREENTRY;
}
/****************************************************************************
*
* fn_mkdir
*
* making a new directory
*
* INPUTS
*
* dirname - new directory name
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char fn_mkdir ( const char * dirname )
{
F_POS posdir;
F_POS pos;
F_DIRENTRY * de;
F_NAME fsname;
unsigned long cluster;
unsigned char ret;
#if F_FILE_CHANGED_EVENT
ST_FILE_CHANGED fc;
#endif
if ( _f_setfsname( dirname, &fsname ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
ret = _f_getvolume();
if ( ret )
{
return ret;
}
if ( !_f_findpath( &fsname, &posdir ) )
{
return F_ERR_INVALIDDIR;
}
pos = posdir;
if ( fsname.filename[0] == '.' )
{
return F_ERR_NOTFOUND;
}
if ( _f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
{
return F_ERR_DUPLICATED;
}
pos = posdir;
gl_volume.fatsector = (unsigned long)-1;
ret = _f_alloccluster( &cluster );
if ( ret )
{
return ret;
}
ret = _f_addentry( &fsname, &pos, &de );
if ( ret )
{
return ret;
}
de->attr |= F_ATTR_DIR; /*set as directory*/
#if F_FILE_CHANGED_EVENT
if ( f_filechangedevent )
{
fc.action = FACTION_ADDED;
fc.flags = FFLAGS_DIR_NAME | FFLAGS_ATTRIBUTES | FFLAGS_SIZE | FFLAGS_LAST_WRITE;
fc.attr = de->attr;
fc.ctime = _f_getword( de->ctime );
fc.cdate = _f_getword( de->cdate );
fc.filesize = _f_getlong( de->filesize );
}
#endif
if ( gl_file.mode != F_FILE_CLOSE )
{
return F_ERR_LOCKED;
}
_f_clustertopos( cluster, &gl_file.pos );
_f_setdecluster( de, cluster ); /*new dir*/
(void)_f_writeglsector( (unsigned long)-1 ); /*write actual directory sector*/
de = (F_DIRENTRY *)gl_sector;
_f_initentry( de, ". ", " " );
de->attr = F_ATTR_DIR; /*set as directory*/
_f_setdecluster( de, cluster ); /*current*/
de++;
_f_initentry( de, ".. ", " " );
de->attr = F_ATTR_DIR; /*set as directory*/
_f_setdecluster( de, posdir.cluster ); /*parent*/
de++;
psp_memset( de, 0, ( F_SECTOR_SIZE - 2 * sizeof( F_DIRENTRY ) ) );
ret = _f_writeglsector( gl_file.pos.sector );
if ( ret )
{
return ret;
}
gl_file.pos.sector++;
psp_memset( gl_sector, 0, ( 2 * sizeof( F_DIRENTRY ) ) );
while ( gl_file.pos.sector < gl_file.pos.sectorend )
{
ret = _f_writeglsector( gl_file.pos.sector );
if ( ret )
{
return ret;
}
gl_file.pos.sector++;
}
gl_volume.fatsector = (unsigned long)-1;
ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
if ( ret )
{
return ret;
}
ret = _f_writefatsector();
#if F_FILE_CHANGED_EVENT
if ( f_filechangedevent && !ret )
{
fc.action = FACTION_ADDED;
fc.flags = FFLAGS_DIR_NAME;
if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
{
f_filechangedevent( &fc );
}
}
#endif
return ret;
} /* fn_mkdir */
/****************************************************************************
*
* fn_rmdir
*
* Remove directory, only could be removed if empty
*
* INPUTS
*
* dirname - which directory needed to be removed
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char fn_rmdir ( const char * dirname )
{
unsigned char ret;
F_POS pos;
F_DIRENTRY * de;
F_NAME fsname;
unsigned long dirsector;
unsigned char a;
if ( _f_setfsname( dirname, &fsname ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
{
return F_ERR_INVALIDNAME; /*invalid name*/
}
if ( fsname.filename[0] == '.' )
{
return F_ERR_NOTFOUND;
}
ret = _f_getvolume();
if ( ret )
{
return ret;
}
if ( !( _f_findpath( &fsname, &pos ) ) )
{
return F_ERR_INVALIDDIR;
}
if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
{
return F_ERR_NOTFOUND;
}
if ( !( de->attr & F_ATTR_DIR ) )
{
return F_ERR_INVALIDDIR; /*not a directory*/
}
dirsector = gl_volume.actsector;
if ( gl_file.mode != F_FILE_CLOSE )
{
return F_ERR_LOCKED;
}
_f_clustertopos( _f_getdecluster( de ), &gl_file.pos );
for ( ; ; )
{
F_DIRENTRY * de2;
char ch = 0;
ret = _f_getcurrsector();
if ( ret == F_ERR_EOF )
{
break;
}
if ( ret )
{
return ret;
}
de2 = (F_DIRENTRY *)gl_sector;
for ( a = 0 ; a < ( F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ) ; a++, de2++ )
{
ch = de2->name[0];
if ( !ch )
{
break;
}
if ( (unsigned char)ch == 0xe5 )
{
continue;
}
if ( ch == '.' )
{
continue;
}
return F_ERR_NOTEMPTY; /*something is there*/
}
if ( !ch )
{
break;
}
gl_file.pos.sector++;
}
ret = _f_readglsector( dirsector );
if ( ret )
{
return ret;
}
de->name[0] = (unsigned char)0xe5;
ret = _f_writeglsector( dirsector );
if ( ret )
{
return ret;
}
gl_volume.fatsector = (unsigned long)-1;
ret = _f_removechain( _f_getdecluster( de ) );
#if F_FILE_CHANGED_EVENT
if ( f_filechangedevent && !ret )
{
ST_FILE_CHANGED fc;
fc.action = FACTION_REMOVED;
fc.flags = FFLAGS_DIR_NAME;
if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
{
f_filechangedevent( &fc );
}
}
#endif
return ret;
} /* fn_rmdir */