📄 fatfs.c
字号:
}
}
static cyg_uint16 fat_bad_char(cyg_uint16 w)
{
return (w < 0x0020)
|| (w == '*') || (w == '?') || (w == '<') || (w == '>')
|| (w == '|') || (w == '"') || (w == ':') || (w == '/')
|| (w == '\\');
}
static cyg_uint16 fat_replace_char( cyg_uint16 w)
{
return (w == '[') || (w == ']') || (w == ';') || (w == ',')
|| (w == '+') || (w == '=');
}
static cyg_uint16 fat_skip_char( cyg_uint16 w)
{
return (w == '.') || (w == ' ');
}
static inline int fat_is_used_badchars(const cyg_uint16 *s, int len)
{
int i;
for (i = 0; i < len; i++)
if (fat_bad_char(s[i]))
return -EINVAL;
return 0;
}
static int fat_valid_longname(const cyg_uint8 *name, cyg_uint32 len)
{
if (name[len - 1] == ' ')
return -EINVAL;
if (len >= 256)
return -ENAMETOOLONG;
return 0;
}
struct shortname_info {
unsigned char lower:1,
upper:1,
valid:1;
};
#define INIT_SHORTNAME_INFO(x) do { \
(x)->lower = 1; \
(x)->upper = 1; \
(x)->valid = 1; \
} while (0)
static cyg_uint32 to_shortname_char(struct nls_table *nls,
cyg_uint8 *buf, cyg_uint32 buf_size,
cyg_uint16 *src, struct shortname_info *info)
{
int len;
if (fat_skip_char(*src)) {
info->valid = 0;
return 0;
}
if (fat_replace_char(*src)) {
info->valid = 0;
buf[0] = '_';
return 1;
}
len = nls->uni2char(*src, buf, buf_size);
if (len <= 0) {
info->valid = 0;
buf[0] = '_';
len = 1;
} else if (len == 1) {
unsigned char prev = buf[0];
if (buf[0] >= 0x7F) {
info->lower = 0;
info->upper = 0;
}
buf[0] = nls_toupper(nls, buf[0]);
if (isalpha(buf[0])) {
if (buf[0] == prev)
info->lower = 0;
else
info->upper = 0;
}
} else {
info->lower = 0;
info->upper = 0;
}
return len;
}
static cyg_uint16 uname[256]={0};
static cyg_uint8 tmp_name_res[12]={0};
int create_shortname(cyg_dir dir,
fatfs_disk_t *disk,
struct nls_table *nls,
const char *name,int len,
cyg_uint8 *name_res)
{
cyg_uint16 *ip, *ext_start, *end, *name_start;
cyg_uint8 base[9], ext[4], buf[8], *p;
cyg_uint8 charbuf[NLS_MAX_CHARSET_SIZE];
int ret,ulen=0;
int chl, chi;
cyg_uint32 jiffies;
int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
int is_shortname;
fatfs_dirsearch_t ds;
struct shortname_info base_info, ext_info;
jiffies=rand();
is_shortname = 1;
INIT_SHORTNAME_INFO(&base_info);
INIT_SHORTNAME_INFO(&ext_info);
for(i=0;i<len;)
{
ret=fatfs_utf8_table->char2uni(name+i, len, &(uname[ulen]));
if(ret<0) return ret;
if(ulen>=256) return -1;
i+=ret;
ulen++;
}
/* Now, we need to create a shortname from the long name */
ext_start = end = &uname[ulen];
while (--ext_start >= uname) {
if (*ext_start == 0x002E) { /* is `.' */
if (ext_start == end - 1) {
sz = ulen;
ext_start = NULL;
}
break;
}
}
if (ext_start == uname - 1) {
sz = ulen;
ext_start = NULL;
} else if (ext_start) {
/*
* Names which start with a dot could be just
* an extension eg. "...test". In this case Win95
* uses the extension as the name and sets no extension.
*/
name_start = &uname[0];
while (name_start < ext_start) {
if (!fat_skip_char(*name_start))
break;
name_start++;
}
if (name_start != ext_start) {
sz = ext_start - uname;
ext_start++;
} else {
sz = ulen;
ext_start = NULL;
}
}
numtail_baselen = 6;
numtail2_baselen = 2;
for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {
chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
ip, &base_info);
if (chl == 0)
continue;
if (baselen < 2 && (baselen + chl) > 2)
numtail2_baselen = baselen;
if (baselen < 6 && (baselen + chl) > 6)
numtail_baselen = baselen;
for (chi = 0; chi < chl; chi++) {
*p++ = charbuf[chi];
baselen++;
if (baselen >= 8)
break;
}
if (baselen >= 8) {
if ((chi < chl - 1) || (ip + 1) - uname < sz)
is_shortname = 0;
break;
}
}
if (baselen == 0) {
return -EINVAL;
}
extlen = 0;
if (ext_start) {
for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
ip, &ext_info);
if (chl == 0)
continue;
if ((extlen + chl) > 3) {
is_shortname = 0;
break;
}
for (chi = 0; chi < chl; chi++) {
*p++ = charbuf[chi];
extlen++;
}
if (extlen >= 3) {
if (ip + 1 != end)
is_shortname = 0;
break;
}
}
}
ext[extlen] = '\0';
base[baselen] = '\0';
/* Yes, it can happen. ".\xe5" would do it. */
if (base[0] == 0xe5)
base[0] = 0x05;
/* OK, at this point we know that base is not longer than 8 symbols,
* ext is not longer than 3, base is nonempty, both don't contain
* any bad symbols (lowercase transformed to uppercase).
*/
memset(name_res, ' ', 11);
memcpy(name_res, base, baselen);
memcpy(name_res + 8, ext, extlen);
if (is_shortname && base_info.valid && ext_info.valid) {
memset(tmp_name_res,0,12);
for(i=10;i>=0;i--)
{
if(name_res[i]==' ') continue;
tmp_name_res[i]=name_res[i];
}
init_dirsearch(&ds,disk,(fatfs_node_t*)dir,tmp_name_res);
if (fatfs_find(&ds)==ENOENT)
return 0;
}
/*
* Try to find a unique extension. This used to
* iterate through all possibilities sequentially,
* but that gave extremely bad performance. Windows
* only tries a few cases before using random
* values for part of the base.
*/
if (baselen > 6) {
baselen = numtail_baselen;
name_res[7] = ' ';
}
name_res[baselen] = '~';
for (i = 1; i < 10; i++) {
int j;
name_res[baselen + 1] = i + '0';
memset(tmp_name_res,0,12);
for(j=baselen+1;j>=0;j--)
{
/* if(name_res[j]==' ') continue;*/
tmp_name_res[j]=name_res[j];
}
init_dirsearch(&ds,disk,(fatfs_node_t*)dir,tmp_name_res);
if (fatfs_find(&ds)==ENOENT)
return 0;
}
i = jiffies & 0xffff;
sz = (jiffies >> 16) & 0x7;
if (baselen > 2) {
baselen = numtail2_baselen;
name_res[7] = ' ';
}
name_res[baselen + 4] = '~';
name_res[baselen + 5] = '1' + sz;
while (1) {
sprintf(buf, "%04X", i);
memcpy(&name_res[baselen], buf, 4);
memset(tmp_name_res,0,12);
for(i=10;i>=0;i--)
{
if(name_res[i]==' ') continue;
tmp_name_res[i]=name_res[i];
}
init_dirsearch(&ds,disk,(fatfs_node_t*)dir,tmp_name_res);
if (fatfs_find(&ds)==ENOENT)
return 0;
i -= 11;
}
return 0;
}
//==========================================================================
// Filesystem operations
// -------------------------------------------------------------------------
// fatfs_mount()
// Process a mount request. This mainly creates a root for the
// filesystem.
static fatfs_dir_entry_t root_dentry;
static int
fatfs_mount(cyg_fstab_entry *fste, cyg_mtab_entry *mte)
{
cyg_io_handle_t dev_h;
fatfs_disk_t *disk;
Cyg_ErrNo err;
CYG_TRACE2(TFS, "mount fste=%p mte=%p", fste, mte);
memset(&root_dentry,0,sizeof(fatfs_dir_entry_t));
init_fatfs_fds();
CYG_TRACE1(TFS, "looking up disk device '%s'", mte->devname);
err = cyg_io_lookup(mte->devname, &dev_h);
if (err != ENOERR)
return err;
disk = (fatfs_disk_t *)malloc(sizeof(fatfs_disk_t));
if (NULL == disk)
return ENOMEM;
CYG_TRACE0(TFS, "initializing block cache");
disk->bcache_mem = (cyg_uint8 *)malloc(CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE);
if (NULL == disk->bcache_mem)
{
free(disk);
return ENOMEM;
}
// FIXME: get block size from disk device
err = cyg_blib_io_create(dev_h, disk->bcache_mem,
CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE, 512, &disk->blib);
if (err != ENOERR)
{
free(disk->bcache_mem);
free(disk);
return err;
}
disk->dev_h = dev_h;
CYG_TRACE0(TFS, "initializing disk");
err = fatfs_init(disk);
if (err != ENOERR)
{
cyg_blib_delete(&disk->blib);
free(disk->bcache_mem);
free(disk);
return err;
}
#if TFS
print_disk_info(disk);
#endif
/*register the utf8*/
fatfs_utf8_table=load_nls_utf8();
if(NULL==fatfs_utf8_table) return -1;
CYG_TRACE0(TFS, "initializing node cache");
fatfs_node_cache_init(disk);
CYG_TRACE0(TFS, "initializing root node");
fatfs_get_root_dir_entry(disk, &root_dentry);
disk->root = fatfs_node_alloc(disk, &root_dentry);
fatfs_node_ref(disk, disk->root);
mte->root = (cyg_dir)disk->root;
mte->data = (CYG_ADDRWORD)disk;
CYG_TRACE0(TFS, "disk mounted");
return ENOERR;
}
// -------------------------------------------------------------------------
// fatfs_umount()
// Unmount the filesystem. This will currently only succeed if the
// filesystem is empty.
static int
fatfs_umount(cyg_mtab_entry *mte)
{
fatfs_disk_t *disk = (fatfs_disk_t *) mte->data;
fatfs_node_t *root = (fatfs_node_t *) mte->root;
CYG_TRACE3(TFS, "umount mte=%p %d live nodes %d dead nodes",
mte, fatfs_get_live_node_count(disk),
fatfs_get_dead_node_count(disk));
if (root->refcnt > 1)
return EBUSY;
if (fatfs_get_live_node_count(disk) != 1)
return EBUSY;
fatfs_node_unref(disk, root);
fatfs_node_cache_flush(disk);
// FIXME: cache delete can fail if cache can't be synced
cyg_blib_delete(&disk->blib);
free(disk->bcache_mem);
free(disk);
mte->root = CYG_DIR_NULL;
mte->data = (CYG_ADDRWORD) NULL;
unload_nls_utf8();
CYG_TRACE0(TFS, "disk umounted");
return ENOERR;
}
// -------------------------------------------------------------------------
// fatfs_open()
// Open a file for reading or writing.
static int
fatfs_open(cyg_mtab_entry *mte,
cyg_dir dir,
const char *name,
int mode,
cyg_file *file)
{
fatfs_disk_t *disk = (fatfs_disk_t *) mte->data;
fatfs_node_t *node = NULL;
fatfs_fd_t *fd;
fatfs_dirsearch_t ds;
int err;
cyg_uint8 shortname[12+1]={0};
CYG_TRACE5(TFS, "open mte=%p dir=%p name='%s' mode=%d file=%p",
mte, dir, name, mode, file);
init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
err = fatfs_find(&ds);
if (err == ENOENT)
{
if (ds.last && (mode & O_CREAT))
{
fatfs_dir_entry_t new_file_dentry;
// No node there, if the O_CREAT bit is set then we must
// create a new one. The dir and name fields of the dirsearch
// object will have been updated so we know where to put it.
CYG_TRACE1(TFS, "creating new file '%s'", name);
err=create_shortname(dir, disk,fatfs_utf8_table, ds.name,ds.namelen,shortname);
if(err<0) return err;
err = fatfs_create_file(disk,
&ds.dir->dentry,
shortname,
strlen(shortname),
ds.name,
ds.namelen,
&new_file_dentry);
if (err != ENOERR)
return err;
node = fatfs_node_alloc(disk, &new_file_dentry);
if (NULL == node)
return EMFILE;
// Update directory times
ds.dir->dentry.atime =
ds.dir->dentry.mtime = cyg_timestamp();
err = ENOERR;
}
}
else if (err == ENOERR)
{
// The node exists. If the O_CREAT and O_EXCL bits are set, we
// must fail the open
if ((mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
err = EEXIST;
else
node = ds.node;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -