rt-thread/components/dfs/filesystems/jffs2/dfs_jffs2.c

688 lines
19 KiB
C

/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-1-7 prife the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "cyg/infra/cyg_type.h"
#include "cyg/fileio/fileio.h"
#include "port/codes.h"
#include "port/fcntl.h"
#undef mode_t
#include <dfs_fs.h>
#include <dfs_file.h>
#include "dfs_jffs2.h"
#include "jffs2_config.h"
#include "porting.h"
#include <string.h>
#if DEVICE_PART_MAX > 1
#error "support only one jffs2 partition on a flash device!"
#endif
/* make sure the following struct var had been initilased to 0! */
struct device_part
{
struct cyg_mtab_entry * mte;
struct rt_mtd_nor_device *dev;
};
static struct device_part device_partition[DEVICE_PART_MAX] = {0};
static struct rt_mutex jffs2_lock;
#define jffs2_mount jffs2_fste.mount
#define jffs2_umount jffs2_fste.umount
#define jffs2_open jffs2_fste.open
#define jffs2_file_unlink jffs2_fste.unlink
#define jffs2_mkdir jffs2_fste.mkdir
#define jffs2_rmdir jffs2_fste.rmdir
#define jffs2_rename jffs2_fste.rename
#define jffs2_link jffs2_fste.link
#define jffs2_opendir jffs2_fste.opendir
#define jffs2_chdir jffs2_fste.chdir
#define jffs2_ops_stat jffs2_fste.stat
#define jffs2_getinfo jffs2_fste.getinfo
#define jffs2_setinfo jffs2_fste.setinfo
#define jffs2_file_read jffs2_fileops.fo_read
#define jffs2_file_write jffs2_fileops.fo_write
#define jffs2_file_lseek jffs2_fileops.fo_lseek
#define jffs2_file_ioctl jffs2_fileops.fo_ioctl
#define jffs2_file_select jffs2_fileops.fo_select
#define jffs2_file_fsync jffs2_fileops.fo_fsync
#define jffs2_file_colse jffs2_fileops.fo_close
#define jffs2_file_fstat jffs2_fileops.fo_fstat
#define jffs2_file_getinfo jffs2_fileops.fo_getinfo
#define jffs2_file_setinfo jffs2_fileops.fo_setinfo
#define jffs2_dir_read jffs2_dirops.fo_read
//#define jffs2_dir_write jffs2_dirops.fo_write
#define jffs2_dir_lseek jffs2_dirops.fo_lseek
//#define jffs2_dir_ioctl jffs2_dirops.fo_ioctl
#define jffs2_dir_select jffs2_dirops.fo_select
//#define jffs2_dir_fsync jffs2_dirops.fo_fsync
#define jffs2_dir_colse jffs2_dirops.fo_close
//#define jffs2_dir_fstat jffs2_dirops.fo_fstat
//#define jffs2_dir_getinfo jffs2_dirops.fo_getinfo
//#define jffs2_dir_setinfo jffs2_dirops.fo_setinfo
/*
* RT-Thread Device Interface for jffs2
*/
/* these code is in src/flashio.c */
static int jffs2_result_to_dfs(int result)
{
if (result < 0) return result;
if (result > 0) return -result;
return 0;
}
/*
* RT-Thread DFS Interface for jffs2
*/
static int dfs_jffs2_mount(struct dfs_filesystem* fs,
unsigned long rwflag,
const void* data)
{
unsigned index;
struct cyg_mtab_entry * mte;
int result;
/* find a empty entry in partition table */
for (index = 0; index < DEVICE_PART_MAX; index ++)
{
if (device_partition[index].dev == RT_NULL)
break;
}
if (index == DEVICE_PART_MAX)
return -ENOSPC;
mte = rt_malloc(sizeof(struct cyg_mtab_entry));
if (mte == RT_NULL)
return -ENOMEM;
mte->name = fs->path;
mte->fsname = "jffs2";
mte->devname = NULL;
/* note that, i use mte->data to store rtt's device
* while, in jffs2_mount, mte->data will be copy into
* s_dev in struct super_block, and mte->data will be
* filled with jffs2_sb(see the source of jffs2_mount.
*/
mte->data = (CYG_ADDRWORD)fs->dev_id;
device_partition[index].dev = RT_MTD_NOR_DEVICE(fs->dev_id);
/* after jffs2_mount, mte->data will not be dev_id any more */
result = jffs2_mount(NULL, mte);
if (result != 0)
{
device_partition[index].dev = NULL;
return jffs2_result_to_dfs(result);
}
/* save this pointer */
device_partition[index].mte = mte;
return 0;
}
static int _find_fs(struct cyg_mtab_entry ** mte, rt_device_t dev_id)
{
unsigned index;
/* find device index */
for (index = 0; index < DEVICE_PART_MAX; index++)
{
if (device_partition[index].dev == RT_MTD_NOR_DEVICE(dev_id))
{
*mte = device_partition[index].mte;
return 0;
}
}
rt_kprintf("error, could not found the fs!");
return -1;
}
static int dfs_jffs2_unmount(struct dfs_filesystem* fs)
{
int result;
unsigned index;
/* find device index, then umount it */
for (index = 0; index < DEVICE_PART_MAX; index++)
{
if (device_partition[index].dev == RT_MTD_NOR_DEVICE(fs->dev_id))
{
result = jffs2_umount(device_partition[index].mte);
if (result) return jffs2_result_to_dfs(result);
rt_free(device_partition[index].mte);
device_partition[index].dev = NULL;
device_partition[index].mte = NULL;
return RT_EOK;
}
}
return -ENOENT;
}
static int dfs_jffs2_mkfs(rt_device_t dev_id)
{
/* just erase all blocks on this nand partition */
return -ENOSYS;
}
static int dfs_jffs2_statfs(struct dfs_filesystem* fs,
struct statfs *buf)
{
/* since the limit of unsigned long, so the max size of flash device is 4G */
struct cyg_mtab_entry * mte;
struct jffs2_fs_info info;
int result;
result = _find_fs(&mte, fs->dev_id);
if (result)
return -ENOENT;
RT_ASSERT(mte->data != 0);
jffs2_get_info_from_sb((void *)mte->data, &info);
buf->f_bsize = info.sector_size;
buf->f_blocks = info.nr_blocks;
buf->f_bfree = info.free_size / info.sector_size;
return 0;
}
static const char jffs2_root_path[] = ".";
static int dfs_jffs2_open(struct dfs_fd* file)
{
int result;
int oflag, mode;
const char * name;
cyg_file * jffs2_file;
struct dfs_filesystem *fs;
struct cyg_mtab_entry * mte;
oflag = file->flags;
fs = (struct dfs_filesystem *)file->data;
RT_ASSERT(fs != RT_NULL);
jffs2_file = rt_malloc(sizeof(cyg_file));
if (jffs2_file == RT_NULL)
return -ENOMEM;
/* just escape '/' provided by dfs code */
name = file->path;
if ((name[0] == '/') && (name[1] == 0))
name = jffs2_root_path;
else /* name[0] still will be '/' */
name ++;
result = _find_fs(&mte, fs->dev_id);
if (result)
{
rt_free(jffs2_file);
return -ENOENT;
}
/* set mount table */
jffs2_file->f_mte = mte;
if (oflag & O_DIRECTORY) /* operations about dir */
{
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
if (oflag & O_CREAT) /* create a dir*/
{
/* fixme, should test file->path can end with '/' */
result = jffs2_mkdir(mte, mte->root, name);
if (result)
{
rt_mutex_release(&jffs2_lock);
rt_free(jffs2_file);
return jffs2_result_to_dfs(result);
}
}
/* open dir */
result = jffs2_opendir(mte, mte->root, name, jffs2_file);
rt_mutex_release(&jffs2_lock);
if (result)
{
rt_free(jffs2_file);
return jffs2_result_to_dfs(result);
}
#ifdef CONFIG_JFFS2_NO_RELATIVEDIR
jffs2_file->f_offset = 2;
#endif
/* save this pointer, it will be used by dfs_jffs2_getdents*/
file->data = jffs2_file;
return 0;
}
/* regular file operations */
mode = JFFS2_O_RDONLY;
if (oflag & O_WRONLY) mode |= JFFS2_O_WRONLY;
if (oflag & O_RDWR) mode |= JFFS2_O_RDWR;
/* Opens the file, if it is existing. If not, a new file is created. */
if (oflag & O_CREAT) mode |= JFFS2_O_CREAT;
/* Creates a new file. If the file is existing, it is truncated and overwritten. */
if (oflag & O_TRUNC) mode |= JFFS2_O_TRUNC;
/* Creates a new file. The function fails if the file is already existing. */
if (oflag & O_EXCL) mode |= JFFS2_O_EXCL;
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_open(mte, 0, name, mode, jffs2_file);
if (result != 0)
{
rt_mutex_release(&jffs2_lock);
rt_free(jffs2_file);
return jffs2_result_to_dfs(result);
}
/* save this pointer, it will be used when calling read(), write(),
flush(), lessk(), and will be rt_free when calling close()*/
file->data = jffs2_file;
file->pos = jffs2_file->f_offset;
file->size = 0;
jffs2_file_lseek(jffs2_file, (off_t *)(&(file->size)), SEEK_END);
jffs2_file->f_offset = (off_t)file->pos;
rt_mutex_release(&jffs2_lock);
if (oflag & O_APPEND)
{
file->pos = file->size;
jffs2_file->f_offset = file->size;
}
return 0;
}
static int dfs_jffs2_close(struct dfs_fd* file)
{
int result;
cyg_file * jffs2_file;
RT_ASSERT(file->data != NULL);
jffs2_file = (cyg_file *)(file->data);
if (file->flags & O_DIRECTORY) /* operations about dir */
{
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_dir_colse(jffs2_file);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
rt_free(jffs2_file);
return 0;
}
/* regular file operations */
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_file_colse(jffs2_file);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
/* release memory */
rt_free(jffs2_file);
return 0;
}
static int dfs_jffs2_ioctl(struct dfs_fd* file, int cmd, void* args)
{
return -ENOSYS;
}
static int dfs_jffs2_read(struct dfs_fd* file, void* buf, size_t len)
{
cyg_file * jffs2_file;
struct CYG_UIO_TAG uio_s;
struct CYG_IOVEC_TAG iovec;
int char_read;
int result;
RT_ASSERT(file->data != NULL);
jffs2_file = (cyg_file *)(file->data);
uio_s.uio_iov = &iovec;
uio_s.uio_iov->iov_base = buf;
uio_s.uio_iov->iov_len = len;
uio_s.uio_iovcnt = 1; //must be 1
//uio_s.uio_offset //not used...
uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;
char_read = jffs2_file->f_offset; /* the current offset */
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_file_read(jffs2_file, &uio_s);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
/* update position */
file->pos = jffs2_file->f_offset;
char_read = jffs2_file->f_offset - char_read;
return char_read;
}
static int dfs_jffs2_write(struct dfs_fd* file,
const void* buf,
size_t len)
{
cyg_file * jffs2_file;
struct CYG_UIO_TAG uio_s;
struct CYG_IOVEC_TAG iovec;
int char_write;
int result;
RT_ASSERT(file->data != NULL);
jffs2_file = (cyg_file *)(file->data);
uio_s.uio_iov = &iovec;
uio_s.uio_iov->iov_base = (void *)buf;
uio_s.uio_iov->iov_len = len;
uio_s.uio_iovcnt = 1; //must be 1
//uio_s.uio_offset //not used...
uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;
char_write = jffs2_file->f_offset;
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_file_write(jffs2_file, &uio_s);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
/* update position */
file->pos = jffs2_file->f_offset;
char_write = jffs2_file->f_offset - char_write;
return char_write;
}
static int dfs_jffs2_flush(struct dfs_fd* file)
{
/* infact, jffs2 not support, jffs2_fo_sync just return ok */
return -ENOSYS;
}
/* fixme warning: the offset is rt_off_t, so maybe the size of a file is must <= 2G*/
static int dfs_jffs2_lseek(struct dfs_fd* file,
rt_off_t offset)
{
cyg_file * jffs2_file;
int result;
RT_ASSERT(file->data != NULL);
jffs2_file = (cyg_file *)(file->data);
/* set offset as current offset */
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_file_lseek(jffs2_file, &offset, SEEK_SET);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
/* update file position */
file->pos = offset;
return offset;
}
/* return the size of struct dirent*/
static int dfs_jffs2_getdents(struct dfs_fd* file,
struct dirent* dirp,
rt_uint32_t count)
{
cyg_file * jffs2_file;
struct CYG_UIO_TAG uio_s;
struct CYG_IOVEC_TAG iovec;
struct jffs2_dirent jffs2_d;
struct dirent * d;
rt_uint32_t index;
#if !defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE)
struct jffs2_stat s;
cyg_mtab_entry * mte;
char * fullname;
#endif
int result;
RT_ASSERT(file->data != RT_NULL);
jffs2_file = (cyg_file*)(file->data);
mte = jffs2_file->f_mte;
//set jffs2_d
memset(&jffs2_d, 0, sizeof(struct jffs2_dirent));
//set CYG_UIO_TAG uio_s
uio_s.uio_iov = &iovec;
uio_s.uio_iov->iov_base = &jffs2_d;
uio_s.uio_iov->iov_len = sizeof(struct jffs2_dirent);;
uio_s.uio_iovcnt = 1; //must be 1
uio_s.uio_offset = 0;//not used...
uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;
/* make integer count, usually count is 1 */
count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
if (count == 0) return -EINVAL;
index = 0;
/* usually, the while loop should only be looped only once! */
while (1)
{
d = dirp + index;
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_dir_read(jffs2_file, &uio_s);
rt_mutex_release(&jffs2_lock);
/* if met a error or all entry are read over, break while*/
if (result || jffs2_d.d_name[0] == 0)
break;
#if defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE)
switch(jffs2_d.d_type & JFFS2_S_IFMT)
{
case JFFS2_S_IFREG: d->d_type = DT_REG; break;
case JFFS2_S_IFDIR: d->d_type = DT_DIR; break;
default: d->d_type = DT_UNKNOWN; break;
}
#else
fullname = rt_malloc(FILE_PATH_MAX);
if(fullname == RT_NULL)
return -ENOMEM;
/* make a right entry */
if ((file->path[0] == '/') )
{
if (file->path[1] == 0)
strcpy(fullname, jffs2_d.d_name);
else
rt_sprintf(fullname, "%s/%s", file->path+1, jffs2_d.d_name);
}
else
rt_sprintf(fullname, "%s/%s", file->path, jffs2_d.d_name);
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_porting_stat(mte, mte->root, fullname, (void *)&s);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
rt_free(fullname);
/* convert to dfs stat structure */
switch(s.st_mode & JFFS2_S_IFMT)
{
case JFFS2_S_IFREG: d->d_type = DT_REG; break;
case JFFS2_S_IFDIR: d->d_type = DT_DIR; break;
default: d->d_type = DT_UNKNOWN; break;
}
#endif
/* write the rest fields of struct dirent* dirp */
d->d_namlen = rt_strlen(jffs2_d.d_name);
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
rt_strncpy(d->d_name, jffs2_d.d_name, d->d_namlen + 1);
index ++;
if (index * sizeof(struct dirent) >= count)
break;
}
if (result)
return jffs2_result_to_dfs(result);
return index * sizeof(struct dirent);
}
static int dfs_jffs2_unlink(struct dfs_filesystem* fs, const char* path)
{
int result;
struct jffs2_stat s;
cyg_mtab_entry * mte;
result = _find_fs(&mte, fs->dev_id);
if (result)
return -ENOENT;
/* deal path */
if (path[0] == '/')
path++;
/* judge file type, dir is to be delete by rmdir, others by unlink */
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_porting_stat(mte, mte->root, path, (void *)&s);
if (result)
{
rt_mutex_release(&jffs2_lock);
return jffs2_result_to_dfs(result);
}
switch(s.st_mode & JFFS2_S_IFMT)
{
case JFFS2_S_IFREG:
result = jffs2_file_unlink(mte, mte->root, path);
break;
case JFFS2_S_IFDIR:
result = jffs2_rmdir(mte, mte->root, path);
break;
default:
/* unknown file type */
rt_mutex_release(&jffs2_lock);
return -1;
}
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
return 0;
}
static int dfs_jffs2_rename(struct dfs_filesystem* fs,
const char* oldpath,
const char* newpath)
{
int result;
cyg_mtab_entry * mte;
result = _find_fs(&mte, fs->dev_id);
if (result)
return -ENOENT;
if (*oldpath == '/')
oldpath += 1;
if (*newpath == '/')
newpath += 1;
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_rename(mte, mte->root, oldpath, mte->root, newpath);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
return 0;
}
static int dfs_jffs2_stat(struct dfs_filesystem* fs, const char *path, struct stat *st)
{
int result;
struct jffs2_stat s;
cyg_mtab_entry * mte;
/* deal the path for jffs2 */
RT_ASSERT(!((path[0] == '/') && (path[1] == 0)));
if (path[0] == '/')
path++;
result = _find_fs(&mte, fs->dev_id);
if (result)
return -ENOENT;
rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
result = jffs2_porting_stat(mte, mte->root, path, (void *)&s);
rt_mutex_release(&jffs2_lock);
if (result)
return jffs2_result_to_dfs(result);
/* convert to dfs stat structure */
switch(s.st_mode & JFFS2_S_IFMT)
{
case JFFS2_S_IFREG:
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
S_IWUSR | S_IWGRP | S_IWOTH;
break;
case JFFS2_S_IFDIR:
st->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
break;
default:
st->st_mode = DT_UNKNOWN; //fixme
break;
}
st->st_dev = 0;
st->st_size = s.st_size;
st->st_mtime = s.st_mtime;
return 0;
}
static const struct dfs_file_ops _jffs2_fops =
{
dfs_jffs2_open,
dfs_jffs2_close,
dfs_jffs2_ioctl,
dfs_jffs2_read,
dfs_jffs2_write,
dfs_jffs2_flush,
dfs_jffs2_lseek,
dfs_jffs2_getdents,
};
static const struct dfs_filesystem_ops _jffs2_ops =
{
"jffs2",
DFS_FS_FLAG_DEFAULT,
&_jffs2_fops,
dfs_jffs2_mount,
dfs_jffs2_unmount,
dfs_jffs2_mkfs,
dfs_jffs2_statfs,
dfs_jffs2_unlink,
dfs_jffs2_stat,
dfs_jffs2_rename,
};
int dfs_jffs2_init(void)
{
/* register fatfs file system */
dfs_register(&_jffs2_ops);
/* initialize mutex */
if (rt_mutex_init(&jffs2_lock, "jffs2lock", RT_IPC_FLAG_FIFO) != RT_EOK)
{
rt_kprintf("init jffs2 lock mutex failed\n");
}
rt_kprintf("init jffs2 lock mutex okay\n");
return 0;
}
INIT_COMPONENT_EXPORT(dfs_jffs2_init);