📄 fs-ecos.c
字号:
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by Dominic Ostrowski <dominic.ostrowski@3glab.com>
* Contributors: David Woodhouse, Nick Garnett, Richard Panton.
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs-ecos.c,v 1.37 2004/04/21 18:45:54 gthomas Exp $
*
*/
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include "nodelist.h"
#include <errno.h>
#include <string.h>
#include <cyg/io/io.h>
#include <cyg/io/config_keys.h>
#if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && defined (__ARM_ARCH_4__)
#error This compiler is known to be broken. Please see:
#error http://ecos.sourceware.org/ml/ecos-patches/2003-08/msg00006.html
#endif
//==========================================================================
// Forward definitions
// Filesystem operations
static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte);
static int jffs2_umount(cyg_mtab_entry * mte);
static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int mode, cyg_file * fte);
#ifdef CYGOPT_FS_JFFS2_WRITE
static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir,
const char *name);
static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1,
const char *name1, cyg_dir dir2, const char *name2);
static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
cyg_dir dir2, const char *name2, int type);
#endif
static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_file * fte);
static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_dir * dir_out);
static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
struct stat *buf);
static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len);
static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len);
// File operations
static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
#ifdef CYGOPT_FS_JFFS2_WRITE
static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
#endif
static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
CYG_ADDRWORD data);
static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode);
static int jffs2_fo_close(struct CYG_FILE_TAG *fp);
static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf);
static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len);
static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len);
// Directory operations
static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
static int jffs2_read_inode (struct _inode *inode);
static void jffs2_clear_inode (struct _inode *inode);
//==========================================================================
// Filesystem table entries
// -------------------------------------------------------------------------
// Fstab entry.
// This defines the entry in the filesystem table.
// For simplicity we use _FILESYSTEM synchronization for all accesses since
// we should never block in any filesystem operations.
#ifdef CYGOPT_FS_JFFS2_WRITE
FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILE,
jffs2_mount,
jffs2_umount,
jffs2_open,
jffs2_ops_unlink,
jffs2_ops_mkdir,
jffs2_ops_rmdir,
jffs2_ops_rename,
jffs2_ops_link,
jffs2_opendir,
jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
#else
FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILE,
jffs2_mount,
jffs2_umount,
jffs2_open,
(cyg_fsop_unlink *)cyg_fileio_erofs,
(cyg_fsop_mkdir *)cyg_fileio_erofs,
(cyg_fsop_rmdir *)cyg_fileio_erofs,
(cyg_fsop_rename *)cyg_fileio_erofs,
(cyg_fsop_link *)cyg_fileio_erofs,
jffs2_opendir,
jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
#endif
// -------------------------------------------------------------------------
// File operations.
// This set of file operations are used for normal open files.
static cyg_fileops jffs2_fileops = {
jffs2_fo_read,
#ifdef CYGOPT_FS_JFFS2_WRITE
jffs2_fo_write,
#else
(cyg_fileop_write *) cyg_fileio_erofs,
#endif
jffs2_fo_lseek,
jffs2_fo_ioctl,
cyg_fileio_seltrue,
jffs2_fo_fsync,
jffs2_fo_close,
jffs2_fo_fstat,
jffs2_fo_getinfo,
jffs2_fo_setinfo
};
// -------------------------------------------------------------------------
// Directory file operations.
// This set of operations are used for open directories. Most entries
// point to error-returning stub functions. Only the read, lseek and
// close entries are functional.
static cyg_fileops jffs2_dirops = {
jffs2_fo_dirread,
(cyg_fileop_write *) cyg_fileio_enosys,
jffs2_fo_dirlseek,
(cyg_fileop_ioctl *) cyg_fileio_enosys,
cyg_fileio_seltrue,
(cyg_fileop_fsync *) cyg_fileio_enosys,
jffs2_fo_close,
(cyg_fileop_fstat *) cyg_fileio_enosys,
(cyg_fileop_getinfo *) cyg_fileio_enosys,
(cyg_fileop_setinfo *) cyg_fileio_enosys
};
//==========================================================================
// STATIC VARIABLES !!!
static unsigned char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure
static unsigned char n_fs_mounted = 0; // a counter to track the number of jffs2 instances mounted
//==========================================================================
// Directory operations
struct jffs2_dirsearch {
struct _inode *dir; // directory to search
const char *path; // path to follow
struct _inode *node; // Node found
const char *name; // last name fragment used
int namelen; // name fragment length
cyg_bool last; // last name in path?
};
typedef struct jffs2_dirsearch jffs2_dirsearch;
//==========================================================================
// Ref count and nlink management
// FIXME: This seems like real cruft. Wouldn't it be better just to do the
// right thing?
static void icache_evict(struct _inode *root_i, struct _inode *i)
{
struct _inode *this = root_i, *next;
restart:
D2(printf("icache_evict\n"));
// If this is an absolute search path from the root,
// remove all cached inodes with i_count of zero (these are only
// held where needed for dotdot filepaths)
while (this) {
next = this->i_cache_next;
if (this != i && this->i_count == 0) {
struct _inode *parent = this->i_parent;
if (this->i_cache_next)
this->i_cache_next->i_cache_prev = this->i_cache_prev;
if (this->i_cache_prev)
this->i_cache_prev->i_cache_next = this->i_cache_next;
jffs2_clear_inode(this);
memset(this, 0x5a, sizeof(*this));
free(this);
if (parent && parent != this) {
parent->i_count--;
this = root_i;
goto restart;
}
}
this = next;
}
}
//==========================================================================
// Directory search
// -------------------------------------------------------------------------
// init_dirsearch()
// Initialize a dirsearch object to start a search
static void init_dirsearch(jffs2_dirsearch * ds,
struct _inode *dir, const char *name)
{
D2(printf("init_dirsearch name = %s\n", name));
D2(printf("init_dirsearch dir = %x\n", dir));
dir->i_count++;
ds->dir = dir;
ds->path = name;
ds->node = dir;
ds->name = name;
ds->namelen = 0;
ds->last = false;
}
// -------------------------------------------------------------------------
// find_entry()
// Search a single directory for the next name in a path and update the
// dirsearch object appropriately.
static int find_entry(jffs2_dirsearch * ds)
{
struct _inode *dir = ds->dir;
const char *name = ds->path;
const char *n = name;
char namelen = 0;
struct _inode *d;
D2(printf("find_entry\n"));
// check that we really have a directory
if (!S_ISDIR(dir->i_mode))
return ENOTDIR;
// Isolate the next element of the path name.
while (*n != '\0' && *n != '/')
n++, namelen++;
// Check if this is the last path element.
while( *n == '/') n++;
if (*n == '\0')
ds->last = true;
// update name in dirsearch object
ds->name = name;
ds->namelen = namelen;
if (name[0] == '.')
switch (namelen) {
default:
break;
case 2:
// Dot followed by not Dot, treat as any other name
if (name[1] != '.')
break;
// Dot Dot
// Move back up the search path
D2(printf("find_entry found ..\n"));
ds->dir = ds->node;
ds->node = ds->dir->i_parent;
ds->node->i_count++;
return ENOERR;
case 1:
// Dot is consumed
D2(printf("find_entry found .\n"));
ds->node = ds->dir;
ds->dir->i_count++;
return ENOERR;
}
// Here we have the name and its length set up.
// Search the directory for a matching entry
D2(printf("find_entry for name = %s\n", ds->path));
d = jffs2_lookup(dir, name, namelen);
D2(printf("find_entry got dir = %x\n", d));
if (d == NULL)
return ENOENT;
// If it's a new directory inode, increase refcount on its parent
if (S_ISDIR(d->i_mode) && !d->i_parent) {
d->i_parent = dir;
dir->i_count++;
}
// pass back the node we have found
ds->node = d;
return ENOERR;
}
// -------------------------------------------------------------------------
// jffs2_find()
// Main interface to directory search code. This is used in all file
// level operations to locate the object named by the pathname.
// Returns with use count incremented on both the sought object and
// the directory it was found in
static int jffs2_find(jffs2_dirsearch * d)
{
int err;
D2(printf("jffs2_find for path =%s\n", d->path));
// Short circuit empty paths
if (*(d->path) == '\0') {
d->node->i_count++;
return ENOERR;
}
// iterate down directory tree until we find the object
// we want.
for (;;) {
err = find_entry(d);
if (err != ENOERR)
return err;
if (d->last)
return ENOERR;
/* We're done with it, although it we found a subdir that
will have caused the refcount to have been increased */
jffs2_iput(d->dir);
// Update dirsearch object to search next directory.
d->dir = d->node;
d->path += d->namelen;
while (*(d->path) == '/')
d->path++; // skip dirname separators
}
}
//==========================================================================
// Pathconf support
// This function provides support for pathconf() and fpathconf().
static int jffs2_pathconf(struct _inode *node, struct cyg_pathconf_info *info)
{
int err = ENOERR;
D2(printf("jffs2_pathconf\n"));
switch (info->name) {
case _PC_LINK_MAX:
info->value = LINK_MAX;
break;
case _PC_MAX_CANON:
info->value = -1; // not supported
err = EINVAL;
break;
case _PC_MAX_INPUT:
info->value = -1; // not supported
err = EINVAL;
break;
case _PC_NAME_MAX:
info->value = NAME_MAX;
break;
case _PC_PATH_MAX:
info->value = PATH_MAX;
break;
case _PC_PIPE_BUF:
info->value = -1; // not supported
err = EINVAL;
break;
case _PC_ASYNC_IO:
info->value = -1; // not supported
err = EINVAL;
break;
case _PC_CHOWN_RESTRICTED:
info->value = -1; // not supported
err = EINVAL;
break;
case _PC_NO_TRUNC:
info->value = 0;
break;
case _PC_PRIO_IO:
info->value = 0;
break;
case _PC_SYNC_IO:
info->value = 0;
break;
case _PC_VDISABLE:
info->value = -1; // not supported
err = EINVAL;
break;
default:
err = EINVAL;
break;
}
return err;
}
//==========================================================================
// Filesystem operations
// -------------------------------------------------------------------------
// jffs2_mount()
// Process a mount request. This mainly creates a root for the
// filesystem.
static int jffs2_read_super(struct super_block *sb)
{
struct jffs2_sb_info *c;
Cyg_ErrNo err;
cyg_uint32 len;
cyg_io_flash_getconfig_devsize_t ds;
cyg_io_flash_getconfig_blocksize_t bs;
D1(printk(KERN_DEBUG "jffs2: read_super\n"));
c = JFFS2_SB_INFO(sb);
len = sizeof (ds);
err = cyg_io_get_config(sb->s_dev,
CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &ds, &len);
if (err != ENOERR) {
D1(printf
("jffs2: cyg_io_get_config failed to get dev size: %d\n",
err));
return err;
}
len = sizeof (bs);
bs.offset = 0;
err = cyg_io_get_config(sb->s_dev,
CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &bs, &len);
if (err != ENOERR) {
D1(printf
("jffs2: cyg_io_get_config failed to get block size: %d\n",
err));
return err;
}
c->sector_size = bs.block_size;
c->flash_size = ds.dev_size;
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
err = jffs2_do_mount_fs(c);
if (err)
return -err;
D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
sb->s_root = jffs2_iget(sb, 1);
if (IS_ERR(sb->s_root)) {
D1(printk(KERN_WARNING "get root inode failed\n"));
err = PTR_ERR(sb->s_root);
sb->s_root = NULL;
goto out_nodes;
}
return 0;
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
free(c->blocks);
return err;
}
static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte)
{
extern cyg_mtab_entry cyg_mtab[], cyg_mtab_end;
struct super_block *jffs2_sb = NULL;
struct jffs2_sb_info *c;
cyg_mtab_entry *m;
cyg_io_handle_t t;
Cyg_ErrNo err;
D2(printf("jffs2_mount\n"));
err = cyg_io_lookup(mte->devname, &t);
if (err != ENOERR)
return -err;
// Iterate through the mount table to see if we're mounted
// FIXME: this should be done better - perhaps if the superblock
// can be stored as an inode in the icache.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -