Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/cluster.c linux-2.6.17.14.rfs/fs/rfs/cluster.c
- --- linux-2.6.17.14.orig/fs/rfs/cluster.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/cluster.c 2008-12-12 08:04:51.000000000 +0100
- @@ -0,0 +1,1915 @@
- +/**
- + * @file fs/rfs/cluster.c
- + * @brief FAT cluster & FAT table handling functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +/*
- + * structure for FAT cache (INCORE)
- + */
- +struct rfs_fcache {
- + unsigned int blkoff;
- + unsigned int f_dirty;
- + struct buffer_head *f_bh;
- + struct list_head list;
- +};
- +
- +/*
- + * structure of candidate segment (INCORE)
- + */
- +struct c_segment {
- + unsigned int start_cluster;
- + unsigned int last_cluster;
- + struct list_head list;
- +};
- +
- +/*
- + * structure for map destroy (INCORE)
- + */
- +struct old_map {
- + unsigned int block;
- + unsigned int nr_blocks;
- + struct list_head list;
- +};
- +
- +#define FAT_CACHE_SIZE 128
- +
- +#define FAT_CACHE_HEAD(sb) (&(RFS_SB(sb)->fcache_lru_list))
- +#define FAT_CACHE_ENTRY(p) list_entry(p, struct rfs_fcache, list)
- +#define SEGMENT_ENTRY(p) list_entry(p, struct c_segment, list)
- +
- +#define IS_CONSECUTION(x, y) ((x + 1) == y ? 1 : 0)
- +
- +#define IS_POOL_EMPTY(n) ((n) == POOL_RESERVED_CLUSTER)
- +
- +static int rfs_insert_candidate(struct inode *);
- +static int rfs_put_pool(struct super_block *, unsigned int, unsigned int, unsigned int);
- +
- +/*
- + * FAT table manipulations
- + */
- +
- +/**
- + * initialize internal fat cache entries and add them into fat cache lru list
- + * @param sb super block
- + * @return return 0 on success, -ENOMEM on failure
- + */
- +int rfs_fcache_init(struct super_block *sb)
- +{
- + struct rfs_fcache *array = NULL;
- + int i, len;
- +
- + len = sizeof(struct rfs_fcache) * FAT_CACHE_SIZE;
- + array = (struct rfs_fcache *) kmalloc(len, GFP_KERNEL);
- + if (!array) /* memory error */
- + return -ENOMEM;
- +
- + INIT_LIST_HEAD(FAT_CACHE_HEAD(sb));
- +
- + for (i = 0; i < FAT_CACHE_SIZE; i++) {
- + array[i].blkoff = NOT_ASSIGNED;
- + array[i].f_dirty = FALSE;
- + array[i].f_bh = NULL;
- + list_add_tail(&(array[i].list), FAT_CACHE_HEAD(sb));
- + }
- +
- + RFS_SB(sb)->fcache_array = array;
- +
- + return 0;
- +}
- +
- +/**
- + * destroy fat cache entries and free memory for them
- + * @param sb super block
- + */
- +void rfs_fcache_release(struct super_block *sb)
- +{
- + struct list_head *p;
- + struct rfs_fcache *fcache_p = NULL;
- +
- + /* release buffer head */
- + list_for_each(p, FAT_CACHE_HEAD(sb)) {
- + fcache_p = FAT_CACHE_ENTRY(p);
- + brelse(fcache_p->f_bh);
- + }
- +
- + /* release fcache */
- + if (RFS_SB(sb)->fcache_array)
- + kfree(RFS_SB(sb)->fcache_array);
- +}
- +
- +/**
- + * sync all fat cache entries if dirty flag of them are set
- + * @param sb super block
- + * @param flush whether to flush or not
- + *
- + * mark dirty flag of buffer head corresponding with all fat cache entries and nullify them
- + */
- +void rfs_fcache_sync(struct super_block *sb, int flush)
- +{
- + struct rfs_fcache *fcache_p;
- + struct list_head *head, *p;
- +
- + head = FAT_CACHE_HEAD(sb);
- +
- + list_for_each(p, head) {
- + fcache_p = FAT_CACHE_ENTRY(p);
- + if (fcache_p->f_dirty) {
- + rfs_mark_buffer_dirty(fcache_p->f_bh, sb);
- + fcache_p->f_dirty = FALSE;
- +
- + if (unlikely(flush)) {
- + ll_rw_block(WRITE, 1, &fcache_p->f_bh);
- + wait_on_buffer(fcache_p->f_bh);
- + }
- + }
- + }
- +}
- +
- +/**
- + * change status of fat cache entry
- + * @param sb super block
- + * @param blkoff block number to modify
- + *
- + * if blkoff is equal to block number(blkoff) fat cache entry, set a dirty flag
- + */
- +static void rfs_fcache_modified(struct super_block *sb, unsigned int blkoff)
- +{
- + struct rfs_fcache *fcache_p = NULL;
- + struct list_head *p;
- +
- + list_for_each(p, FAT_CACHE_HEAD(sb)) {
- + fcache_p = FAT_CACHE_ENTRY(p);
- + if (fcache_p->blkoff == blkoff) {
- + fcache_p->f_dirty = TRUE;
- + return;
- + }
- + }
- +}
- +
- +/**
- + * append fat cache entry to fcache lru list
- + * @param sb super block
- + * @param blkoff block number of fat cache entry
- + * @return return buffer head corrsponding with fat cache entry on success, NULL on failure
- + */
- +static struct buffer_head *rfs_fcache_add(struct super_block *sb, unsigned int blkoff)
- +{
- + struct rfs_fcache *fcache_p;
- + struct buffer_head *bh;
- + struct list_head *head, *p;
- +
- + head = FAT_CACHE_HEAD(sb);
- +
- +retry:
- + p = head->prev;
- + fcache_p = FAT_CACHE_ENTRY(p);
- + while (fcache_p->f_bh && fcache_p->f_dirty && p != head) {
- + /* We flush the dirty FAT caches only in fat_sync() */
- + p = p->prev;
- + fcache_p = FAT_CACHE_ENTRY(p);
- + }
- +
- + if (unlikely(p == head)) {
- + /* there is no clean fat cache. So, perform fat cache flush */
- + rfs_fcache_sync(sb, 1);
- + goto retry;
- + }
- +
- + brelse(fcache_p->f_bh);
- +
- + /*
- + * initialize fat cache with some effort
- + * because possible error of following sb_bread() makes it false
- + */
- + fcache_p->f_bh = NULL;
- + fcache_p->blkoff = NOT_ASSIGNED;
- +
- + bh = rfs_bread(sb, blkoff, BH_RFS_FAT);
- + if (!bh) { /* I/O error */
- + DPRINTK("can't get buffer head related with fat block\n");
- + return NULL;
- + }
- +
- + /* fill fcache */
- + fcache_p->blkoff = blkoff;
- + fcache_p->f_dirty = FALSE; /* just read */
- + fcache_p->f_bh = bh;
- +
- + list_move(p, head);
- +
- + return bh;
- +}
- +
- +/**
- + * lookup fat cache entry by using blkoff
- + * @param sb super block
- + * @param blkoff block number
- + * @return buffer head of fat cache entry
- + *
- + * if fat cache entry doesn't exist, get free fat entry and fill it
- + */
- +static struct buffer_head *rfs_fcache_get(struct super_block *sb, unsigned int blkoff)
- +{
- + struct rfs_fcache *fcache_p = NULL;
- + struct list_head *head, *p;
- +
- + /* find fcache entry included blkoff */
- + head = FAT_CACHE_HEAD(sb);
- + list_for_each(p, head) {
- + fcache_p = FAT_CACHE_ENTRY(p);
- + if (fcache_p->blkoff == blkoff) {
- + /* Update LRU list */
- + if (p != head->next)
- + list_move(p, head);
- + return fcache_p->f_bh; /* found */
- + }
- + }
- +
- + return rfs_fcache_add(sb, blkoff);
- +}
- +
- +/**
- + * read a block that contains a fat entry
- + * @param sb super block
- + * @param[in, out] index index number of fat entry to calculate block number
- + * @param[out] blocknr block number corresponding to index
- + * @return buffer head of fat cache entry which include a fat entry
- + */
- +static struct buffer_head *fat_read_block(struct super_block *sb, unsigned int *index, unsigned int *blocknr)
- +{
- + unsigned int block, offset = *index;
- +
- + if (IS_FAT16(RFS_SB(sb)))
- + offset = (offset << 1) + RFS_SB(sb)->fat_start_addr;
- + else if (IS_FAT32(RFS_SB(sb)))
- + offset = (offset << 2) + RFS_SB(sb)->fat_start_addr;
- +
- + block = offset >> sb->s_blocksize_bits;
- +
- + *index = offset;
- + *blocknr = block;
- +
- + return rfs_fcache_get(sb, block);
- +}
- +
- +/**
- + * read a fat entry in fat table
- + * @param sb super block
- + * @param location location to read a fat entry
- + * @param[out] content content cluster number which is saved in fat entry
- + * @return return 0 on sucess, EIO on failure
- + */
- +int fat_read(struct super_block *sb, unsigned int location, unsigned int *content)
- +{
- + struct buffer_head *bh = NULL;
- + unsigned int index = location, dummy;
- +
- + if (IS_INVAL_CLU(RFS_SB(sb), location)) {
- + /* out-of-range input */
- + RFS_BUG("invalid cluster number(%u)\n", location);
- + return -EINVAL;
- + }
- +
- + if (!(bh = fat_read_block(sb, &index, &dummy))) { /* I/O error */
- + RFS_BUG("can't get buffer head related with fat block\n");
- + return -EIO;
- + }
- +
- + if (IS_FAT16(RFS_SB(sb))) {
- + index &= (sb->s_blocksize - 1);
- + *content = GET16(((u16 *)bh->b_data)[index >> 1]);
- +
- + if (*content >= 0xFFF8)
- + *content = CLU_TAIL; /* last cluster of fat chain */
- +
- + } else if (IS_FAT32(RFS_SB(sb))) {
- + index &= (sb->s_blocksize - 1);
- + *content = GET32(((u32 *)bh->b_data)[index >> 2]);
- +
- + if (*content >= 0xFFFFFF8)
- + *content = CLU_TAIL; /* last cluster of chain */
- + }
- +
- + /* sanity check */
- + if ((*content != CLU_FREE) && (*content != CLU_TAIL)) {
- + if (IS_INVAL_CLU(RFS_SB(sb), *content)) {
- + RFS_BUG("invalid contents(%u)\n", *content);
- + return -EIO;
- + }
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * write a fat entry in fat table 1 & 2
- + * @param sb super block
- + * @param location location to write a fat entry
- + * @param content next cluster number or end mark of fat chain
- + * @return return 0 on sucess, EIO or EINVAL on failure
- + * @pre content shouldn't have 0 or 1 which are reserved
- + */
- +int fat_write(struct super_block *sb, unsigned int location, unsigned int content)
- +{
- + struct buffer_head *bh;
- + unsigned int index = location, block;
- +
- + if (IS_INVAL_CLU(RFS_SB(sb), location)) {
- + /* out-of-range input */
- + RFS_BUG("invalid cluster number(%u)\n", location);
- + return -EINVAL;
- + }
- +
- + /* sanity check */
- + if ((content != CLU_FREE) && (content != CLU_TAIL)) {
- + if (IS_INVAL_CLU(RFS_SB(sb), content)) {
- + RFS_BUG("invalid contents(%u)\n", content);
- + return -EIO;
- + }
- + }
- +
- + bh = fat_read_block(sb, &index, &block);
- + if (!bh) { /* I/O error */
- + RFS_BUG("Can't get buffer head related with fat block\n");
- + return -EIO;
- + }
- +
- + if (IS_FAT16(RFS_SB(sb))) {
- + index &= sb->s_blocksize - 1;
- + content &= 0x0000FFFF;
- + SET16(((u16 *)bh->b_data)[index >> 1], content);
- +
- + } else if (IS_FAT32(RFS_SB(sb))) {
- + index &= sb->s_blocksize - 1;
- + content &= 0xFFFFFFFF;
- + SET32(((u32 *)bh->b_data)[index >> 2], content);
- + }
- +
- + rfs_fcache_modified(sb, block);
- +
- + return 0;
- +}
- +
- +/*
- + * Cluster manipulations
- + */
- +
- +/**
- + * find free cluster in fat table
- + * @param inode inode
- + * @param[out] free_clu free cluster number found
- + * @return return 0 on success, errno on failure
- + */
- +int find_free_cluster(struct inode *inode, unsigned int *free_clu)
- +{
- + struct rfs_sb_info *sbi = RFS_SB(inode->i_sb);
- + unsigned int i;
- + int err;
- +
- + for (i = VALID_CLU; i < sbi->num_clusters; i++) {
- + /* search free cluster from hint(search_ptr) */
- + if (sbi->search_ptr >= sbi->num_clusters)
- + sbi->search_ptr = VALID_CLU;
- +
- + err = fat_read(inode->i_sb, sbi->search_ptr, free_clu);
- + if (!err && *free_clu == 0) {
- + *free_clu = sbi->search_ptr++;
- + return 0;
- + }
- + sbi->search_ptr++;
- + }
- +
- + return -ENOSPC;
- +}
- +
- +/**
- + * find last cluster and return the number of clusters from specified fat chain of inode
- + * @param inode inode
- + * @param[out] last_clu last cluster number
- + * @return return the number of clusters on success, errno on failure
- + */
- +int find_last_cluster(struct inode *inode, unsigned int *last_clu)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int prev, next;
- + int count = 0;
- + int err = 0;
- +
- + /* set default value */
- + if (last_clu)
- + *last_clu = CLU_TAIL;
- +
- + prev = RFS_I(inode)->start_clu;
- +
- + if (prev == CLU_TAIL) /* cluster dose not be allocated */
- + return 0;
- +
- + fat_lock(sb);
- +
- + while (1) {
- + err = fat_read(sb, prev, &next);
- + if (err) {
- + fat_unlock(sb);
- + return -EIO;
- + }
- +
- + if (next < VALID_CLU) { /* out-of-range input */
- + fat_unlock(sb);
- + RFS_BUG("fat entry(%u) was corrupted\n", next);
- + return -EIO;
- + }
- +
- + count++;
- +
- + if (next == CLU_TAIL) /* found last clu */
- + break;
- +
- + prev = next;
- + }
- +
- + fat_unlock(sb);
- +
- + if (last_clu) {
- + *last_clu = prev;
- + DEBUG(DL2, "last cluster number = %d \n", *last_clu);
- + }
- +
- +
- + return count;
- +}
- +
- +/**
- + * append new cluster to fat chain of inode
- + * @param inode inode
- + * @param last_clu current last cluster number of the fat chain
- + * @param new_clu new last cluster number to add
- + * @return return 0 on success, errno on failure
- + */
- +int append_new_cluster(struct inode *inode, unsigned int last_clu, unsigned int new_clu)
- +{
- + int err;
- +
- + err = fat_write(inode->i_sb, new_clu, CLU_TAIL);
- + if (err) {
- + DPRINTK("can't write a fat entry(%u)\n", new_clu);
- + return err;
- + }
- +
- + err = fat_write(inode->i_sb, last_clu, new_clu);
- + if (err) {
- + DPRINTK("can't write a fat entry(%u). "
- + "cluster(%u) is lost\n", last_clu, new_clu);
- + return err;
- + }
- +
- + return err;
- +}
- +
- +/**
- + * allocate a new cluster from pool file or fat table
- + * @param inode inode
- + * @param[out] new_clu new clsuter number to be allocated
- + * @param last_clu last clsuter number for logging
- + * @return return 0 on success, errno on failure
- + */
- +static int get_cluster(struct inode *inode, unsigned int *new_clu, unsigned int last_clu)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int pool_prev, pool_next;
- + unsigned int clu;
- + int where, err;
- +
- + if (!RFS_SB(sb)->pool_info ||
- + IS_POOL_EMPTY(RFS_POOL_I(sb)->num_clusters)) {
- + /* alloc-cluster from fat table */
- + err = find_free_cluster(inode, &clu);
- + if (err)
- + return err;
- +
- + pool_prev = pool_next = CLU_TAIL;
- + where = RFS_FAT_TABLE;
- + } else {
- + /* alloc-cluster from pool file */
- + unsigned int dummy;
- +
- + err = rfs_shrink_pool_chain(sb, &clu,
- + 1, &dummy, &pool_next);
- + if (err)
- + return err;
- +
- + pool_prev = RFS_POOL_I(sb)->start_cluster;
- + where = RFS_POOL;
- + }
- +
- + err = rfs_log_alloc_chain(sb, RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index, pool_prev, pool_next,
- + last_clu, CLU_TAIL, 1, &clu);
- + if (err)
- + return err;
- +
- + if (where == RFS_POOL) {
- + err = rfs_get_pool(sb, pool_next, 1);
- + if (err)
- + return err;
- + }
- +
- + *new_clu = clu;
- + return 0;
- +}
- +
- +/**
- + * allocate a new cluster from pool file or fat table
- + * @param inode inode
- + * @param[out] new_clu new clsuter number to be allocated
- + * @return return 0 on success, errno on failure
- + *
- + * if file write or expand file(truncate), pre-allocation is available
- + * if fat table doesn't have a free cluster at normal allocation case, free cluster will be allocated in pool file
- + */
- +int alloc_cluster(struct inode *inode, unsigned int *new_clu)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int last_clu;
- + int is_first = FALSE;
- + int err;
- +
- + if (RFS_I(inode)->start_clu < VALID_CLU) { /* out-of-range input */
- + DPRINTK("inode has invalid start cluster(%u)\n",
- + RFS_I(inode)->start_clu);
- + return -EINVAL;
- + }
- +
- + fat_lock(sb);
- +
- + if (RFS_I(inode)->start_clu != CLU_TAIL)
- + last_clu = RFS_I(inode)->last_clu;
- + else
- + last_clu = CLU_TAIL;
- +
- + /* Phase 1 : get one free cluster in source */
- + if (tr_pre_alloc(sb)) { /* pre-allocation case */
- + err = rfs_log_get_cluster(inode, new_clu);
- + if (err)
- + goto out;
- + } else { /* normal allocation case */
- + err = get_cluster(inode, new_clu, last_clu);
- + if (err)
- + goto out;
- + }
- +
- + /* Phase 2 : append free cluster to end of fat chain related to inode */
- + if (RFS_I(inode)->start_clu == CLU_TAIL)
- + err = fat_write(sb, *new_clu, CLU_TAIL);
- + else
- + err = append_new_cluster(inode, last_clu, *new_clu);
- + if (err)
- + goto out;
- +
- + /* update start & last cluster */
- + if (RFS_I(inode)->start_clu == CLU_TAIL) {
- + RFS_I(inode)->start_clu = *new_clu;
- + is_first = TRUE;
- + }
- + RFS_I(inode)->last_clu = *new_clu;
- + inode->i_blocks += 1 << (RFS_SB(sb)->cluster_bits - SECTOR_BITS);
- +
- + RFS_SB(sb)->num_used_clusters++;
- +
- + /* check rfs-specific inode state */
- + if (unlikely(RFS_I(inode)->i_state == RFS_I_FREE)) {
- + /* RFS-log : have already logged things about following */
- + if (is_first)
- + err = rfs_attach_candidate(inode);
- + else
- + err = rfs_insert_candidate(inode);
- + }
- +
- +out:
- + fat_unlock(sb);
- +
- + return err;
- +}
- +
- +#ifdef CONFIG_RFS_MAPDESTROY
- +extern int (*xsr_stl_delete)(dev_t dev, u32 start, u32 nums, u32 b_size);
- +#endif
- +
- +/**
- + * destroy stl map corresponding with deallocated clusters
- + * @param sb super block
- + * @return return 0 on success
- + *
- + * it is usually invoked after unlink & truncate
- + */
- +int rfs_map_destroy(struct super_block *sb)
- +{
- + struct old_map *old_map;
- + struct list_head *this, *next;
- +
- + list_for_each_safe(this, next, &RFS_SB(sb)->free_chunks) {
- + old_map = list_entry(this, struct old_map, list);
- + if (!old_map) {
- + RFS_BUG("Chunk is NULL for fat cleansing\n");
- + return -EIO;
- + }
- + DEBUG(DL2, "block = %d nr_blocks = %d",
- + old_map->block, old_map->nr_blocks);
- +#ifdef CONFIG_RFS_MAPDESTROY
- + xsr_stl_delete(sb->s_dev, old_map->block, old_map->nr_blocks,
- + sb->s_blocksize);
- +#endif
- + /* delete chunk element from list and free them */
- + list_del(this);
- + kfree(old_map);
- + }
- +
- + /* reinitialize chunk list head */
- + list_del_init(&RFS_SB(sb)->free_chunks);
- +
- + return 0;
- +}
- +
- +/**
- + * make a chunk element and add it to chunk list
- + * @param sb super block
- + * @param start start cluster number to free
- + * @param nr_clusters number of clusters to free
- + * @return return 0 on success
- + */
- +static int rfs_add_chunk(struct super_block *sb, unsigned int start, unsigned int nr_clusters)
- +{
- + struct old_map *old_map;
- + unsigned int start_block = START_BLOCK(start, sb);
- +
- + if (!IS_XSR(sb->s_dev))
- + return 0;
- +
- + if (!nr_clusters)
- + return 0;
- +
- + old_map = (struct old_map *)
- + kmalloc(sizeof(struct old_map), GFP_KERNEL);
- + if (!old_map) { /* memory error */
- + DEBUG(DL1, "memory allocation failed\n");
- + return -ENOMEM;
- + }
- +
- + old_map->block = start_block;
- + old_map->nr_blocks = nr_clusters << RFS_SB(sb)->blks_per_clu_bits;
- + DEBUG(DL2, "start block = %d nr_blocks = %d",
- + old_map->block, old_map->nr_blocks);
- +
- + list_add_tail(&old_map->list, &RFS_SB(sb)->free_chunks);
- +
- + return 0;
- +}
- +
- +/**
- + * count clusters in fat chain & make a chunk list for stl delete
- + * @param sb super block
- + * @param start_clu start cluster number of fat chain
- + * @param[out] last_clu last cluster number of fat chain
- + * @param last_val content of last cluster
- + * @param[out] clusters number of clusters which may be appended or not
- + * @return return 0 on success, errno on failure
- + *
- + */
- +static int rfs_check_size(struct super_block *sb, unsigned int start_clu, unsigned int *last_clu, unsigned int last_val, unsigned int *clusters)
- +{
- + unsigned int prev = start_clu, next;
- + unsigned int total_nr_clus = 0, chunk_nr_clus = 0;
- + unsigned int chunk_start = start_clu;
- + int err;
- +
- + while (1) { /* count clusters from start_clu */
- + err = fat_read(sb, prev, &next);
- + if (err) {
- + DPRINTK("can't read a fat entry(%u)\n", prev);
- + return err;
- + }
- +
- + if (next < VALID_CLU) { /* out-of-range input */
- + RFS_BUG("fat entry(%u) was corrupted\n", next);
- + return -EIO;
- + }
- +
- + total_nr_clus++;
- +
- + /* make a chunk list for map destroy */
- + chunk_nr_clus++;
- + if (!IS_CONSECUTION(prev, next)) {
- + rfs_add_chunk(sb, chunk_start, chunk_nr_clus);
- + chunk_start = next;
- + chunk_nr_clus = 0;
- + }
- +
- + if (prev == last_val || next == CLU_TAIL) {
- + if (IS_CONSECUTION(prev, next))
- + rfs_add_chunk(sb, chunk_start, chunk_nr_clus);
- + break;
- + }
- +
- + prev = next;
- + }
- +
- + *last_clu = prev;
- + *clusters = total_nr_clus;
- +
- + return 0;
- +}
- +
- +/**
- + * free fat chain from start_clu to EOC
- + * @param inode inode
- + * @param new_last new last cluster number
- + * @param start_clu start cluster number to free
- + * @param[out] count number of clusters which will be freed
- + * @return return 0 on success, errno on failure
- + * @pre caller must have a mutex for fat table
- + *
- + * new_last is only available if reduce a file especially truncate case
- + */
- +int free_chain(struct inode *inode, unsigned int new_last, unsigned int start_clu, unsigned int *count)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int free_clus[2];
- + unsigned int free_count;
- + unsigned int next;
- + int err = 0;
- +
- + *count = free_count = 0;
- +
- + /* check whether clusters are appended to pool file */
- + err = rfs_check_size(sb, start_clu, &next, CLU_TAIL, &free_count);
- + if (err)
- + return err;
- +
- + free_clus[0] = start_clu;
- + free_clus[1] = RFS_I(inode)->last_clu;
- + if (rfs_log_move_chain(sb, RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + RFS_POOL_I(sb)->last_cluster,
- + RFS_POOL_I(sb)->c_start_cluster,
- + new_last, CLU_TAIL, 2, free_clus)) {
- + return -EIO;
- + }
- +
- + err = rfs_put_pool(sb, start_clu, next, free_count);
- + if (err) {
- + DPRINTK("can't append free clusters into pool\n");
- + return err;
- + }
- +
- + if (new_last != CLU_TAIL) {
- + /* mark new last cluster */
- + err = fat_write(sb, new_last, CLU_TAIL);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n", new_last);
- + return err;
- + }
- +
- + RFS_I(inode)->last_clu = new_last;
- + } else {
- + RFS_I(inode)->start_clu = CLU_TAIL;
- + RFS_I(inode)->last_clu = CLU_TAIL;
- + }
- +
- + /* update used clusters */
- + RFS_SB(inode->i_sb)->num_used_clusters -= free_count;
- + *count = free_count;
- +
- + return 0;
- +}
- +
- +/**
- + * deallocate clusters after skipping some clusters
- + * @param inode inode
- + * @param skip the number of clusters to skip
- + * @return return 0 on success, errno on failure
- + */
- +int dealloc_clusters(struct inode *inode, unsigned int skip)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int new_last = NOT_ASSIGNED;
- + unsigned int prev, next;
- + unsigned int off = skip - 1;
- + unsigned int count = 0;
- + int err = 0;
- +
- + fat_lock(sb);
- +
- + if (!skip) { /* free all clusters */
- + next = RFS_I(inode)->start_clu;
- + goto free;
- + }
- +
- + err = find_cluster(sb, RFS_I(inode)->start_clu, off, &prev, &next);
- + if (err)
- + goto out;
- +
- + /* change last cluster number */
- + new_last = prev;
- +
- +free:
- + if (next == CLU_TAIL) /* do not need free chain */
- + goto out;
- +
- + err = free_chain(inode, new_last, next, &count);
- +
- +
- +out:
- + fat_unlock(sb);
- +
- + return err;
- +}
- +
- +/**
- + * count all used clusters in file system
- + * @param sb super block
- + * @param[out] used_clusters the number of used clusters in volume
- + * @return return 0 on success, errno on failure
- + *
- + * cluster 0 & 1 are reserved according to the fat spec
- + */
- +int count_used_clusters(struct super_block *sb, unsigned int *used_clusters)
- +{
- + unsigned int i, clu;
- + unsigned int count = 2; /* clu 0 & 1 are reserved */
- + int err;
- +
- + fat_lock(sb);
- +
- + for (i = VALID_CLU; i < RFS_SB(sb)->num_clusters; i++) {
- + err = fat_read(sb, i, &clu);
- + if (err) {
- + fat_unlock(sb);
- + DPRINTK("can't read a fat entry (%u)\n", i);
- + return err;
- + }
- +
- + if (clu)
- + count++;
- + }
- +
- + *used_clusters = count;
- +
- + fat_unlock(sb);
- +
- + return 0;
- +}
- +
- +/**
- + * find a cluster which has an offset into fat chain
- + * @param sb super block
- + * @param start_clu start cluster number of fat chain
- + * @param off offset within fat chain
- + * @param[out] clu cluster number found
- + * @param[out] value fat entry value of cluster
- + * @return return 0 on success, errno on failure
- + * @pre caller should get fat lock
- + */
- +int find_cluster(struct super_block *sb, unsigned int start_clu, unsigned int off, unsigned int *clu, unsigned int *value)
- +{
- + unsigned int prev, next;
- + unsigned int i;
- + int err;
- +
- + prev = start_clu;
- + for (i = 0; i <= off; i++) {
- + err = fat_read(sb, prev, &next);
- + if (err) {
- + DPRINTK("can't read a fat entry (%u)\n", prev);
- + return err;
- + }
- +
- + if (next < VALID_CLU) { /* out-of-range input */
- + /*
- + * If reset happened during appending a cluster,
- + * the appending cluster had a free status (0).
- + * EFAULT notifies it to replay method.
- + */
- + if (tr_in_replay(sb))
- + return -EFAULT;
- + else
- + return -EIO;
- + }
- +
- + if (i == off) /* do not need to change prev to next */
- + break;
- +
- + if (next == CLU_TAIL)
- + return -EFAULT; /* over request */
- +
- + prev = next;
- + }
- +
- + *clu = prev;
- + *value = next;
- + return 0;
- +}
- +
- +/*
- + * fast unlink
- + */
- +
- +/**
- + * initialize first block for meta data of the pool file
- + * @param sb super block
- + * @param index index of dir entry
- + * @param start_clu start cluster number of the pool file
- + * @param blocknr block number to save the dir entry of pool file
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_init_pool_block(struct super_block *sb, unsigned int index, unsigned int start_clu, unsigned long blocknr)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_pool_info *pool_info = NULL;
- +
- + bh = sb_getblk(sb, START_BLOCK(start_clu, sb));
- + if (!bh) { /* I/O error */
- + DPRINTK("can't get buffer head to get dir entry of pool file\n");
- + return -EIO;
- + }
- + memset(bh->b_data, 0x00, sb->s_blocksize);
- +
- + pool_info = (struct rfs_pool_info *) bh->b_data;
- + SET32(pool_info->index, index);
- + SET32(pool_info->start_cluster, start_clu);
- + SET32(pool_info->last_cluster, start_clu);
- + SET32(pool_info->num_clusters, POOL_RESERVED_CLUSTER);
- + SET32(pool_info->c_start_cluster, NOT_ASSIGNED);
- + SET32(pool_info->c_last_cluster, NOT_ASSIGNED);
- + SET32(pool_info->blocknr, blocknr);
- +
- +#ifdef RFS_FOR_2_6
- + set_buffer_uptodate(bh);
- +#else
- + mark_buffer_uptodate(bh, 1);
- +#endif
- + mark_buffer_dirty(bh);
- + brelse(bh);
- +
- + return 0;
- +}
- +
- +/**
- + * allocate memory and make the in-core meta data of pool file
- + * @param sb super block
- + * @param index index of dir entry
- + * @param start_clu start cluster number of the pool file
- + * @param blocknr block number which saved dir entry
- + * @return return 0 on success, errno on failure
- + *
- + * hint info are written in start block of start cluster allocated the pool file
- + */
- +static int rfs_alloc_pool(struct super_block *sb, unsigned int index, unsigned int start_clu, unsigned long blocknr)
- +{
- + struct rfs_pool_info *pool_info = NULL, *disk_pool_info = NULL;
- + struct buffer_head *bh = NULL;
- +
- + /* read hint info */
- + bh = rfs_bread(sb, START_BLOCK(start_clu, sb), BH_RFS_POOL_BLOCK);
- + if (!bh) { /* I/O error */
- + DPRINTK("can't get buffer head to get head of pool file\n");
- + return -EIO;
- + }
- +
- + disk_pool_info = (struct rfs_pool_info *) bh->b_data;
- +
- + /* memory allocation */
- + pool_info = (struct rfs_pool_info *)
- + kmalloc(sizeof(struct rfs_pool_info), GFP_KERNEL);
- + if (!pool_info) { /* memory error */
- + DEBUG(DL1, "failed memory allocation for pool info\n");
- + brelse(bh);
- + return -ENOMEM;
- + }
- +
- + /* compare disk info and in-core info */
- + if ((index != GET32(disk_pool_info->index)) ||
- + (start_clu != GET32(disk_pool_info->start_cluster))) {
- + kfree(pool_info);
- + brelse(bh);
- + DPRINTK("disk info and in-core info aren't equal. The file system is corrupted.\n");
- + return -EIO;
- + }
- +
- + /* initialize pool info */
- + pool_info->index = GET32(disk_pool_info->index);
- +// pool_info->blocknr = GET32(disk_pool_info->blocknr);
- + pool_info->start_cluster = GET32(disk_pool_info->start_cluster);
- + pool_info->last_cluster = GET32(disk_pool_info->last_cluster);
- + pool_info->num_clusters = GET32(disk_pool_info->num_clusters);
- + pool_info->c_start_cluster = GET32(disk_pool_info->c_start_cluster);
- + pool_info->c_last_cluster = GET32(disk_pool_info->c_last_cluster);
- +#ifdef RFS_UPDATE_POOLHEADER_AT_MOUNT
- + pool_info->blocknr = GET32(disk_pool_info->blocknr);
- +
- + if (pool_info->blocknr != blocknr) { /* reinit block number */
- + pool_info->blocknr = blocknr;
- + SET32(disk_pool_info->blocknr, blocknr);
- + mark_buffer_dirty(bh);
- + }
- +#else
- + pool_info->blocknr = blocknr;
- +#endif
- +
- + INIT_LIST_HEAD(&pool_info->c_list);
- +
- + brelse(bh);
- +
- + RFS_SB(sb)->pool_info = (void *) pool_info;
- +
- + return 0;
- +}
- +
- +/**
- + * open the pool file
- + * @param sb super block
- + * @return return 0 on success, errno on failure
- + *
- + * the pool file doesn't exist, make the pool file before anything else
- + */
- +static int rfs_open_pool(struct super_block *sb)
- +{
- + struct inode *root_dir = sb->s_root->d_inode;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned int index;
- + int ret;
- +
- + /* find pool file */
- + ret = find_entry(root_dir, RFS_POOL_FILE_NAME, &bh, TYPE_FILE);
- + if (ret < 0) {
- + brelse(bh);
- + return ret;
- + }
- +
- + index = ret;
- + ep = get_entry(root_dir, index, &bh);
- + if (IS_ERR(ep)) {
- + brelse(bh);
- + return PTR_ERR(ep);
- + }
- +
- + /* allocate memory for hint & fill hint info */
- + ret = rfs_alloc_pool(sb, index, START_CLUSTER(ep), bh->b_blocknr);
- + if (ret) {
- + brelse(bh);
- + return ret;
- + }
- +
- + brelse(bh);
- + return 0;
- +}
- +
- +/**
- + * make the pool file
- + * @param sb super block
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_make_pool(struct super_block *sb)
- +{
- + struct inode *root_dir = sb->s_root->d_inode;
- + struct inode *inode = NULL;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned int start_clu, index;
- + unsigned long blocknr;
- + int err;
- +
- + /* get a new inode */
- + if (!(inode = new_inode(sb)))
- + return -ENOMEM; /* memory error */
- +
- + /* allocate start cluster */
- + RFS_I(inode)->start_clu = CLU_TAIL;
- + err = alloc_cluster(inode, &start_clu);
- + if (err)
- + goto remove_inode;
- +
- + /* make a dir entry */
- + index = build_entry(root_dir, NULL, start_clu, TYPE_FILE,
- + RFS_POOL_FILE_NAME);
- + if ((int) index < 0) {
- + err = index;
- + goto dealloc_cluster;
- + }
- +
- + ep = get_entry(root_dir, index, &bh);
- + if (IS_ERR(ep)) {
- + err = PTR_ERR(ep);
- + goto remove_entry;
- + }
- +
- + blocknr = bh->b_blocknr;
- +
- + ep->attr |= (ATTR_READONLY | ATTR_SYSTEM | ATTR_HIDDEN);
- + SET32(ep->size, RFS_SB(sb)->cluster_size);
- +
- + /* initialize first block of cluster */
- + err = rfs_init_pool_block(sb, index, start_clu, blocknr);
- + if (err) /* I/O error */
- + goto remove_entry;
- +
- + /* allocate memory for hint & fill hint info */
- + err = rfs_alloc_pool(sb, index, start_clu, blocknr);
- + if (err)
- + goto remove_entry;
- +
- + mark_buffer_dirty(bh);
- + brelse(bh);
- +
- + iput(inode);
- +
- + /* flush buffers for internal fcache & dir entry */
- + rfs_sync_vol(sb);
- +
- + return 0;
- +
- +remove_entry:
- + ep->name[0] = (u8) DELETE_MARK;
- + mark_buffer_dirty(bh);
- + brelse(bh);
- +dealloc_cluster:
- + fat_write(sb, start_clu, CLU_FREE);
- +remove_inode:
- + inode->i_nlink--;
- + RFS_BUG_ON(inode->i_nlink);
- + iput(inode);
- + return err;
- +}
- +
- +/**
- + * initialize the pool file at mount time
- + * @param sb super block
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_init_pool(struct super_block *sb)
- +{
- + int err;
- +
- + if (RFS_POOL_I(sb)) /* already initialize it */
- + return 0;
- +
- + /* check whether pool file exist */
- + err = rfs_open_pool(sb);
- + if (err == -ENOENT) {
- + /* make pool file */
- + err = rfs_make_pool(sb);
- + if (err) {
- + DPRINTK("can not create a pool file\n");
- + return err;
- + }
- + } else if (err) {
- + /* unexpected error case */
- + DPRINTK("can not open pool file\n");
- + return err;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * release the pool file at umount time
- + * @param sb super block
- + */
- +void rfs_release_pool(struct super_block *sb)
- +{
- + if (!RFS_POOL_I(sb))
- + return;
- +
- + kfree(RFS_POOL_I(sb));
- +}
- +
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- +int sanity_check_pool(struct super_block *sb)
- +{
- + unsigned int prev = RFS_POOL_I(sb)->start_cluster;
- + unsigned int next;
- + unsigned int p_last_clu = RFS_POOL_I(sb)->last_cluster;
- + unsigned int c_start_clu = RFS_POOL_I(sb)->c_start_cluster;
- + unsigned int num_clus = RFS_POOL_I(sb)->num_clusters;
- + unsigned int count = 1;
- + int err;
- +
- + next = prev;
- +
- + while (count < num_clus) {
- + err = fat_read(sb, prev, &next);
- + if (err)
- + return -1;
- +
- + if (next == CLU_TAIL)
- + break;
- +
- + count++;
- + if (next == p_last_clu)
- + break;
- +
- + prev = next;
- + }
- +
- + if (next != p_last_clu || count != RFS_POOL_I(sb)->num_clusters) {
- + RFS_BUG("pool corrupted (%d, %d) (%d, %d)\n", next, p_last_clu,
- + count, RFS_POOL_I(sb)->num_clusters);
- + return -1;
- + }
- +
- + prev = next;
- + fat_read(sb, prev, &next);
- +
- + if (next != c_start_clu) {
- + RFS_BUG("candidate corrupted(%d, %d)\n", next, c_start_clu);
- + return -1;
- + }
- +
- + return 0;
- +}
- +#endif
- +
- +/**
- + * update last cluster number and total clusters for the pool file
- + * @param sb super block
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_update_pool_block(struct super_block *sb)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_pool_info *disk_pool = NULL;
- + struct rfs_pool_info *rpi = RFS_POOL_I(sb);
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + int bug_on = 0;
- +
- + bh = rfs_bread(sb, START_BLOCK(rpi->start_cluster, sb),
- + BH_RFS_POOL_BLOCK);
- + if (!bh) { /* I/O error */
- + DPRINTK("can't get buffer head to get head of pool file\n");
- + return -EIO;
- + }
- +
- + /* sanity check */
- + if (unlikely(rpi->last_cluster != CLU_TAIL &&
- + IS_INVAL_CLU(sbi, rpi->last_cluster)))
- + bug_on = 1;
- + else if (unlikely(rpi->num_clusters > RFS_SB(sb)->num_clusters - 2))
- + bug_on = 1;
- + else if (unlikely(rpi->c_start_cluster != CLU_TAIL &&
- + IS_INVAL_CLU(sbi, rpi->c_start_cluster)))
- + bug_on = 1;
- + else if (unlikely(rpi->c_last_cluster != CLU_TAIL &&
- + IS_INVAL_CLU(sbi, rpi->c_last_cluster)))
- + bug_on = 1;
- +
- + if (unlikely(bug_on)) {
- + DPRINTK("in-memory pool is corrupted(%u, %u, %u, %u)\n",
- + rpi->last_cluster, rpi->num_clusters,
- + rpi->c_start_cluster, rpi->c_last_cluster);
- + BUG();
- + }
- +
- +
- + disk_pool = (struct rfs_pool_info *) bh->b_data;
- + SET32(disk_pool->last_cluster, rpi->last_cluster);
- + SET32(disk_pool->num_clusters, rpi->num_clusters);
- + SET32(disk_pool->c_start_cluster, rpi->c_start_cluster);
- + SET32(disk_pool->c_last_cluster, rpi->c_last_cluster);
- +
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- + sanity_check_pool(sb);
- +#endif
- + rfs_mark_buffer_dirty(bh, sb);
- + brelse(bh);
- +
- + return 0;
- +}
- +
- +/**
- + * update size of dir entry for the pool file
- + * @param sb super block
- + * @param clusters number of clusters to expand or shrink
- + * @param resize new size of pool file
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_update_pool_entry(struct super_block *sb, unsigned int clusters, int resize)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + struct rfs_pool_info *pool_info = RFS_POOL_I(sb);
- + unsigned int size, total_size;
- + unsigned long offset;
- +
- + bh = rfs_bread(sb, pool_info->blocknr, BH_RFS_ROOT);
- + if (!bh) { /* I/O error */
- + DPRINTK("can't get buffer head related with pool head\n");
- + return -EIO;
- + }
- +
- + offset = pool_info->index << DENTRY_SIZE_BITS;
- + offset += RFS_SB(sb)->root_start_addr;
- + offset &= (sb->s_blocksize - 1);
- + ep = (struct rfs_dir_entry *) (bh->b_data + offset);
- +
- + if (RFS_POOL_I(sb)->start_cluster != START_CLUSTER(ep)) {
- + brelse(bh);
- + RFS_BUG("mismatch start_cluster in-core & disk(%d != %d)\n",
- + pool_info->start_cluster, START_CLUSTER(ep));
- + return -EIO;
- + }
- +
- + /* update size */
- + total_size = GET32(ep->size);
- + size = clusters << RFS_SB(sb)->cluster_bits;
- + if (resize == EXPAND_POOL_SIZE) {
- + /* append case */
- + total_size += size;
- + } else if (resize == SHRINK_POOL_SIZE) {
- + /* shrink case */
- + total_size -= size;
- + } else {
- + /* RFS-log : SET_POOL_SIZE */
- + total_size = size;
- + }
- +
- +
- + SET32(ep->size, total_size);
- + rfs_mark_buffer_dirty(bh, sb);
- + brelse(bh);
- +
- +
- +
- + return 0;
- +}
- +
- +/**
- + * shrink some clusters from the pool chain
- + * @param sb super block
- + * @param[out] clu_list cluster list allocated
- + * @param num_clusters number of clusters will be allocated
- + * @param[out] alloc_clusters number of clusters were allocated
- + * @param[out] next_clu next cluster number to connect with start cluster of pool file
- + * @return return 0 on success, errno on failure
- + *
- + * when it is invoked, fat chain will not be updated
- + */
- +int rfs_shrink_pool_chain(struct super_block *sb, unsigned int *clu_list, unsigned int num_clusters, unsigned int *alloc_clusters, unsigned int *next_clu)
- +{
- + unsigned int prev, next;
- + unsigned int count;
- + unsigned int pool_clusters;
- + int i, err;
- +
- + pool_clusters = RFS_POOL_I(sb)->num_clusters - POOL_RESERVED_CLUSTER;
- + *next_clu = CLU_TAIL;
- +
- + /* first cluster of pool is reserved for pool meta data */
- + if (IS_POOL_EMPTY(RFS_POOL_I(sb)->num_clusters)) {
- + /* run out of free clusters */
- + *alloc_clusters = 0;
- + return 0;
- + }
- +
- + if (num_clusters > pool_clusters)
- + count = pool_clusters;
- + else
- + count = num_clusters;
- +
- + prev = RFS_POOL_I(sb)->start_cluster;
- +
- + for (i = count; i >= 0; i--) {
- + err = fat_read(sb, prev, &next);
- + if (err) {
- + DPRINTK("can't read a fat entry (%u)\n", prev);
- + return -EIO;
- + }
- +
- + if (next < VALID_CLU) { /* out-of-range input */
- + RFS_BUG("fat entry(%u) was corrupted\n", next);
- + return -EIO;
- + }
- +
- + /* do not need to update clu_list */
- + if (i == 0)
- + break;
- +
- + *clu_list++ = next;
- + prev = next;
- + }
- +
- + *next_clu = next;
- + *alloc_clusters = count;
- +
- + return 0;
- +}
- +
- +/**
- + * update the fat chain of pool file
- + * @param sb super block
- + * @param next_clu next cluster number
- + * @param last_clu new last cluster number
- + * @param clusters number of clusters
- + * @param is_expand flag to expand or shrink
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_move_pool_chain(struct super_block *sb, unsigned int next_clu, unsigned int last_clu, unsigned int clusters, int is_expand)
- +{
- + struct rfs_pool_info *pool_info = RFS_POOL_I(sb);
- + int err;
- +
- + if (is_expand == EXPAND_POOL_SIZE) {
- + err = fat_write(sb, pool_info->last_cluster, next_clu);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->last_cluster);
- + return err;
- + }
- +
- + /* update hint info */
- + pool_info->num_clusters += clusters;
- + pool_info->last_cluster = last_clu;
- +
- + /* check whether candidate segments exist */
- + if (pool_info->c_start_cluster == NOT_ASSIGNED) /* not exist */
- + return 0;
- +
- + /* link start candidate segment with
- + end of free clusters in pool file */
- + err = fat_write(sb, pool_info->last_cluster,
- + pool_info->c_start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->last_cluster);
- + return err;
- + }
- + } else if (is_expand == SHRINK_POOL_SIZE) {
- + err = fat_write(sb, pool_info->start_cluster, next_clu);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->start_cluster);
- + return err;
- + }
- +
- + /* update hint info */
- + pool_info->num_clusters -= clusters;
- + if (IS_POOL_EMPTY(pool_info->num_clusters)) {
- + pool_info->last_cluster =
- + pool_info->start_cluster;
- + }
- + } else {
- + /* RFS-log : SET_POOL_SIZE is never passed in this function */
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * put clusters deallocated in the pool file
- + * @param sb super block
- + * @param start_clu start cluster number
- + * @param last_clu new last cluster number
- + * @param clusters number of clusters
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_put_pool(struct super_block *sb, unsigned int start_clu, unsigned int last_clu, unsigned int clusters)
- +{
- + int err;
- +
- + err = rfs_move_pool_chain(sb, start_clu, last_clu,
- + clusters, EXPAND_POOL_SIZE);
- + if (err)
- + return err;
- +
- + err = rfs_update_pool_entry(sb, clusters, EXPAND_POOL_SIZE);
- + if (err)
- + return err;
- +
- + err = rfs_update_pool_block(sb);
- + if (err) /* I/O error */
- + return err;
- +
- +
- +
- + return 0;
- +}
- +
- +/**
- + * get free clusters in the pool file
- + * @param sb super block
- + * @param next_clu next cluster number
- + * @param clusters number of clusters
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_get_pool(struct super_block *sb, unsigned int next_clu, unsigned int clusters)
- +{
- + int err;
- + unsigned int dummy = 0;
- +
- + err = rfs_move_pool_chain(sb, next_clu, dummy,
- + clusters, SHRINK_POOL_SIZE);
- + if (err)
- + return err;
- +
- + err = rfs_update_pool_entry(sb, clusters, SHRINK_POOL_SIZE);
- + if (err)
- + return err;
- +
- + err = rfs_update_pool_block(sb);
- + if (err) /* I/O error */
- + return err;
- +
- +
- +
- + return 0;
- +}
- +
- +/**
- + * update candidate segment info and fat chain of pool file
- + * @param sb super block
- + * @param segment candidate segment
- + * @param is_attach flag to attach or detach
- + * @return return 0 on success, errno on failure
- + */
- +static int update_candidate_segment(struct super_block *sb, struct c_segment *segment, int is_attach)
- +{
- + struct rfs_pool_info *pool_info = RFS_POOL_I(sb);
- + unsigned int start, last;
- + int err;
- +
- + start = pool_info->c_start_cluster;
- + last = pool_info->c_last_cluster;
- +
- + if (is_attach) { /* attach case */
- + if (start == NOT_ASSIGNED) {
- + err = fat_write(sb, pool_info->last_cluster,
- + segment->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->last_cluster);
- + return err;
- + }
- + pool_info->c_start_cluster = segment->start_cluster;
- + } else {
- + err = fat_write(sb, pool_info->c_last_cluster,
- + segment->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->c_last_cluster);
- + return err;
- + }
- + }
- + pool_info->c_last_cluster = segment->last_cluster;
- + } else { /* detach case */
- + struct c_segment *prev_seg, *next_seg;
- +
- + prev_seg = SEGMENT_ENTRY(segment->list.prev);
- + next_seg = SEGMENT_ENTRY(segment->list.next);
- +
- + if (start == segment->start_cluster &&
- + last == segment->last_cluster) { /* only one */
- + pool_info->c_start_cluster = NOT_ASSIGNED;
- + pool_info->c_last_cluster = NOT_ASSIGNED;
- + } else if (start == segment->start_cluster) { /* first seg */
- + pool_info->c_start_cluster = next_seg->start_cluster;
- + } else if (last == segment->last_cluster) { /* last seg */
- + err = fat_write(sb, pool_info->last_cluster,
- + segment->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->last_cluster);
- + return err;
- + }
- +
- + err = fat_write(sb, segment->last_cluster, start);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + segment->last_cluster);
- + return err;
- + }
- +
- + err = fat_write(sb, prev_seg->last_cluster, CLU_TAIL);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + prev_seg->last_cluster);
- + return err;
- + }
- + pool_info->c_last_cluster = prev_seg->last_cluster;
- + } else { /* middle seg */
- + err = fat_write(sb, pool_info->last_cluster,
- + segment->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + pool_info->last_cluster);
- + return err;
- + }
- +
- + err = fat_write(sb, segment->last_cluster, start);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + segment->last_cluster);
- + return err;
- + }
- +
- + err = fat_write(sb, prev_seg->last_cluster,
- + next_seg->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + prev_seg->last_cluster);
- + return err;
- + }
- + }
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * attach segment into candidate segment list on pool file
- + * @param inode inode
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_attach_candidate(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct c_segment *segment;
- + unsigned int move_chain[2];
- + unsigned int p_prev_clu;
- + int err;
- +
- + if (RFS_I(inode)->start_clu == CLU_TAIL) /* not assigned to inode */
- + return 0;
- +
- + fat_lock(sb);
- +
- + /* make a candidate segment */
- + segment = (struct c_segment *)
- + kmalloc(sizeof(struct c_segment), GFP_KERNEL);
- + if (!segment) { /* memory error */
- + DEBUG(DL1, "memory allocation failed\n");
- + err = -ENOMEM;
- + goto out;
- + }
- +
- + segment->start_cluster = RFS_I(inode)->start_clu;
- + segment->last_cluster = RFS_I(inode)->last_clu;
- +
- + /* RFS-log : move chain of file to pool's tail */
- + p_prev_clu = (RFS_POOL_I(sb)->c_last_cluster != CLU_TAIL) ?
- + RFS_POOL_I(sb)->c_last_cluster : RFS_POOL_I(sb)->last_cluster;
- +
- + move_chain[0] = segment->start_cluster;
- + move_chain[1] = segment->last_cluster;
- + if ((err = rfs_log_move_chain(sb, RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + p_prev_clu, CLU_TAIL,
- + CLU_TAIL, CLU_TAIL, 2, move_chain))) {
- + goto out;
- + }
- +
- + /* update candidate segment list on pool file */
- + err = update_candidate_segment(sb, segment, 1);
- + if (err)
- + goto out;
- +
- + list_add_tail(&segment->list, &RFS_POOL_I(sb)->c_list);
- +
- + /* update hint info of pool file */
- + err = rfs_update_pool_block(sb);
- +
- +out:
- + fat_unlock(sb);
- + return err;
- +}
- +
- +/**
- + * lookup a candidate segment corresponding with start cluster of inode in list
- + * @param inode inode
- + * @return return segment on success, NULL on failure
- + */
- +static struct c_segment *lookup_segment(struct inode *inode)
- +{
- + unsigned int start_clu = RFS_I(inode)->start_clu;
- + struct c_segment *segment;
- + struct list_head *p;
- +
- + list_for_each(p, &RFS_POOL_I(inode->i_sb)->c_list) {
- + segment = SEGMENT_ENTRY(p);
- + if (segment->start_cluster == start_clu)
- + return segment; /* found */
- + }
- +
- + return NULL;
- +}
- +
- +/**
- + * detach segment from candidate segment list
- + * @param inode inode
- + * @return return 0 on success, errno on failure
- + *
- + * And add it into deleted segment on pool file
- + */
- +int rfs_detach_candidate(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct c_segment *segment, *s;
- + unsigned int nr_clusters, dummy;
- + unsigned int t_prev_clu, t_next_clu;
- + unsigned int move_chain[2];
- + unsigned int err;
- +
- + if ((RFS_I(inode)->start_clu == CLU_TAIL) ||
- + (RFS_I(inode)->i_state != RFS_I_FREE))
- + return 0;
- +
- + fat_lock(sb);
- +
- + /* lookup candidate segment in list */
- + segment = lookup_segment(inode);
- + if (!segment || segment->last_cluster != RFS_I(inode)->last_clu) {
- + /* NOT found */
- + fat_unlock(sb);
- + RFS_BUG("segment does not exist for %d\n",
- + RFS_I(inode)->start_clu);
- + return -EIO;
- + }
- +
- + /* count clusters in segment and make chunk list for stl delete */
- + err = rfs_check_size(sb, segment->start_cluster, &dummy,
- + segment->last_cluster, &nr_clusters);
- + if (err)
- + goto out;
- +
- + /* RFS-log : get prev and next clu of candidate segment */
- + if (segment->start_cluster !=
- + RFS_POOL_I(inode->i_sb)->c_start_cluster) {
- + s = SEGMENT_ENTRY(segment->list.prev);
- + t_prev_clu = s->last_cluster;
- + } else
- + t_prev_clu = RFS_POOL_I(sb)->last_cluster;
- +
- + if (segment->last_cluster != RFS_POOL_I(inode->i_sb)->c_last_cluster) {
- + s = SEGMENT_ENTRY(segment->list.next);
- + t_next_clu = s->start_cluster;
- + } else
- + t_next_clu = CLU_TAIL;
- +
- + move_chain[0] = segment->start_cluster;
- + move_chain[1] = segment->last_cluster;
- +
- + /* RFS-log : move candidate segment to deleted pool */
- + /* don't care pdir (CLU_TAIL) and entry (-1) */
- + if ((err = rfs_log_move_chain(sb, CLU_TAIL, -1,
- + RFS_POOL_I(sb)->last_cluster,
- + RFS_POOL_I(sb)->c_start_cluster,
- + t_prev_clu, t_next_clu,
- + 2, move_chain))) {
- + goto out;
- + }
- +
- + /* update candidate segment list on pool file */
- + err = update_candidate_segment(sb, segment, 0);
- + if (err)
- + goto out;
- +
- + RFS_POOL_I(sb)->last_cluster = segment->last_cluster;
- + RFS_POOL_I(sb)->num_clusters += nr_clusters;
- +
- + list_del(&segment->list);
- + kfree(segment);
- +
- + /* update hint info & dir entry of pool file */
- + err = rfs_update_pool_entry(sb, nr_clusters, EXPAND_POOL_SIZE);
- + if (err)
- + goto out;
- +
- + err = rfs_update_pool_block(sb);
- + if (err) /* I/O error */
- + goto out;
- +
- + /* update used clusters in volume */
- + RFS_SB(sb)->num_used_clusters -= nr_clusters;
- +
- +
- +out:
- + fat_unlock(sb);
- + return err;
- +}
- +
- +/**
- + * insert cluster into candidate segment
- + * @param inode inode
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_insert_candidate(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct rfs_pool_info *pool_info = RFS_POOL_I(sb);
- + struct c_segment *segment, *next_seg;
- + int err;
- +
- + /* lookup candidate segment in list */
- + segment = lookup_segment(inode);
- + if (!segment) {
- + /* NOT found */
- + RFS_BUG("segment does not exist for %d\n",
- + RFS_I(inode)->start_clu);
- + return -EIO;
- + }
- +
- + /* update fat chain of pool file */
- + if (segment->last_cluster != pool_info->c_last_cluster) {
- + next_seg = SEGMENT_ENTRY(segment->list.next);
- + err = fat_write(sb, RFS_I(inode)->last_clu,
- + next_seg->start_cluster);
- + if (err) {
- + DPRINTK("can't write a fat entry (%u)\n",
- + RFS_I(inode)->last_clu);
- + return err;
- + }
- + }
- +
- + /* update segment info */
- + if (segment->last_cluster == pool_info->c_last_cluster) {
- + pool_info->c_last_cluster = RFS_I(inode)->last_clu;
- + err = rfs_update_pool_block(sb);
- + if (err)
- + return err;
- + }
- +
- + segment->last_cluster = RFS_I(inode)->last_clu;
- +
- + return 0;
- +}
- +
- +/**
- + * remove all candidate segments in pool file
- + * @param sb super block
- + * @return return 0 on success, errno on failure
- + *
- + * It is only invoked at mount time
- + */
- +int rfs_remove_candidates(struct super_block *sb)
- +{
- + struct rfs_pool_info *pool_info = RFS_POOL_I(sb);
- + unsigned int nr_clusters, dummy;
- + int err = 0;
- +
- + if (pool_info->c_start_cluster == NOT_ASSIGNED)
- + return 0;
- +
- + /* count clusters in candidate segments and make chunk for stl delete */
- + err = rfs_check_size(sb, pool_info->c_start_cluster, &dummy,
- + CLU_TAIL, &nr_clusters);
- + if (err)
- + return err;
- +
- + if (rfs_log_start(sb, RFS_LOG_DEL_INODE, NULL))
- + return -EIO;
- +
- + if ((err = rfs_log_update_pool(sb)))
- + goto out;
- +
- + /* update hint info & dir entry of pool file */
- + pool_info->last_cluster = pool_info->c_last_cluster;
- + pool_info->num_clusters += nr_clusters;
- + pool_info->c_start_cluster = NOT_ASSIGNED;
- + pool_info->c_last_cluster = NOT_ASSIGNED;
- +
- + err = rfs_update_pool_entry(sb, nr_clusters, EXPAND_POOL_SIZE);
- + if (err)
- + goto out;
- +
- + err = rfs_update_pool_block(sb);
- +out:
- +
- + if (rfs_log_end(sb, err))
- + return err;
- +
- + return 0;
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/code_convert.c linux-2.6.17.14.rfs/fs/rfs/code_convert.c
- --- linux-2.6.17.14.orig/fs/rfs/code_convert.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/code_convert.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,460 @@
- +/**
- + * @file fs/rfs/code_convert.c
- + *
- + * @brief Dos name and Unicode name handling operations
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/types.h>
- +#include <linux/string.h>
- +#include <linux/rfs_fs.h>
- +#include <linux/ctype.h>
- +#include <linux/nls.h>
- +
- +#include "rfs.h"
- +
- +/**
- + * check the invalid character in the short entry
- + * this character will be changed to underscore when configured RFS_VFAT
- + * otherwise, making a entry with this character fails
- + * @param c dosname character to check
- + * @return zero on valid character
- + */
- +static inline int check_invalid_short(u8 c)
- +{
- + if ((c <= 0x20) || (c == '+') || (c == ',') || (c == ';') ||
- + (c == '=') || (c == '[') || (c == ']'))
- + return 1;
- + else
- + return 0;
- +}
- +
- +/**
- + * check the invalid character for FAT file name
- + * @param c unicode character to check
- + * @return zero on valid character
- + */
- +static inline int check_invalid(u16 c)
- +{
- +#ifndef CONFIG_RFS_VFAT
- + if ((c == '\\') || (c == '/') || (c == ':') || (c == '*') || (c == '?')
- + || (c == '\"') || (c == '<') || (c == '>') || (c == '|')
- + || (c <= 0x20) || (c == '+') || (c == ',') || (c == ';')
- + || (c == '=') || (c == '[') || (c == ']'))
- +#else
- + if ((c == '\\') || (c == '/') || (c == ':') || (c == '*') || (c == '?')
- + || (c == '\"') || (c == '<') || (c == '>') || (c == '|'))
- +#endif
- + return 1;
- + else
- + return 0;
- +}
- +
- +/**
- + * Function converting cstring to dosname
- + * @param dosname resulting dosname
- + * @param cstring cstring to be converted
- + * @param status flag indicating the capital combination and whether resulting dosname fits the 8.3 dosname or not
- + * @param check flag indicating whether check invalid characters or not
- + * @return zero on sucess, negative error code on failure.
- + */
- +int convert_cstring_to_dosname(u8 *dosname, const char* cstring, unsigned int *status, unsigned int check)
- +{
- + const char *end_of_name, *last_period;
- + int i, len, lossy = FALSE;
- + char mixed = 0;
- +
- + /* strip all leading spaces and periods */
- + while ((*cstring == SPACE) || (*cstring == PERIOD)) {
- + lossy = TRUE;
- + cstring++;
- + }
- +
- + len = strlen(cstring);
- + if (!len)
- + return -EINVAL;
- +
- + end_of_name = cstring + len - 1;
- +
- + /* check the trailing period & space */
- + if (!check && (*end_of_name == PERIOD || *end_of_name == SPACE))
- + return -EINVAL;
- +
- + /* search for the last embedded period */
- + last_period = strrchr(cstring, PERIOD);
- + if (last_period == NULL)
- + last_period = end_of_name + 1;
- +
- + memset(dosname, SPACE, DOS_NAME_LENGTH);
- +
- + i = 0;
- + for (;(i < DOS_NAME_LENGTH) && (cstring <= end_of_name); cstring++) {
- + if (check && check_invalid((u16)(*cstring & 0x00ff))) {
- + DEBUG(DL3, "Invalid character");
- + return -EINVAL;
- + }
- +
- + if (*cstring == SPACE) {
- + lossy = TRUE;
- + continue;
- + }
- +
- + if (*cstring == PERIOD) {
- + if (cstring < last_period)
- + lossy = TRUE;
- + else
- + i = SHORT_NAME_LENGTH;
- + continue;
- + }
- +
- +#if !defined(CONFIG_RFS_NLS) && defined(CONFIG_RFS_VFAT)
- + /* not support non-ASCII */
- + if (check && (*cstring & 0x80)) {
- + DEBUG(DL3, "NLS not support");
- + return -EINVAL;
- + }
- +#endif
- +
- + /* fill dosname */
- + if (check_invalid_short(*cstring)) {
- + dosname[i++] = UNDERSCORE;
- + lossy = TRUE;
- + } else if (isascii(*cstring) && islower(*cstring)) {
- + if (i < SHORT_NAME_LENGTH)
- + mixed |= PRIMARY_LOWER;
- + else
- + mixed |= EXTENSION_LOWER;
- + dosname[i++] = toupper(*cstring);
- + } else if (isascii(*cstring) && isupper(*cstring)) {
- + if (i < SHORT_NAME_LENGTH)
- + mixed |= PRIMARY_UPPER;
- + else
- + mixed |= EXTENSION_UPPER;
- + dosname[i++] = *cstring;
- + } else {
- + dosname[i++] = *cstring;
- + }
- +
- + if ((i == SHORT_NAME_LENGTH) && ((cstring + 1) < last_period)) {
- + lossy = TRUE;
- +#ifdef CONFIG_RFS_VFAT
- + if (check) {
- + while(++cstring < last_period) {
- + if (check_invalid((u16)(*cstring & 0x0ff)))
- + return -EINVAL;
- + }
- + }
- +#endif
- + cstring = last_period;
- + }
- + } /* end of loop */
- +
- + if (cstring <= end_of_name) {
- + lossy = TRUE;
- +#ifdef CONFIG_RFS_VFAT
- + if (check) {
- + while(cstring <= end_of_name) {
- + if (check_invalid((u16)(*cstring++ & 0x0ff)))
- + return -EINVAL;
- + }
- + }
- +#endif
- + }
- +
- + /* post check */
- + if (dosname[0] == KANJI_LEAD)
- + dosname[0] = REPLACE_KANJI;
- +
- + if (status != NULL) {
- + *status = 0;
- +
- + if ((primary_masked(mixed) == (PRIMARY_UPPER | PRIMARY_LOWER))
- + || (extension_masked(mixed) ==
- + (EXTENSION_UPPER | EXTENSION_LOWER))) {
- + put_mix(*status, UPPER_N_LOWER);
- + } else {
- + if (primary_masked(mixed) == PRIMARY_LOWER)
- + put_mix(*status, PRIMARY_LOWER);
- + if (extension_masked(mixed) == EXTENSION_LOWER)
- + put_mix(*status, EXTENSION_LOWER);
- + }
- +
- + put_lossy(*status, lossy);
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * Function converting dos name to cstring
- + * @param cstring dosname to be converted
- + * @param dosname resulting cstring
- + * @param sysid flag indicating the capital combination
- + * @return none
- + */
- +void convert_dosname_to_cstring(char *cstring, const u8 *dosname, unsigned char sysid)
- +{
- + int i = 0;
- +
- + if (dosname[0] == REPLACE_KANJI) {
- + *cstring++ = (s8) KANJI_LEAD;
- + i = 1;
- + }
- +
- + for ( ; i < SHORT_NAME_LENGTH; i++) {
- + if (dosname[i] == SPACE)
- + break;
- +
- + if (sysid & PRIMARY_LOWER && isascii(dosname[i]))
- + *cstring++ = (s8) tolower(dosname[i]);
- + else
- + *cstring++ = (s8) dosname[i];
- + }
- +
- + /* no extension */
- + if (dosname[SHORT_NAME_LENGTH] == SPACE) {
- + *cstring = '\0';
- + return;
- + }
- +
- + *cstring++ = PERIOD;
- +
- + for (i = SHORT_NAME_LENGTH; i < DOS_NAME_LENGTH; i++) {
- + if (dosname[i] == SPACE)
- + break;
- +
- + if (sysid & EXTENSION_LOWER && isascii(dosname[i]))
- + *cstring++ = (s8) tolower(dosname[i]);
- + else
- + *cstring++ = (s8) dosname[i];
- + }
- +
- + *cstring = '\0';
- + return;
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +
- +#ifdef CONFIG_RFS_NLS
- +/**
- + * Function to convert encoded character to unicode by NLS table
- + * @param nls NLS table
- + * @param chars encoded characters
- + * @param chars_len the length of character buffer
- + * @param uni the unicode character converted
- + * @param lower flag indicating the case type of unicode character
- + * @return the length of character converted
- + */
- +static int char2uni(struct nls_table *nls, unsigned char *chars, int chars_len, wchar_t *uni)
- +{
- + int clen = 0;
- + wchar_t uni_tmp;
- +
- + clen = nls->char2uni(chars, chars_len, &uni_tmp);
- + if (clen < 0) {
- + *uni = UNDERSCORE;
- + clen = 1;
- + } else if (clen <= 1) {
- + *uni = uni_tmp;
- + clen = 1;
- + } else {
- + *uni = uni_tmp;
- + }
- +
- + return clen;
- +}
- +#endif /* CONFIG_RFS_NLS */
- +
- +/**
- + * Function converting cstring to unicode name
- + * @param uname unicode name to be converted
- + * @param cstring resulting cstring
- + * @param nls Linux NLS object pointer for future enchancement
- + * @param check flag indicating wether check invalid characters or not
- + * @return the length of uname on success, negative error code on failure
- + */
- +static int convert_cstring_to_uname(u16 *uname, const char *cstring, struct nls_table *nls, unsigned int check)
- +{
- +#ifndef CONFIG_RFS_NLS /* if not support nls */
- + int i = 0;
- +
- + while (cstring[i] != 0x00) {
- + if (cstring[i] & 0x80) {
- + /* cannot convert to unicode without codepage */
- + DEBUG(DL3, "NLS not support");
- + return -EINVAL;
- + }
- +
- + uname[i] = (u16) (cstring[i] & 0x00ff);
- + i++;
- + }
- +
- + /* add the null */
- + uname[i] = 0x0000;
- + return i;
- +#else
- + /* support nls codepage to convert character */
- + int clen = 0, uni_len = 0, len;
- + int i = 0;
- +
- + len = strlen(cstring);
- +
- + for (i = 0; (cstring[i] != '\0') && (uni_len < MAX_NAME_LENGTH); uni_len++) {
- +
- + clen = char2uni(nls, (unsigned char *)&cstring[i],
- + len - i, &uname[uni_len]);
- + i += clen;
- +
- + if (uname[uni_len] == 0x00)
- + break;
- +
- + if (check && check_invalid(uname[uni_len])) {
- + DEBUG(DL3, "Invalid character");
- + return -EINVAL;
- + }
- + }
- +
- + /* the length of unicode name is never over the limitation */
- + {
- + uname[uni_len] = 0x0000;
- +
- + /* return the length of name */
- + return uni_len;
- + }
- +#endif /* CONFIG_RFS_NLS */
- +}
- +
- +/**
- + * Function converting unicode name to cstring
- + * @param cstring resulting converted
- + * @param uname unicode name to be converted
- + * @param nls Linux NLS object pointer for future use
- + * @return zero on success, negative error code on failure
- + */
- +int convert_uname_to_cstring(char *cstring, const u16 *uname, struct nls_table *nls)
- +{
- +#ifndef CONFIG_RFS_NLS /* if not support nls */
- + int i = 0;
- + for (; i < MAX_NAME_LENGTH; i++) {
- + /* cannot convert */
- + if (uname[i] & 0xff80)
- + cstring[i] = UNDERSCORE;
- + else
- + cstring[i] = (s8) uname[i];
- +
- + if (cstring[i] == '\0')
- + break;
- + }
- +
- + /* return the length of unicode name */
- + return i;
- +#else
- + /* need nls codepage to convert character */
- + int clen = 0, stridx = 0, i = 0;
- + const u16 *uname_ptr = uname;
- + char buf[NLS_MAX_CHARSET_SIZE];
- +
- + for (;(i < MAX_NAME_LENGTH) && (stridx <= NAME_MAX); i++, uname_ptr++) {
- + /* end of uname is NULL */
- + if (*uname_ptr == 0x0000)
- + break;
- +
- + clen = nls->uni2char(*uname_ptr, buf, sizeof(buf));
- + if (clen <= 0) {
- + cstring[stridx] = UNDERSCORE;
- + clen = 1;
- + } else if (clen == 1) {
- + cstring[stridx] = buf[0];
- + } else {
- + if (stridx + clen > NAME_MAX)
- + break;
- + memcpy(&cstring[stridx], buf, clen);
- + }
- +
- + stridx += clen;
- + }
- +
- + cstring[stridx] = '\0';
- +
- + /* return the length of unicode name */
- + return i;
- +#endif /* CONFIG_RFS_NLS */
- +
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/**
- + * translate encoded string from user to 8.3 dosname and unicode
- + *
- + * @param cstring encoded string from user IO
- + * @param dosname resulting dosname of encoded string
- + * @param unicode resulting unicode name
- + * @param status flag indicating the capital combination and whether resulting dosname fits the 8.3 dosname or not
- + * @param nls NLS codepage table
- + * @param check flag whether check invalid characters or not
- + * @return zero or length of uname on success, or errno on failure
- + */
- +int create_fatname(const char *cstring, u8 *dosname, u16 *unicode, unsigned int *status, struct nls_table *nls, unsigned int check)
- +{
- + /*
- + * support NLS or not
- + */
- + int ret = 0;
- +
- +#ifdef CONFIG_RFS_NLS
- + if (unlikely(!nls)) /* out-of-range input */
- + return -EINVAL;
- +#else
- + nls = NULL;
- +#endif /* CONFIG_RFS_NLS */
- +
- + if (dosname) {
- + /* check trailing space, period */
- + ret = convert_cstring_to_dosname(dosname, cstring, status, check);
- + if (ret < 0)
- + return ret;
- + }
- +
- +#ifdef CONFIG_RFS_VFAT
- + if (unicode && status) {
- + /*
- + * don't check the length of unicode name
- + * because it's checked at rfs_lookup
- + */
- +
- + /* make unicode only if condition is satisfied */
- + if (get_lossy(*status) || get_mix(*status) == UPPER_N_LOWER) {
- + /* don't check the length of unicode */
- + ret = convert_cstring_to_uname(unicode, cstring, nls, FALSE);
- + if (ret < 0)
- + return ret;
- + } else {
- + unicode[0] = 0x00;
- + return 0;
- + }
- + }
- +#endif /* CONFIG_RFS_VFAT */
- +
- + return ret;
- +}
- +
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Config.in linux-2.6.17.14.rfs/fs/rfs/Config.in
- --- linux-2.6.17.14.orig/fs/rfs/Config.in 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Config.in 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,37 @@
- +#
- +# RFS FAT configuration
- +#
- +
- +tristate 'RFS filesystem support' CONFIG_RFS_FS
- +
- +if [ "$CONFIG_RFS_FS" = "y" -o "$CONFIG_RFS_FS" = "m" ]; then
- + dep_mbool ' FAT32 & long file name support' CONFIG_RFS_VFAT
- + dep_mbool ' Sync on close support' CONFIG_RFS_SYNC_ON_CLOSE
- + dep_mbool ' Support NLS on RFS filesystem' CONFIG_RFS_NLS $CONFIG_RFS_VFAT
- + if [ "$CONFIG_RFS_NLS" = "y" ]; then
- + string ' Use default NLS Codepage' CONFIG_RFS_DEFAULT_CODEPAGE "cp949"
- + fi
- + dep_mbool ' Debugging' CONFIG_RFS_FAT_DEBUG
- + if [ "$CONFIG_RFS_FAT_DEBUG" = "y" ]; then
- + int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_RFS_FAT_DEBUG_VERBOSE 0
- + fi
- +fi
- +
- +if [ "$CONFIG_RFS_XSR_STL" = "y" ]; then
- + define_bool CONFIG_RFS_MAPDESTROY y
- +fi
- +
- +if [ "$CONFIG_RFS_FS" = "m" -a "$CONFIG_RFS_XSR_STL" = "m" ]; then
- + define_bool CONFIG_RFS_MAPDESTROY y
- +fi
- +
- +define_bool CONFIG_RFS_IGET4 y
- +define_string CONFIG_RFS_VERSION "1.2.2p1-rc2"
- +
- +# When file is extended, it is the max number of clusters
- +# which one transaction can use.
- +# If the transaction rollbacks, the amount of
- +# (CONFIG_RFS_PRE_ALLOC * cluster size) data would also rollback.
- +define_int CONFIG_RFS_PRE_ALLOC 50
- +
- +define_int CONFIG_RFS_LOG_WAKEUP_DELAY 5
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/dir.c linux-2.6.17.14.rfs/fs/rfs/dir.c
- --- linux-2.6.17.14.orig/fs/rfs/dir.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/dir.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,268 @@
- +/**
- + * @file fs/rfs/dir.c
- + * @brief directory handling functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +#define loff_t off_t
- +#endif
- +
- +/* internal data structure for readdir */
- +struct rfs_dir_info {
- + unsigned int type;
- + unsigned long ino;
- + char name[NAME_MAX + 1];
- +};
- +
- +/**
- + * initialize the dot(.) and dotdot(..) dir entries for new directory
- + * @param inode inode for itself
- + * @return return 0 on success, EIO on failure
- + *
- + * inode must have start cluster of itself and start cluster of parent dir
- + */
- +int init_new_dir(struct inode *inode)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *dot_ep = NULL;
- + struct rfs_dir_entry *dotdot_ep = NULL;
- + unsigned char dummy = 0;
- + int err = 0;
- +
- + /* initialize .(itself) and ..(parent) */
- + dot_ep = get_entry(inode, 0, &bh);
- + if (IS_ERR(dot_ep)) {
- + brelse(bh);
- + return -EIO;
- + }
- +
- + dotdot_ep = (struct rfs_dir_entry *) (bh->b_data + DENTRY_SIZE);
- +
- + init_dir_entry(inode, dot_ep, TYPE_DIR,
- + RFS_I(inode)->start_clu, DOT, &dummy);
- + init_dir_entry(inode, dotdot_ep, TYPE_DIR,
- + RFS_I(inode)->p_start_clu, DOTDOT, &dummy);
- +
- + rfs_mark_buffer_dirty(bh, inode->i_sb);
- + brelse(bh);
- +
- + return err;
- +}
- +
- +/**
- + * check whether directory is emtpy
- + * @param dir inode corresponding to the directory
- + * @return return 0 on success, errno on failure
- + *
- + * is_dir_empty is usually invoked before removing or renaming directry.
- + */
- +int is_dir_empty(struct inode *dir) {
- +
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- + unsigned int cpos = 0;
- + int err = 0, count = 0;
- + unsigned int type;
- +
- + if (dir->i_ino == ROOT_INO)
- + return -ENOTEMPTY;
- +
- + while (1) {
- + ep = get_entry(dir, cpos++, &bh);
- + if (IS_ERR(ep)) {
- + err = PTR_ERR(ep);
- + if (err == -EFAULT)
- + err = 0;
- + goto out;
- + }
- +
- + type = entry_type(ep);
- + if ((type == TYPE_FILE) || (type == TYPE_DIR)) {
- + /* check entry index bigger than
- + entry index of parent directory (..) */
- + if (++count > 2) {
- + err = -ENOTEMPTY;
- + goto out;
- + }
- + } else if (type == TYPE_UNUSED) {
- + /* do not need checking anymore */
- + goto out;
- + }
- + }
- +
- +out :
- + brelse(bh);
- + return err;
- +}
- +
- +/**
- + * counts the number of sub-directories in specified directory
- + * @param sb super block
- + * @param clu start cluster number of specified directory to count
- + * @return return the number of sub-directories on sucess, errno on failure
- + */
- +int count_subdir(struct super_block *sb, unsigned int clu)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned int cpos = 0;
- + unsigned int type;
- + int count = 0, err = 0;
- +
- + while (1) {
- + ep = get_entry_with_cluster(sb, clu, cpos++, &bh);
- + if (IS_ERR(ep)) {
- + err = PTR_ERR(ep);
- + if (err == -EFAULT) /* end of cluster */
- + break;
- + brelse(bh);
- + return err;
- + }
- +
- + /* check type of dir entry */
- + type = entry_type(ep);
- + if (type == TYPE_DIR)
- + count++;
- + else if (type == TYPE_UNUSED)
- + break;
- + }
- +
- + brelse(bh);
- + return count;
- +}
- +
- +/**
- + * read dir entry in specified directory
- + * @param inode specified directory inode
- + * @param bh buffer head to read dir entries
- + * @param ppos entry position to read
- + * @param[out] dir_info to save dir entry info
- + * @return return 0 on success, errno on failure
- + */
- +static int internal_readdir(struct inode *inode, struct buffer_head **bh, loff_t *ppos, struct rfs_dir_info *dir_info)
- +{
- +#ifdef CONFIG_RFS_VFAT
- + unsigned short uname[UNICODE_NAME_LENGTH];
- +#endif
- + struct rfs_dir_entry *ep = NULL;
- + loff_t index = *ppos;
- + unsigned long ino;
- + unsigned int type;
- + int err;
- +
- + while (1) {
- + ep = get_entry(inode, (u32) index, bh);
- + if (IS_ERR(ep))
- + return PTR_ERR(ep);
- +
- + index++;
- +
- + type = entry_type(ep);
- +
- + dir_info->type = type;
- +
- + if (type == TYPE_UNUSED)
- + return -INTERNAL_EOF; /* not error case */
- +
- + if ((type == TYPE_DELETED) || (type == TYPE_EXTEND) ||
- + (type == TYPE_VOLUME))
- + continue;
- +
- +#ifdef CONFIG_RFS_VFAT
- + uname[0] = 0x0;
- + get_uname_from_entry(inode, index - 1, uname);
- + if (uname[0] == 0x0 || !IS_VFAT(RFS_SB(inode->i_sb)))
- + convert_dosname_to_cstring(dir_info->name, ep->name, ep->sysid);
- + else
- + convert_uname_to_cstring(dir_info->name, uname, RFS_SB(inode->i_sb)->nls_disk);
- +#else
- + convert_dosname_to_cstring(dir_info->name, ep->name, ep->sysid);
- +#endif
- +
- + err = rfs_iunique(inode, index - 1, &ino);
- + if (err)
- + return err;
- + dir_info->ino = ino;
- +
- + *ppos = index;
- +
- + return 0;
- +
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * read all dir entries in specified directory
- + * @param filp file pointer of specified directory to read
- + * @param dirent buffer pointer
- + * @param filldir function pointer which fills dir info
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
- +{
- + struct dentry *dentry = filp->f_dentry;
- + struct inode *inode = dentry->d_inode;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_info dir_info;
- + unsigned int type;
- + loff_t pos;
- + int ret;
- +
- + CHECK_RFS_INODE(inode, -ENOENT);
- +
- + while (1) {
- + pos = filp->f_pos;
- +
- + ret = internal_readdir(inode, &bh, (loff_t *) &filp->f_pos, &dir_info);
- + if (ret < 0)
- + break;
- +
- + if (dir_info.type == TYPE_DIR)
- + type = DT_DIR;
- + else
- + type = DT_REG;
- +
- + ret = filldir(dirent, dir_info.name, strlen(dir_info.name),
- + pos, dir_info.ino, type);
- + if (ret < 0) {
- + filp->f_pos = pos; /* rollback */
- + break;
- + }
- + }
- +
- + brelse(bh);
- + return 0;
- +}
- +
- +struct file_operations rfs_dir_operations = {
- + .read = generic_read_dir,
- + .readdir = rfs_readdir,
- +};
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/dos.c linux-2.6.17.14.rfs/fs/rfs/dos.c
- --- linux-2.6.17.14.orig/fs/rfs/dos.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/dos.c 2008-06-19 04:02:08.000000000 +0200
- @@ -0,0 +1,1148 @@
- +/**
- + * @file fs/rfs/dos.c
- + * @brief FAT directory entry Manipultion and mangement operations
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/string.h>
- +#include <linux/sched.h>
- +#include <linux/time.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +/* for dir entries manipulation */
- +
- +/* local variable definition */
- +static const unsigned int days[] = {
- + 0, 31, 59, 90, 120, 151, 181, 212,
- + 243, 273, 304, 334, 0, 0, 0, 0
- +};
- +extern struct timezone sys_tz;
- +
- +static const u8 *reserved_names[] = {
- + "AUX ", "CON ", "NUL ", "PRN ",
- + "COM1 ", "COM2 ", "COM3 ", "COM4 ",
- + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ",
- + NULL };
- +
- +#define MAX_DIGIT 4 /* maximum tail: 1024 */
- +#define MAX_NUMERIC 1024
- +
- +#define SEC_PER_MIN (60) /* 60 secs / min */
- +#define MIN_PER_HR (60) /* 60 mins / hour */
- +#define SEC_PER_HR (3600) /* 3600 secs / hour */
- +#define HR_PER_DAY (24) /* 24 hours / day */
- +#define DAY_PER_YR (365) /* 365 days / year */
- +#define SEC_PER_DAY (60 * 60 * 24) /* 86400 secs / day */
- +#define DAY_PER_10YR (365 * 10 + 2) /* 3650 days / 10years */
- +#define SEC_PER_10YR DAY_PER_10YR * SEC_PER_DAY /* 10 years -> 315532800 secs */
- +#define MIN_DATE SEC_PER_10YR
- +
- +#define leap_days(yr) ((yr + 3) / 4) /* leap-year days during yr years */
- +#define leap_year(yr) ((yr & 3) == 0) /* true if yr is the leap year */
- +
- +/* time: [0-4] 2-second counts [5-10] minutes [11-15] hours */
- +#define FAT_time(sec, min, hour) ((hour << 11) + (min << 5) + (sec / 2))
- +/* date: [0-4] day [5-8] month [9-15] years from 1980*/
- +#define FAT_date(day, month, year) ((year << 9) + (month << 5) + day)
- +
- +/**
- + * Function converting linux time to FAT time
- + * @param linux_date linux time to be converted
- + * @param time return value to contain a time of converted FAT time
- + * @param date return value to contain a date of converted FAT time
- + * @return none
- + *
- + * Linux time starts from 1970.1.1 and FAT time starts from 1980.1.1
- + */
- +static void convert_date_linux2dos(time_t linux_date, unsigned short *time, unsigned short *date)
- +{
- + unsigned int day = 0, year = 0, month = 0;
- +
- + /* set to GMT time */
- + linux_date -= sys_tz.tz_minuteswest * SEC_PER_MIN;
- +
- + /* set the minimum value of date to 1980.1.1 */
- + if (linux_date < MIN_DATE)
- + linux_date = MIN_DATE;
- +
- + /* set the FAT time */
- + *time = FAT_time(linux_date % SEC_PER_MIN,
- + (linux_date / SEC_PER_MIN) % MIN_PER_HR,
- + (linux_date / SEC_PER_HR) % HR_PER_DAY);
- +
- + /* get the days & years */
- + day = (linux_date - MIN_DATE) / SEC_PER_DAY;
- + year = day / DAY_PER_YR;
- +
- + /* re-organize the year & day by the leap-years */
- + if ((leap_days(year) + (year * DAY_PER_YR)) > day)
- + year--;
- + day -= (leap_days(year) + (year * DAY_PER_YR));
- +
- + /* find the month & day */
- + if (day == days[2] && leap_year(year)) {
- + month = 2;
- + } else {
- + if (leap_year(year) && (day > days[2]))
- + day--;
- +
- + for (month = 0; month < 12; month++)
- + if (days[month] > day)
- + break;
- + }
- +
- + /* set the FAT date */
- + *date = FAT_date(day - days[month - 1] + 1, month, year);
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +/**
- + * Function setting a given buffer to zero
- + * @param bitmap character buffer to be filled with zero
- + * @param mapsize size of buffer
- + * @return none
- + */
- +static inline void bitmap_clear_all(unsigned char *bitmap, unsigned int mapsize)
- +{
- + memset(bitmap, 0x00, mapsize);
- +}
- +
- +/**
- + * Function check if certain bit of buffer is set to 1
- + * @param bitmap character buffer to be tested
- + * @param i offset of target bit
- + * @return return 1 if a bit is 1, if not, 0
- + */
- +static inline int bitmap_test(unsigned char *bitmap, int i)
- +{
- + unsigned char row;
- +
- + row = bitmap[i >> 3];
- + if ((row >> (i & 0x7)) & 0x1)
- + return 1;
- + else
- + return 0;
- +}
- +
- +/**
- + * Function set certain bit of buffer to 1
- + * @param bitmap character buffer to be modified
- + * @param i offset of target bit
- + * @return none
- + */
- +static inline void bitmap_set(unsigned char *bitmap, int i)
- +{
- + bitmap[i >> 3] |= (0x01 << (i & 0x7));
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/**
- + * Function checking if given dos file name is one of the reserved names
- + * @param dosname dos name to be tested
- + * @return return zero if dosname is valid, if not, a negative error code
- + *
- + * This function does validity check of give dos file name not to make dosname
- + * same with reserved name
- + */
- +static int check_reserved(u8 *dosname)
- +{
- + const u8 **reserved;
- +
- + for (reserved = reserved_names; *reserved != NULL; reserved++) {
- + if (!strncmp((char *) dosname, (char *)(*reserved), SHORT_NAME_LENGTH))
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * Function checking if given name is valid
- + * @param dosname name to be tested
- + * @param lossy indicating wether the original string has lossy or not
- + * @param isvfat indicating VFAT if not zero
- + * @return return zero when name is valid if not a negative error code
- + *
- + * This function does the validity check of given file name. This function is
- + * invoked prior to rfs_lookup
- + */
- +static inline int check_name(u8 *dosname, unsigned int lossy, int isvfat)
- +{
- + if (!isvfat || lossy != TRUE)
- + return check_reserved(dosname);
- + else
- + return 0;
- +}
- +
- +/**
- + * Function replacing the type of directory entry with give type
- + * @param ep directory entry to be modified
- + * @param type entry type
- + * @return return zero on success, a negative value on failure
- + */
- +static int set_entry_type(struct rfs_dir_entry *ep, unsigned char type)
- +{
- + if (type == TYPE_UNUSED) {
- + ep->name[0] = 0x0;
- + } else if (type == TYPE_DELETED) {
- + ep->name[0] = (u8) DELETE_MARK;
- + } else if (type == TYPE_EXTEND) {
- + ep->attr = (u8) ATTR_EXTEND;
- + } else if (type == TYPE_DIR) {
- + ep->attr = (u8) ATTR_SUBDIR;
- + } else if (type == TYPE_FILE) {
- + ep->attr = (u8) ATTR_ARCHIVE;
- + } else if (type == TYPE_SYMLINK) {
- + ep->attr = (u8) ATTR_ARCHIVE;
- + ep->cmsec = SYMLINK_MARK;
- + } else { /* out-of-range input */
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +/**
- + * Function retrieving unicode name from extend directory entry
- + * @param extp extend entry from which unicode is extracted
- + * @param uname pointer to array to store a unicode name
- + * @param is_last condition flag indicating if current extend entry
- + * is the last one.
- + * @return return zero on success, a negative value on failure
- + *
- + * This function is invokded to extract unicode names scatterd in several
- + * directory entries and put them together so that they can be commplete
- + * unicode name.
- + */
- +static int get_uname_from_ext_entry(struct rfs_ext_entry *extp, u16 *uname, unsigned int is_last)
- +{
- + unsigned int i;
- +
- + for (i = 0; i < EXT_UNAME_LENGTH; i++) {
- + if (i < 5) {
- + uname[i] = GET16(extp->uni_0_4[i]);
- + } else if (i < 11) {
- + uname[i] = GET16(extp->uni_5_10[i - 5]);
- + } else {
- + uname[i] = GET16(extp->uni_11_12[i - 11]);
- + }
- + if (uname[i] == 0x0)
- + return 0;
- + }
- +
- + if (is_last)
- + uname[i] = 0x0;
- +
- + return 0;
- +}
- +
- +/**
- + * Function searching numeric tail in an effort to avoid redundancy
- + * of filename.
- + * @param dir inode corresponding to given dos name
- + * @param dos_name dos_name to which numeric tail is to be appended
- + * @return return zero on success, a negative value on failure
- + *
- + * This function check if any file having same dos name with given one exists
- + * in the directory and calcuate a numeric tail.
- + */
- +static int get_numeric_tail(struct inode *dir, u8 *dos_name)
- +{
- + int has_tilde;
- + unsigned char bmap[MAX_NUMERIC >> 3];
- + unsigned int type;
- + unsigned int i, j, cpos = 0;
- + unsigned int count = 0;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + int err = 0;
- +
- + bitmap_clear_all(bmap, (MAX_NUMERIC >> 3));
- + bitmap_set(bmap, 0);
- +
- + while (1) {
- + ep = get_entry(dir, cpos++, &bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT) { /* end-of-directory */
- + break;
- + }
- + err = PTR_ERR(ep);
- + goto out;
- + }
- +
- + type = entry_type(ep);
- + if (type == TYPE_FILE || type == TYPE_DIR) {
- + for (i = 0; i < SHORT_NAME_LENGTH; i++) {
- + if (ep->name[i] == TILDE)
- + break;
- + }
- + if (strncmp(ep->name, dos_name, i))
- + continue;
- +
- + has_tilde = FALSE;
- + for (i = 0, count = 0; i < SHORT_NAME_LENGTH; i++) {
- + if (ep->name[i] == TILDE) {
- + has_tilde = TRUE;
- + } else if (has_tilde) {
- + if (ep->name[i] >= '0' && ep->name[i] <='9') {
- + count = count * 10 + (ep->name[i] - '0');
- + }
- + }
- + }
- + } else if (type == TYPE_UNUSED) {
- + break; /* end of valid entry */
- + }
- +
- + if (count)
- + bitmap_set(bmap, count);
- + }
- +
- + for (count = 0, i = 0; (!count) && (i < (MAX_NUMERIC >> 3)); i++) {
- + if (bmap[i] != (u8) 0xff) {
- + for (j = 0; j < SHORT_NAME_LENGTH; j++) {
- + if (bitmap_test(&bmap[i], j) == 0) {
- + count = (i << 3) + j;
- + break;
- + }
- + }
- + }
- + }
- +
- + if (count == 0 || count >= MAX_NUMERIC) {
- + err = -ENOENT; /* out-of-range input (numeric tail) */
- + goto out;
- + } else {
- + err = count;
- + }
- +
- +out:
- + if (!IS_ERR(bh))
- + brelse(bh);
- +
- + return err;
- +}
- +
- +/**
- + * Function appending a given numerical tail to given dos name
- + * @param dos_name directory entry to be modified
- + * @param count numerical tail to be appended
- + * @return none
- + *
- + * This function does not return error
- + */
- +static void append_tail(u8 *dos_name, unsigned int count)
- +{
- + char str_tail[SHORT_NAME_LENGTH - 1];
- + int length, n_tail = 0;
- +
- + sprintf(str_tail, "%c%d", TILDE, count);
- + length = strlen(str_tail);
- +
- + while (n_tail < (SHORT_NAME_LENGTH - length)) {
- + if (dos_name[n_tail] == SPACE)
- + break;
- + if (dos_name[n_tail] & 0x80) {
- + if (n_tail + 2 < (SHORT_NAME_LENGTH - length))
- + n_tail += 2;
- + else
- + break;
- + } else {
- + n_tail++;
- + }
- + }
- +
- + memcpy(&(dos_name[n_tail]), str_tail, length);
- +
- + memset(&(dos_name[n_tail + length]), SPACE,
- + SHORT_NAME_LENGTH - length - n_tail);
- +}
- +
- +/**
- + * Function appending numeric tail in an effort to avoid redundancy
- + * of filename.
- + * @param dir inode corresponding to given dos name
- + * @param dos_name dos_name to which numeric tail is to be appended
- + * @return return zero on success, a negative value on failure
- + *
- + * This function check if any file having same dos name with given one exists
- + * in the directory and calcuate a numeric tail.
- + */
- +static int add_numeric_tail(struct inode *dir, u8 *dos_name)
- +{
- + int count = 0;
- +
- + count = get_numeric_tail(dir, dos_name);
- + if (count < 0)
- + return count;
- +
- + append_tail(dos_name, count);
- +
- + return 0;
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/*
- + * Global function
- + */
- +
- +/**
- + * Function assigning a FAT time to directory entry by converting a given
- + * linux time.
- + * @param ep directory entry to be modified
- + * @param cur_time linux time to be converted
- + * @return none
- + */
- +#ifdef RFS_FOR_2_6
- +void set_entry_time(struct rfs_dir_entry *ep, struct timespec cur_time)
- +{
- + unsigned short time = 0x00, date = 0x21;
- + struct timespec tmp_time;
- +
- + /* set current system time by default */
- + tmp_time = (cur_time.tv_sec > 0) ? cur_time : CURRENT_TIME;
- + convert_date_linux2dos(tmp_time.tv_sec, &time, &date);
- +#else
- +void set_entry_time(struct rfs_dir_entry *ep, time_t cur_time)
- +{
- + unsigned short time = 0x00, date = 0x21;
- + time_t tmp_time = 0;
- +
- + /* set current system time by default */
- + tmp_time = (cur_time > 0) ? cur_time : CURRENT_TIME;
- + convert_date_linux2dos(tmp_time, &time, &date);
- +#endif
- +
- + SET16(ep->mtime, time);
- + SET16(ep->mdate, date);
- +}
- +
- +/**
- + * Function retreiving the type of entry and convert it to internal type flag
- + * @param ep directory entry to extract type information
- + * @return internal type flag
- + */
- +unsigned int entry_type(struct rfs_dir_entry *ep)
- +{
- + if (ep->name[0] == (u8) 0x0)
- + return TYPE_UNUSED;
- + else if (ep->name[0] == (u8) DELETE_MARK)
- + return TYPE_DELETED;
- + else if (ep->attr == (u8) ATTR_EXTEND)
- + return TYPE_EXTEND;
- + else if (ep->attr & (u8) ATTR_SUBDIR)
- + return TYPE_DIR;
- + else if (ep->attr & (u8) ATTR_VOLUME)
- + return TYPE_VOLUME;
- +
- + return TYPE_FILE;
- +}
- +
- +#define DOS_time(sec, min, hour) ((sec * 2) + (min * SEC_PER_MIN) + (hour * SEC_PER_HR))
- +#define days_in_years(year) (year / 4) + (year * DAY_PER_YR)
- +#define DOS_date(day, month, year) \
- + ((day - 1) + days[month] + days_in_years(year) + \
- + ((leap_year(year) && (month < 2))? 0: 1))
- +
- +/**
- + * Function converting a FAT time to linux time
- + * @param time a time value of the FAT time
- + * @param date a date value of teh FAT time
- + * @return converted linux time
- + */
- +unsigned int entry_time(unsigned short time, unsigned short date)
- +{
- + int secs, day;
- +
- + /* first subtract and mask after that... Otherwise, if
- + date == 0, bad things happen */
- +
- + secs = DOS_time((time & 0x1F), ((time >> 5) & 0x3F), (time >> 11));
- +
- + /* days since 1.1.70 plus 80's leap day */
- + day = DOS_date((date & 0x1F), (((date >> 5) - 1) & 0xF), (date >> 9)) +
- + DAY_PER_10YR;
- +
- + secs += day * SEC_PER_DAY;
- +
- + /* reflect timezone */
- + secs += sys_tz.tz_minuteswest * 60;
- +
- + return secs;
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +/**
- + * Function calculating checksum for given dos name
- + * @param dos_name name to be calcuated
- + * @return checksum value
- + *
- + * This function calcuate the check of dos name, which is the key
- + * to identify the validity of the dos entry.
- + */
- +unsigned char calc_checksum(const u8 *dos_name)
- +{
- + unsigned char checksum = 0;
- + int i;
- +
- + for (i = 0; i < DOS_NAME_LENGTH; i++, dos_name++)
- + checksum = (((checksum & 1) << 7) | ((checksum & 0xFE) >> 1)) + *dos_name;
- +
- + return checksum;
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/**
- + * Function to create dosname with given cstring
- + * @param dir inode pointer
- + * @param name given cstring
- + * @param dosname empty string buffer
- + * @param mixed return value indicating mixed string
- + * @param uname return unicode name from cstring name
- + * @return the number of the extend slots needed on success, a negative error code on failure
- + */
- +int mk_dosname(struct inode *dir, const char *name, u8 *dosname, unsigned char *mixed, u16 *uname)
- +{
- + struct rfs_sb_info *sbi = RFS_SB(dir->i_sb);
- + unsigned int status = 0;
- + int ret = 0;
- + int uni_len;
- +
- + /* make uname and base dosname (without numeric-tail) */
- + uni_len = create_fatname(name, dosname, uname, &status, sbi->nls_disk, TRUE);
- + if (uni_len < 0)
- + return uni_len;
- +
- + /* check reserved names */
- + /* if not support long name,
- + * must check reserved names regardless of lossy
- + * if support long name, only check reserved names if lossy is true
- + */
- +
- + ret = check_name(dosname, get_lossy(status), IS_VFAT(sbi));
- + if (ret < 0)
- + return ret;
- +
- +#ifdef CONFIG_RFS_VFAT
- + /* when long file name is supported */
- + if (get_lossy(status)) {
- + ret = add_numeric_tail(dir, dosname);
- + if (ret < 0)
- + return ret;
- +
- + ret = ((uni_len + (EXT_UNAME_LENGTH - 1)) / EXT_UNAME_LENGTH);
- + } else if (get_mix(status) == UPPER_N_LOWER) {
- + ret = ((uni_len + (EXT_UNAME_LENGTH - 1)) / EXT_UNAME_LENGTH);
- + } else {
- + ret = 0;
- + }
- +
- + if (mixed) {
- + /*
- + * if SFN has extra slot,
- + * dosname in SFN has mark of lower case in sysid
- + */
- + if (ret)
- + *mixed = (PRIMARY_LOWER | EXTENSION_LOWER);
- + else
- + *mixed = get_mix(status);
- + }
- +
- +#else
- + /* when long file name isn't supported */
- + ret = 0;
- + if (mixed) {
- + /* dosname has mark of lower case in sysid */
- + if (get_mix(status) == UPPER_N_LOWER)
- + *mixed = (PRIMARY_LOWER | EXTENSION_LOWER);
- + else
- + *mixed = get_mix(status);
- + }
- +#endif /* CONFIG_RFS_VFAT */
- +
- + /* there's no unicode name for the extend slots */
- + if ((ret == 0) && uname)
- + uname[0] = 0x0000;
- +
- + /* return the number of extend slots needed */
- + return ret;
- +}
- +
- +/**
- + * Function initializing given entry
- + * @param dir inode of parent directory
- + * @param ep directory entry to be initalized
- + * @param type entry type
- + * @param start_clu start cluster of file
- + * @param dosname dos name of file
- + * @param mixed return value indicating if a file needs extend entry
- + * @return none
- + */
- +void init_dir_entry(struct inode *dir, struct rfs_dir_entry *ep, unsigned int type, unsigned int start_clu, const u8 *dosname, unsigned char *mixed)
- +{
- + ep->cmsec = 0;
- + SET16(ep->ctime, 0);
- + SET16(ep->cdate, 0);
- + SET16(ep->adate, 0);
- + set_entry_time(ep, CURRENT_TIME);
- +
- + SET16(ep->start_clu_lo, start_clu);
- + SET16(ep->start_clu_hi, start_clu >> 16);
- +
- + SET32(ep->size, 0);
- +
- + set_entry_type(ep, type);
- + ep->sysid = *mixed;
- + memcpy(ep->name, dosname, DOS_NAME_LENGTH);
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +/**
- + * Function initializing given extend entry
- + * @param extp extend entry to be initalized
- + * @param type entry type
- + * @param entry_offset a sequence number of current extend entry
- + * @param uname unicode name converted from original file name
- + * @param checksum checksum value
- + * @return return zero on success, a negative value on failure
- + */
- +int init_ext_entry(struct rfs_ext_entry *extp, unsigned int type, unsigned int entry_offset, const u16 *uname, unsigned char checksum)
- +{
- + int i, uname_end = FALSE;
- + u16 part;
- +
- + if (unlikely(!uname)) /* out-of-range input */
- + return -EIO;
- +
- + set_entry_type((struct rfs_dir_entry *)extp, type);
- + extp->entry_offset = (u8) entry_offset;
- + extp->sysid = 0;
- + extp->checksum = checksum;
- + SET16(extp->start_clu, 0);
- +
- + for (i =0; i < EXT_UNAME_LENGTH; i++) {
- + if (!uname_end)
- + part = uname[i];
- + else
- + part = 0xFFFF;
- +
- + if (i < 5)
- + SET16(extp->uni_0_4[i], part);
- + else if (i < 11)
- + SET16(extp->uni_5_10[i - 5], part);
- + else
- + SET16(extp->uni_11_12[i - 11], part);
- +
- + if (uname[i] == 0x0)
- + uname_end = TRUE;
- + }
- +
- + return 0;
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/**
- + * get block number related with offset in FAT16 root directory
- + * @param sb super block pointer
- + * @param[in, out] offset offset of directory entry
- + * @param[out] block block number
- + * @return return 0 on success, errno on failure
- + */
- +static inline int get_root_block(struct super_block *sb, unsigned int *offset, unsigned long *block)
- +{
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + unsigned int off = *offset;
- +
- + off += sbi->root_start_addr;
- + if (off > sbi->root_end_addr) /* out-of-range input */
- + return -EFAULT;
- +
- + *offset = off;
- + *block = off >> sb->s_blocksize_bits;
- + return 0;
- +}
- +
- +/**
- + * Function retriving directory entry using a given cluster value
- + * @param sb super block pointer
- + * @param clu cluster value
- + * @param entry entry offset
- + * @param bh buffer head pointer
- + * @return a pointer of directory entry on success, a negative value on failure
- + */
- +struct rfs_dir_entry *get_entry_with_cluster(struct super_block *sb, unsigned int clu, unsigned int entry, struct buffer_head **bh)
- +{
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + struct rfs_dir_entry *ep = NULL;
- + unsigned long block;
- + unsigned int off;
- + int err;
- +
- + off = entry << DENTRY_SIZE_BITS;
- +
- + if ((clu != sbi->root_clu) || IS_FAT32(sbi)) {
- + unsigned int cluster_offset, block_offset;
- + unsigned int prev, next;
- +
- + /* FAT32 or sub directory */
- + cluster_offset = off >> sbi->cluster_bits;
- + block_offset = (off >> sb->s_blocksize_bits)
- + & (sbi->blks_per_clu - 1);
- + fat_lock(sb);
- + err = find_cluster(sb, clu, cluster_offset, &prev, &next);
- + fat_unlock(sb);
- + if (err)
- + return ERR_PTR(err);
- + block = START_BLOCK(prev, sb) + block_offset;
- + } else {
- + /* FAT16 root directory */
- + err = get_root_block(sb, &off, &block);
- + if (err)
- + return ERR_PTR(err);
- + }
- +
- + if (*bh)
- + brelse(*bh);
- +
- + *bh = rfs_bread(sb, block, BH_RFS_DIR);
- + if (!(*bh)) { /* I/O error */
- + DPRINTK("can't get buffer head\n");
- + return ERR_PTR(-EIO);
- + }
- +
- + off &= sb->s_blocksize - 1;
- + ep = (struct rfs_dir_entry *) ((*bh)->b_data + off);
- +
- + return ep;
- +}
- +
- +/**
- + * Function retriving directy entry indicted by given entry num
- + * @param dir inode relating to seeking entry
- + * @param entry entry position to be found
- + * @param res_bh buffer head to contain found directry portion
- + * @return a pointer of directory entry on success, a negative value on failure
- + *
- + * Because of difference in name handling routine, find_entry_long handles only
- + * searching with long file name.
- + */
- +struct rfs_dir_entry *get_entry(struct inode *dir, unsigned int entry, struct buffer_head **res_bh)
- +{
- + struct super_block *sb = dir->i_sb;
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + struct rfs_dir_entry *ep = NULL;
- + unsigned long block;
- + unsigned int off;
- + int err;
- +
- + /* 1. get the real offset in the buffer head */
- + off = entry << DENTRY_SIZE_BITS;
- +
- + if ((RFS_I(dir)->start_clu != sbi->root_clu) || IS_FAT32(sbi)) {
- + long iblock;
- +
- + /* FAT32 or sub directory */
- + iblock = off >> sb->s_blocksize_bits;
- + err = rfs_bmap(dir, iblock, &block);
- + } else {
- + /* FAT16 root directory */
- + err = get_root_block(sb, &off, &block);
- + }
- +
- + if (err)
- + return ERR_PTR(err);
- +
- + if (*res_bh)
- + brelse(*res_bh);
- +
- + *res_bh = rfs_bread(sb, block, BH_RFS_DIR);
- + if (*res_bh == NULL) { /* I/O error */
- + DPRINTK("can't get buffer head\n");
- + return ERR_PTR(-EIO);
- + }
- +
- + off &= sb->s_blocksize - 1;
- + ep = (struct rfs_dir_entry *) ((*res_bh)->b_data + off);
- +
- + return ep;
- +}
- +
- +#ifdef CONFIG_RFS_VFAT
- +/**
- + * Function retrieving unicode name from directory entry
- + * @param dir inode corresponding to directory entry
- + * @param entry the position of directory entry with short file name
- + * @param uname variable for retrieved unicode name
- + * @return zero on success, a negative error code on failure
- + *
- + * This function retrieves unicode name by seeking extend directory entry
- + * with given a offset of short file name entry.
- + */
- +int get_uname_from_entry (struct inode *dir, unsigned int entry, u16 *uname)
- +{
- + struct rfs_ext_entry *extp = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + struct buffer_head *bh = NULL;
- + int i, err = 0;
- +
- + if ((int) entry <= 0) /* out-of-range input */
- + return err;
- +
- + for (i = 0, entry--; entry >= 0; entry--) {
- + ep = get_entry(dir, entry, &bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) != -EFAULT)
- + err = PTR_ERR(ep);
- + break;
- + }
- + extp = (struct rfs_ext_entry *)ep;
- + /* boundary check for max length of array & error handling*/
- + if (entry_type(ep) == TYPE_EXTEND) {
- + get_uname_from_ext_entry(extp, &(uname[i]), TRUE);
- +
- + if (i > UNICODE_NAME_LENGTH) { /* out-of-range input */
- + err = -EIO;
- + break;
- + }
- +
- + if (extp->entry_offset > EXT_END_MARK)
- + break;
- + } else
- + break;
- +
- + i += 13;
- + }
- +
- + brelse(bh);
- +
- + return err;
- +}
- +
- +/**
- + * Function to extract the unicode name from the extend slots
- + * @param dir inode relating to seeking entry
- + * @param entry entry position of the last extend slot
- + * @param bh buffer head pointer
- + * @param ep the last extend slot as input and the SFN slot as output
- + * @return the number of the extend slots
- + */
- +static int get_long_name(struct inode *dir, unsigned int entry, struct buffer_head **res_bh, struct rfs_dir_entry **ep, u16 *ext_uname)
- +{
- + struct rfs_ext_entry *extp;
- + unsigned char checksum;
- + unsigned int type;
- + unsigned int cpos = entry;
- + int nr_ext, i;
- +
- + /* last index */
- + extp = (struct rfs_ext_entry *) (*ep);
- + nr_ext = (int) (extp->entry_offset - EXT_END_MARK);
- + checksum = extp->checksum;
- +
- + i = nr_ext;
- +
- + while (i > 0) {
- + get_uname_from_ext_entry(extp,
- + ext_uname + (i - 1) * EXT_UNAME_LENGTH,
- + (extp->entry_offset > EXT_END_MARK)? TRUE: FALSE);
- +
- + *ep = get_entry(dir, ++cpos, res_bh);
- + if (IS_ERR(*ep)) {
- + if (PTR_ERR(*ep) == -EFAULT) /* end-of-directory */
- + return -ENOENT;
- + return PTR_ERR(*ep);
- + }
- +
- + /* last is SFN directory entry */
- + if (i == 1)
- + break;
- +
- + /* no extend entries */
- + if (entry_type(*ep) != TYPE_EXTEND) {
- + ext_uname[0] = 0x0000;
- + return 0;
- + }
- +
- + /* orphan LFN entry */
- + extp = (struct rfs_ext_entry *) *ep;
- + if ((extp->entry_offset > EXT_END_MARK) ||
- + (extp->checksum != checksum))
- + return 0;
- +
- + i--;
- + }
- +
- + /* SFN entry with extend slots */
- + type = entry_type(*ep);
- +
- + /* orphan LFN entries upto now */
- + if (!(type == TYPE_DIR || type == TYPE_FILE))
- + return 0;
- +
- + if (checksum != calc_checksum((*ep)->name)) {
- + /* no extend entries */
- + ext_uname[0] = 0x0000;
- + return 0;
- + }
- +
- + return nr_ext;
- +}
- +
- +/**
- + * Function check if given file name is exist
- + * @param dir inode relating to seeking entry
- + * @param name name of file to be sought
- + * @param bh buffer head pointer
- + * @param seek_type entry type to be sought
- + * @return a offset of entry if file name exists, a negative value otherwise.
- + *
- + * Because of difference in name handling routine, find_entry_long handles only
- + * searching with long file name
- + */
- +int find_entry_long (struct inode *dir, const char *name, struct buffer_head **bh, unsigned int seek_type)
- +{
- + struct rfs_dir_entry *ep;
- + struct rfs_ext_entry *extp;
- + u16 ext_uname[MAX_TOTAL_LENGTH];
- + u16 unicode[MAX_TOTAL_LENGTH];
- + char dosname[DOS_NAME_LENGTH + 1];
- + int cpos = 0;
- + int nr_slot = 0;
- + int uni_len = 0, uni_slot = 0;
- + unsigned int status = 0;
- + unsigned int type;
- +
- + if (RFS_I(dir)->start_clu == RFS_SB(dir->i_sb)->root_clu) {
- + if (!strcmp(name, ".") || !strcmp(name, ".."))
- + return -INVALID_ENTRY;
- + }
- +
- + memset(unicode, 0xff, MAX_TOTAL_LENGTH * sizeof(u16));
- +
- + /* uni_len has the length of unicode */
- + uni_len = create_fatname(name, dosname, unicode, &status,
- + RFS_SB(dir->i_sb)->nls_disk, FALSE);
- + if (uni_len < 0)
- + return uni_len;
- +
- + uni_slot = ((uni_len + (EXT_UNAME_LENGTH - 1)) / EXT_UNAME_LENGTH);
- +
- + /* scan the directory */
- + while(1) {
- + ep = get_entry(dir, cpos, bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT) /* end-of-directory */
- + return -ENOENT;
- + return PTR_ERR(ep);
- + }
- +
- + type = entry_type(ep);
- + if (type == TYPE_UNUSED) /* end-of-directory */
- + return -ENOENT;
- + if (type == TYPE_DELETED || type == TYPE_VOLUME) {
- + cpos++;
- + continue;
- + }
- +
- + if (type == TYPE_EXTEND) {
- + extp = (struct rfs_ext_entry *) ep;
- + if ((extp->entry_offset < EXT_END_MARK) || (uni_len == 0)) {
- + cpos++;
- + continue;
- + }
- +
- + /* get long name from extend slot iff unicode exist */
- + memset(ext_uname, 0xff, MAX_TOTAL_LENGTH * sizeof(u16));
- + nr_slot = get_long_name(dir, cpos, bh, &ep, ext_uname);
- + if (nr_slot > 0) {
- + /* found LFN slot, SFN slot */
- + cpos += nr_slot;
- + } else if (nr_slot == 0) {
- + /* not found LFN slot */
- + cpos++;
- + continue;
- + } else {
- + /* fail */
- + return nr_slot;
- + }
- +
- + /* compare long name if length is same */
- + if (nr_slot == uni_slot) {
- + if (!memcmp(ext_uname, unicode, (nr_slot *
- + EXT_UNAME_LENGTH * sizeof(u16)))) {
- + if ((seek_type == TYPE_ALL) ||
- + (seek_type == type))
- + goto found;
- + }
- + }
- + }
- +
- + if (uni_len == 0) {
- + /* always compare short name */
- + if (!strncmp(dosname, ep->name, DOS_NAME_LENGTH)) {
- + if ((seek_type == TYPE_ALL) || (seek_type == type))
- + goto found;
- + }
- + }
- + cpos++;
- + }
- +
- +found:
- + return cpos;
- +}
- +
- +#else /* !CONFIG_RFS_VFAT */
- +
- +/**
- + * Function check if given file name is exist
- + * @param dir inode relating to seeking entry
- + * @param name file name to be sought
- + * @param bh buffer head to contain found directry portion
- + * @param seek_type entry type to be sought
- + * @return a offset of entry if file name exists, a negative value otherwise.
- + *
- + * Because of difference in name handling routine, find_entry_long handles only
- + * searching with short file name (8.3 dos name)
- + */
- +int find_entry_short(struct inode *dir, const char *name, struct buffer_head **bh, unsigned int seek_type)
- +{
- + struct rfs_dir_entry *ep;
- + char dosname[DOS_NAME_LENGTH];
- + int cpos = 0;
- + unsigned int type;
- +
- + if (RFS_I(dir)->start_clu == RFS_SB(dir->i_sb)->root_clu) {
- + if (!strcmp(name, ".") || !strcmp(name, ".."))
- + return -INVALID_ENTRY;
- + }
- +
- + cpos = create_fatname(name, dosname, NULL, NULL,
- + RFS_SB(dir->i_sb)->nls_disk, FALSE);
- + if (cpos < 0)
- + return cpos;
- +
- + cpos = 0;
- + while (1) {
- + ep = get_entry(dir, cpos, bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT) /* end-of-directory */
- + return -ENOENT;
- + return PTR_ERR(ep);
- + }
- +
- + type = entry_type(ep);
- + if (type == TYPE_FILE || type == TYPE_DIR) {
- + if (!strncmp(dosname, ep->name, DOS_NAME_LENGTH)) {
- + if (seek_type == TYPE_ALL) {
- + goto found;
- + } else if (seek_type == type) {
- + goto found;
- + }
- + }
- + } else if (type == TYPE_UNUSED) { /* end-of-directory */
- + return -ENOENT;
- + }
- + cpos++;
- + }
- +
- +found:
- + return cpos;
- +}
- +#endif /* CONFIG_RFS_VFAT */
- +
- +/**
- + * Function to remove directy entries
- + * @param dir inode relating to deteted entry
- + * @param entry position of entry deletion will occur
- + * @param bh buffer head containing directory entry
- + * @return zero on success, negative error code on failure
- + */
- +int remove_entry (struct inode *dir, unsigned int entry, struct buffer_head **bh)
- +{
- + struct rfs_dir_entry *ep;
- + unsigned int numof_entries = 0;
- + unsigned int i = 0;
- + /*
- + * RFS-log : 21 comes from
- + * cell(MAX_NAME_LENGTH / EXT_UNAME_LENGTH) + 1.
- + * It means max(extended entries) + original entry
- + */
- + unsigned char undel_buf[21];
- + unsigned char ent_off = 0;
- +
- + while (1) {
- + ep = get_entry(dir, entry - i, bh);
- + if (IS_ERR(ep))
- + goto error;
- +
- + if ((i != 0) && (entry_type(ep) != TYPE_EXTEND))
- + break;
- +
- + if (i == 0) {
- + /* sanity check */
- + if (unlikely((entry_type(ep) != TYPE_FILE) &&
- + (entry_type(ep) != TYPE_DIR))) {
- + RFS_BUG("dir entry (%d, %d) is corrupted\n",
- + RFS_I(dir)->start_clu,
- + entry);
- + return -EIO;
- + }
- + }
- +
- + undel_buf[i] = ep->name[0];
- + numof_entries++;
- + if (entry - i == 0)
- + break;
- + if (entry_type(ep) == TYPE_EXTEND) {
- + ent_off = ((struct rfs_ext_entry *) ep)->entry_offset;
- + if (ent_off > EXT_END_MARK)
- + break;
- + }
- + i++;
- + }
- +
- + /* RFS-log : remove entry */
- + if (rfs_log_remove_entry(dir->i_sb, RFS_I(dir)->start_clu, entry,
- + numof_entries, undel_buf)) {
- + return -EIO;
- + }
- +
- + for (i = 0; i < numof_entries; i++) {
- + ep = get_entry(dir, entry - i, bh);
- + if (IS_ERR(ep))
- + goto error;
- +
- + set_entry_type(ep, TYPE_DELETED);
- +
- + if (buffer_uptodate(*bh))
- + rfs_mark_buffer_dirty(*bh, dir->i_sb);
- + }
- +
- + return 0;
- +
- +error:
- + return PTR_ERR(ep);
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/file.c linux-2.6.17.14.rfs/fs/rfs/file.c
- --- linux-2.6.17.14.orig/fs/rfs/file.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/file.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,731 @@
- +/**
- + *
- + * @file fs/rfs/file.c
- + * @brief file and file inode functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/spinlock.h>
- +#include <linux/smp_lock.h>
- +#include <linux/fs.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +#define loff_t off_t
- +#endif
- +
- +/**
- + * check the hint info of inode whether it is available
- + * @param inode inode
- + * @param num_clusters number of clusters to skip
- + * @param start_clu start cluster number to read after skipping hint
- + * @return return 0 on success, EINVAL on failure
- + *
- + * If hint info has availability, rest cluster chain can be read after skipping specified clusters
- + */
- +static inline int rfs_lookup_hint(struct inode *inode, unsigned int *num_clusters, unsigned int *start_clu)
- +{
- + if (RFS_I(inode)->start_clu == CLU_TAIL)
- + return -EFAULT;
- +
- + if ((*num_clusters > 0) && (RFS_I(inode)->hint_last_offset > 0)
- + && (RFS_I(inode)->hint_last_clu >= VALID_CLU)
- + && (*num_clusters >= RFS_I(inode)->hint_last_offset)) {
- + *start_clu = RFS_I(inode)->hint_last_clu;
- + *num_clusters -= RFS_I(inode)->hint_last_offset;
- + } else {
- + *start_clu = RFS_I(inode)->start_clu;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * invalidate the hint info of inode
- + * @param inode inode
- + */
- +void rfs_invalidate_hint(struct inode *inode)
- +{
- + RFS_I(inode)->hint_last_clu = 0;
- + RFS_I(inode)->hint_last_offset = 0;
- +}
- +
- +/**
- + * update the hint info of inode
- + * @param inode inode
- + * @param cluster previous last cluster number accessed
- + * @param offset cluster offset into a fat chain
- + */
- +static inline void rfs_update_hint(struct inode *inode, unsigned int cluster, unsigned int offset)
- +{
- + if (cluster < VALID_CLU || !offset) {
- + rfs_invalidate_hint(inode);
- + } else {
- + RFS_I(inode)->hint_last_clu = cluster;
- + RFS_I(inode)->hint_last_offset = offset;
- + }
- +}
- +
- +/**
- + * fill the page with zero
- + * @param inode inode
- + * @param page page pointer
- + * @param zerofrom start offset within page
- + * @param zeroto last offset within page
- + * @param get_block get_block funciton
- + * @return return 0 on success, errno on failure
- + *
- + * This function is invoked by extend_with_zerofill
- + */
- +static int rfs_page_zerofill(struct inode * inode, struct page * page, unsigned zerofrom, unsigned zeroto, get_block_t *get_block)
- +{
- + struct buffer_head *bh, *head;
- + unsigned long block;
- + unsigned block_start, block_end, blocksize, bbits, blk_aligned_zeroto;
- + int err = 0, partial = 0;
- + char *kaddr;
- +
- + bbits = inode->i_blkbits;
- + blocksize = 1 << bbits;
- +
- +#ifdef RFS_FOR_2_6
- + if (!page_has_buffers(page))
- + create_empty_buffers(page, blocksize, 0);
- + head = page_buffers(page);
- +#else
- + if (!page->buffers)
- + create_empty_buffers(page, inode->i_dev, blocksize);
- + head = page->buffers;
- +#endif
- +
- + block = page->index << (PAGE_CACHE_SHIFT - bbits); /* start block # */
- +
- + /* In the first phase, we allocate buffers and map them to fill zero */
- + for(bh = head, block_start = 0; bh != head || !block_start;
- + block++, block_start = block_end + 1, bh = bh->b_this_page) {
- + if (!bh) { /* I/O error */
- + err = -EIO;
- + RFS_BUG("can't get buffer head\n");
- + goto out;
- + }
- + block_end = block_start + blocksize - 1;
- + if (block_end < zerofrom)
- + continue;
- + else if (block_start > zeroto)
- + break;
- + clear_bit(BH_New, &bh->b_state);
- +
- + /* map new buffer head */
- + err = get_block(inode, block, bh, 1);
- + if (err) {
- + DEBUG(DL1, "no block\n");
- + goto out;
- + }
- + if (!buffer_uptodate(bh) && (block_start < zerofrom)) {
- + ll_rw_block(READ, 1, &bh);
- + wait_on_buffer(bh);
- + if (!buffer_uptodate(bh)) {
- + err = -EIO;
- + goto out;
- + }
- + }
- + }
- +
- + /*
- + * In the second phase, we memset the page with zero,
- + * in the block aligned manner.
- + * If memset is not block-aligned, hole may return garbage data.
- + */
- + blk_aligned_zeroto = zeroto | (blocksize - 1);
- + kaddr = kmap_atomic(page, KM_USER0);
- + memset(kaddr + zerofrom, 0, blk_aligned_zeroto - zerofrom + 1);
- +
- + /* In the third phase, we make the buffers uptodate and dirty */
- + for(bh = head, block_start = 0; bh != head || !block_start;
- + block_start = block_end + 1, bh = bh->b_this_page) {
- + block_end = block_start + blocksize - 1;
- + if (block_end < zerofrom) {
- + /* block exists in the front of zerofrom. */
- + if (!buffer_uptodate(bh))
- + partial = 1;
- + continue;
- + } else if (block_start > zeroto) {
- + /* block exists in the back of zeroto. */
- + partial = 1;
- + break;
- + }
- +
- +#ifdef RFS_FOR_2_6
- + set_buffer_uptodate(bh);
- + mark_buffer_dirty(bh);
- +#else
- + mark_buffer_uptodate(bh, 1);
- + mark_buffer_dirty(bh);
- + down(&RFS_I(inode)->data_mutex);
- + buffer_insert_inode_data_queue(bh, inode);
- + up(&RFS_I(inode)->data_mutex);
- +#endif
- + }
- +
- + /* if all buffers of a page were filled zero */
- + if (!partial)
- + SetPageUptodate(page);
- +
- +out:
- + flush_dcache_page(page);
- + kunmap_atomic(kaddr, KM_USER0);
- + return err;
- +}
- +
- +/**
- + * extend a file with zero-fill
- + * @param inode inode
- + * @param origin_size file size before extend
- + * @param new_size file size to extend
- + * @return return 0 on success, errno on failure
- + *
- + * rfs doesn't allow holes.
- + */
- +int extend_with_zerofill(struct inode *inode, unsigned int origin_size, unsigned int new_size)
- +{
- + struct address_space *mapping = inode->i_mapping;
- + struct super_block *sb = inode->i_sb;
- + struct page *page = NULL;
- + unsigned long index, final_index;
- + unsigned long next_page_start, offset, next_offset;
- + unsigned int origin_clusters, new_clusters, clusters_to_extend;
- + unsigned zerofrom, zeroto; /* offset within page */
- + int err = 0;
- +
- + /* compare the number of required clusters with that of free clusters */
- + origin_clusters = (origin_size + RFS_SB(sb)->cluster_size - 1)
- + >> RFS_SB(sb)->cluster_bits;
- + new_clusters = (new_size + RFS_SB(sb)->cluster_size - 1)
- + >> RFS_SB(sb)->cluster_bits;
- + clusters_to_extend = new_clusters - origin_clusters;
- +
- + if (clusters_to_extend && (clusters_to_extend > GET_FREE_CLUS(RFS_SB(sb)))) {
- + DEBUG(DL2, "No space \n");
- + return -ENOSPC;
- + }
- +
- + offset = origin_size;
- + final_index = (new_size - 1) >> PAGE_CACHE_SHIFT; /* newsize isn't 0 */
- + while ((index = (offset >> PAGE_CACHE_SHIFT)) <= final_index) {
- + page = grab_cache_page(mapping, index);
- + if (!page) { /* memory error */
- + DEBUG(DL0, "out of memory !!");
- + return -ENOMEM;
- + }
- +
- + /* calculate zerofrom and zeroto */
- + next_page_start = (index + 1) << PAGE_CACHE_SHIFT;
- + next_offset = (new_size > next_page_start) ? next_page_start : new_size;
- +
- + zerofrom = offset & (PAGE_CACHE_SIZE - 1);
- + zeroto = (next_offset - 1) & (PAGE_CACHE_SIZE - 1);
- +
- + err = rfs_page_zerofill(inode, page, zerofrom, zeroto, rfs_get_block);
- + if (err) {
- + if (unlikely(err == -ENOSPC)) {
- + DEBUG(DL0, "The # of the real free clusters is different from super block.");
- + err = -EIO;
- + }
- + ClearPageUptodate(page);
- + unlock_page(page);
- + page_cache_release(page);
- + DEBUG(DL1, "zero fill failed (err : %d)\n", err);
- + goto out;
- + }
- +
- + offset = next_page_start;
- + unlock_page(page);
- + page_cache_release(page);
- + }
- +
- +out:
- + return err;
- +}
- +
- +/**
- + * truncate a file to a specified size
- + * @param inode inode
- + *
- + * support to reduce or enlarge a file
- + */
- +void rfs_truncate(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int num_clusters = 0;
- + unsigned long origin_size, origin_mmu_private;
- + int is_back = 0;
- + int err;
- +
- + origin_size = RFS_I(inode)->trunc_start;
- + origin_mmu_private = RFS_I(inode)->mmu_private;
- +
- + /* check the validity */
- + if (IS_RDONLY(inode))
- + goto rollback_size;
- +
- + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
- + S_ISLNK(inode->i_mode)))
- + goto rollback_size;
- +
- + if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- + goto rollback_size;
- +
- + /* RFS-log : start truncate */
- + if ((loff_t) origin_size < (loff_t) inode->i_size) {
- + /* if caller is inode_setattr, tr can be nested */
- + err = rfs_log_start(sb, RFS_LOG_TRUNCATE_F, inode);
- + is_back = 0;
- + } else {
- + err = rfs_log_start(sb, RFS_LOG_TRUNCATE_B, inode);
- + is_back = 1;
- + }
- + if (err)
- + goto rollback_size;
- +
- + /* transactin starts from here */
- + if (RFS_I(inode)->i_state == RFS_I_FREE)
- + /* RFS do not perform truncate of unlinked file */
- + goto end_log;
- +
- + if (is_back) { /* reduce a file */
- + num_clusters = (inode->i_size + RFS_SB(sb)->cluster_size - 1)
- + >> RFS_SB(sb)->cluster_bits;
- +
- + err = dealloc_clusters(inode, num_clusters);
- + if (err) {
- + /*
- + * FIXME: media failure
- + * Even though this failure may result in serious error,
- + * rfs_truncate can propagate it to the upper layer.
- + * This will make it difficult to debug the error
- + * caused by media failure.
- + * Should we revive the dealloced clusters?
- + * Or, should we truncate the file anyway?
- + *
- + * We can mark the inode with media fail.
- + */
- + goto invalidate_hint;
- + }
- + set_mmu_private(inode, inode->i_size);
- + } else if (origin_mmu_private < inode->i_size) {
- + /* extending file with zero */
- + err = extend_with_zerofill(inode,
- + (u32) origin_mmu_private, (u32) inode->i_size);
- + if (err == -ENOSPC) {
- + DEBUG(DL2, "failed to enlarge a file");
- + goto end_log;
- + } else if (err) {
- + DEBUG(DL2, "failed to enlarge a file");
- + truncate_inode_pages(inode->i_mapping, origin_size);
- + num_clusters = (origin_size + RFS_SB(sb)->cluster_size - 1)
- + >> RFS_SB(sb)->cluster_bits;
- +
- + err = dealloc_clusters(inode, num_clusters);
- + if (err)
- + DEBUG(DL2, "failed to reduce a file");
- +
- + goto invalidate_hint;
- + }
- + } else {
- + /* truncate forward but already zero filled, so do nothing */
- + }
- +
- + /* invalidate hint info */
- + rfs_invalidate_hint(inode);
- +
- + inode->i_blocks = (inode->i_size + SECTOR_SIZE - 1) >> SECTOR_BITS;
- + inode->i_mtime = inode->i_atime = CURRENT_TIME;
- + rfs_mark_inode_dirty(inode);
- +
- + /* RFS-log : end truncate */
- + if (rfs_log_end(sb, 0)) {
- + /* should we mark the file with media failure */
- + ;
- + }
- +
- + return;
- +
- +invalidate_hint:
- + rfs_invalidate_hint(inode);
- + RFS_I(inode)->mmu_private = origin_mmu_private;
- + inode->i_size = (loff_t) origin_size;
- + rfs_mark_inode_dirty(inode);
- + rfs_log_end(sb, 1);
- + return;
- +
- +end_log:
- + inode->i_size = (loff_t) origin_size;
- + rfs_mark_inode_dirty(inode);
- + rfs_log_end(sb, 1);
- + return;
- +
- +rollback_size:
- + inode->i_size = (loff_t) origin_size;
- + mark_inode_dirty(inode);
- + return;
- +}
- +
- +/**
- + * change an attribute of inode
- + * @param dentry dentry
- + * @param attr new attribute to set
- + * @return return 0 on success, errno on failure
- + *
- + * it is only used for chmod, especially when read only mode be changed
- + */
- +int rfs_setattr(struct dentry *dentry, struct iattr *attr)
- +{
- + struct inode *inode = dentry->d_inode;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + int perm, err = 0;
- + int tr_start = 0;
- +
- + CHECK_RFS_INODE(inode, -EINVAL);
- +
- + if (check_reserved_files(inode, NULL))
- + return -EPERM;
- +
- + if (attr->ia_valid & ATTR_SIZE) {
- + if ((loff_t) inode->i_size < (loff_t) attr->ia_size) {
- + unsigned int end_index = inode->i_size >>
- + PAGE_CACHE_SHIFT;
- + unsigned int mmu_private = RFS_I(inode)->mmu_private;
- + unsigned int next_page_start, to_size;
- +
- + next_page_start = (end_index + 1) << PAGE_CACHE_SHIFT;
- + to_size = ((loff_t) attr->ia_size >
- + (loff_t) next_page_start) ?
- + next_page_start : (unsigned int) attr->ia_size;
- + if (mmu_private < to_size) {
- + /*
- + * RFS-log : fill last page with zero
- + * to avoid race-condition with writepage
- + * (recursive transaction)
- + */
- + if (rfs_log_start(inode->i_sb,
- + RFS_LOG_TRUNCATE_F,
- + inode))
- + return -EIO;
- +
- + tr_start = 1; /* transaction starts */
- +
- + err = extend_with_zerofill(inode,
- + (u32) mmu_private,
- + (u32) to_size);
- +
- + if (err) {
- + rfs_log_end(inode->i_sb, err);
- + return err;
- + }
- +
- + /* update zerofilled inode's size */
- + inode->i_size = to_size;
- + }
- + }
- +
- + /* keep current inode size for truncate operation */
- + RFS_I(inode)->trunc_start = inode->i_size;
- + }
- +
- + if (attr->ia_valid & ATTR_MODE) {
- + perm = attr->ia_mode & 0777;
- + ep = get_entry_with_cluster(inode->i_sb,
- + RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index, &bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT)
- + err = -ENOENT;
- + else
- + err = PTR_ERR(ep);
- + goto out;
- + }
- +
- + if (perm & 0222)
- + ep->attr &= ~ATTR_READONLY;
- + else
- + ep->attr |= ATTR_READONLY;
- +
- + mark_buffer_dirty(bh);
- + }
- +
- + err = inode_setattr(inode, attr);
- +
- +out:
- + brelse(bh);
- +
- + if (tr_start)
- + rfs_log_end(inode->i_sb, err);
- +
- + return err;
- +}
- +
- +/**
- + * check a permission of inode
- + * @param inode inode
- + * @param mode mode
- + * @param nd nameidata
- + * @return return 0 on success, EPERM on failure
- + *
- + * System file (log or pool file) can not be accessed
- + */
- +#ifdef RFS_FOR_2_6
- +int rfs_permission(struct inode *inode, int mode, struct nameidata *nd)
- +#else
- +int rfs_permission(struct inode *inode, int mode)
- +#endif
- +{
- + if (mode & (MAY_WRITE | MAY_READ))
- + return check_reserved_files(inode, NULL);
- +
- + return 0; /* all operations are permitted */
- +}
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +#undef loff_t
- +#endif
- +
- +/**
- + * write up to count bytes to file at speicified position
- + * @param file file
- + * @param buf buffer pointer
- + * @param count number of bytes to write
- + * @param ppos offset in file
- + * @return return write bytes on success, errno on failure
- + *
- + * use pre-allocation for reducing working logs
- + */
- +static ssize_t rfs_file_write(struct file *file, const char *buf,
- + size_t count, loff_t *ppos)
- +{
- + struct inode *inode;
- + ssize_t ret;
- + int err;
- +
- + ret = generic_file_write(file, buf, count, ppos);
- + if (ret <= 0)
- + return ret;
- +
- + inode = file->f_dentry->d_inode->i_mapping->host;
- + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) {
- + err = rfs_log_force_commit(inode->i_sb, inode);
- + if (err)
- + return -EIO;
- + }
- +
- + return ret;
- +}
- +
- +/**
- + * flush all dirty buffers of inode include data and meta data
- + * @param file file pointer
- + * @param dentry dentry pointer
- + * @param datasync flag
- + * @return return 0 on success, EPERM on failure
- + */
- +static int rfs_file_fsync(struct file * file, struct dentry *dentry, int datasync)
- +{
- + struct inode *inode = dentry->d_inode;
- + struct super_block *sb = inode->i_sb;
- + int ret = 0, err = 0;
- +
- + /* data commit */
- + ret = rfs_sync_inode(inode, 1, 1);
- +
- + /* meta-commit deferred tr */
- + if (tr_deferred_commit(sb) && RFS_LOG_I(sb)->inode &&
- + (RFS_LOG_I(sb)->inode == inode)) {
- + err = rfs_log_force_commit(inode->i_sb, inode);
- + if (err && !ret)
- + ret = err;
- + }
- +
- + return ret;
- +}
- +
- +#ifdef CONFIG_RFS_SYNC_ON_CLOSE
- +/**
- + * flush modified data of file object
- + * @inode inode of file object to flush
- + * @file file object to flush
- + *
- + * It is only called when all files are closed, that is inode is released
- + */
- +static int rfs_file_release(struct inode * inode, struct file * file)
- +{
- +#ifdef RFS_FOR_2_6
- + filemap_fdatawrite(inode->i_mapping);
- + filemap_fdatawait(inode->i_mapping);
- +#endif
- + return rfs_file_fsync(file, file->f_dentry, 0);
- +}
- +#endif
- +
- +struct file_operations rfs_file_operations = {
- + .read = generic_file_read,
- + .write = rfs_file_write,
- + .mmap = generic_file_mmap,
- + .fsync = rfs_file_fsync,
- +#ifdef CONFIG_RFS_SYNC_ON_CLOSE
- + .release = rfs_file_release,
- +#endif
- +};
- +
- +struct inode_operations rfs_file_inode_operations = {
- + .truncate = rfs_truncate,
- + .permission = rfs_permission,
- + .setattr = rfs_setattr,
- +};
- +
- +/**
- + * translate index into a logical block
- + * @param inode inode
- + * @param iblock index
- + * @param bh_result buffer head pointer
- + * @param create flag whether new block will be allocated
- + * @return returns 0 on success, errno on failure
- + *
- + * if there aren't logical block, allocate new cluster and map it
- + */
- +#ifdef RFS_FOR_2_6
- +int rfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
- +#else
- +int rfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
- +#endif
- +{
- + unsigned long phys = 0;
- + struct super_block *sb = inode->i_sb;
- + unsigned int new_clu;
- + int ret = 0;
- +
- +#ifdef RFS_FOR_2_4
- + lock_kernel();
- +#endif
- +
- + ret = rfs_bmap(inode, iblock, &phys);
- + if (!ret) {
- +#ifdef RFS_FOR_2_6
- + map_bh(bh_result, sb, phys);
- +#else
- + bh_result->b_dev = inode->i_dev;
- + bh_result->b_blocknr = phys;
- + bh_result->b_state |= (1UL << BH_Mapped);
- +#endif
- + goto out;
- + }
- +
- + ret = -EIO;
- + if (!create)
- + goto out;
- +
- + if (iblock != (RFS_I(inode)->mmu_private >> sb->s_blocksize_bits))
- + goto out;
- +
- + if (!(iblock & (RFS_SB(sb)->blks_per_clu - 1))) {
- + ret = alloc_cluster(inode, &new_clu);
- + if (ret)
- + goto out;
- + }
- +
- + RFS_I(inode)->mmu_private += sb->s_blocksize;
- + ret = rfs_bmap(inode, iblock, &phys);
- + if (ret) {
- + RFS_I(inode)->mmu_private -= sb->s_blocksize;
- + RFS_BUG("iblock(%ld) doesn't have a physical mapping",
- + iblock);
- + goto out;
- + }
- +
- +#ifdef RFS_FOR_2_6
- + set_buffer_new(bh_result);
- + map_bh(bh_result, sb, phys);
- +#else
- + bh_result->b_dev = inode->i_dev;
- + bh_result->b_blocknr = phys;
- + bh_result->b_state |= (1UL << BH_Mapped);
- + bh_result->b_state |= (1UL << BH_New);
- +#endif
- +
- +out:
- +#ifdef RFS_FOR_2_4
- + unlock_kernel();
- +#endif
- + return ret;
- +}
- +
- +/**
- + * translation index into logical block number
- + * @param inode inode
- + * @param index index number
- + * @param[out] phys logical block number
- + * @return returns 0 on success, errno on failure
- + * @pre FAT16 root directory's inode does not invoke this function
- + */
- +int rfs_bmap(struct inode *inode, long index, unsigned long *phys)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + unsigned int cluster, offset, num_clusters;
- + unsigned int last_block;
- + unsigned int clu, prev, next;
- + int err = 0;
- +
- + fat_lock(sb);
- +
- + cluster = index >> sbi->blks_per_clu_bits;
- + offset = index & (sbi->blks_per_clu - 1);
- +
- + /* check hint info */
- + num_clusters = cluster;
- + err = rfs_lookup_hint(inode, &num_clusters, &clu);
- + if (err)
- + goto out;
- +
- + last_block = (RFS_I(inode)->mmu_private + (sb->s_blocksize - 1))
- + >> sb->s_blocksize_bits;
- + if (index >= last_block) {
- + err = -EFAULT;
- + goto out;
- + }
- +
- + err = find_cluster(sb, clu, num_clusters, &prev, &next);
- + if (err)
- + goto out;
- +
- + /* update hint info */
- + rfs_update_hint(inode, prev, cluster);
- +
- + *phys = START_BLOCK(prev, sb) + offset;
- +out:
- + fat_unlock(sb);
- +
- + return err;
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/inode.c linux-2.6.17.14.rfs/fs/rfs/inode.c
- --- linux-2.6.17.14.orig/fs/rfs/inode.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/inode.c 2008-06-19 04:02:08.000000000 +0200
- @@ -0,0 +1,814 @@
- +/**
- + * @file fs/rfs/inode.c
- + * @brief common inode operations
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/rfs_fs.h>
- +#include <linux/smp_lock.h>
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +#include <linux/mpage.h>
- +#include <linux/backing-dev.h>
- +#endif
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +#ifdef RFS_FOR_2_6
- +
- +ssize_t rfs_direct_IO(int, struct kiocb *, const struct iovec *,
- + loff_t, unsigned long);
- +
- +#define GET_ENTRY_TIME(ep, inode) \
- +do { \
- + inode->i_ctime.tv_sec = inode->i_atime.tv_sec = \
- + inode->i_mtime.tv_sec = \
- + entry_time(GET16(ep->mtime), GET16(ep->mdate)); \
- + inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = \
- + inode->i_atime.tv_nsec = 0; \
- +} while(0);
- +
- +#else /* !RFS_FOR_2_6 */
- +
- +int rfs_direct_IO (int, struct inode *, struct kiobuf *, unsigned long, int);
- +
- +#define GET_ENTRY_TIME(ep, inode) \
- +do { \
- + inode->i_ctime = inode->i_atime= inode->i_mtime = \
- + entry_time(GET16(ep->mtime), GET16(ep->mdate)); \
- +} while(0);
- +#endif /* RFS_FOR_2_6 */
- +
- +/**
- + * read a specified page
- + * @param file file to read
- + * @param page page to read
- + * @return return 0 on success
- + */
- +static int rfs_readpage(struct file *file, struct page *page)
- +{
- +#ifdef RFS_FOR_2_6
- + return mpage_readpage(page, rfs_get_block);
- +#else
- + return block_read_full_page(page, rfs_get_block);
- +#endif
- +}
- +
- +/**
- + * write a specified page
- + * @param page to write page
- + * @param wbc writeback control
- + * @return return 0 on success, errno on failure
- + */
- +#ifdef RFS_FOR_2_6
- +static int rfs_writepage(struct page *page, struct writeback_control *wbc)
- +{
- + return block_write_full_page(page, rfs_get_block, wbc);
- +}
- +#else
- +static int rfs_writepage(struct page *page)
- +{
- + struct inode *inode = page->mapping->host;
- + int ret;
- +
- + down(&RFS_I(inode)->data_mutex);
- + ret = block_write_full_page(page, rfs_get_block);
- + up(&RFS_I(inode)->data_mutex);
- +
- + return ret;
- +}
- +#endif
- +
- +#ifdef RFS_FOR_2_6
- +/**
- + * read multiple pages
- + * @param file file to read
- + * @param mapping address space to read
- + * @param pages page list to read
- + * @param nr_pages number of pages
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_readpages(struct file *file, struct address_space *mapping,
- + struct list_head *pages, unsigned nr_pages)
- +{
- + return mpage_readpages(mapping, pages, nr_pages, rfs_get_block);
- +}
- +
- +/**
- + * write multiple pages
- + * @param mapping address space to write
- + * @param wbc writeback_control
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
- +{
- + return mpage_writepages(mapping, wbc, rfs_get_block);
- +}
- +#endif
- +
- +/**
- + * read some paritial page to write rest page
- + * @param file to read file
- + * @param page specified page to read
- + * @param from start position in page
- + * @param to bytes counts to prepare in page
- + * @return return 0 on success, errno on failure
- + *
- + * This function requires addtional code saving inode->i_size because there is
- + * case when inode->i_size is chagned after cont_prepare_write.
- + */
- +#ifdef RFS_FOR_2_4
- +static int rfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
- +{
- + struct inode *inode = page->mapping->host;
- + unsigned page_start_offset = page->index << PAGE_CACHE_SHIFT;
- + unsigned mmu_private = RFS_I(inode)->mmu_private;
- + unsigned mmu_private_in_page = mmu_private & (PAGE_CACHE_SIZE - 1);
- + unsigned newfrom;
- + char *kaddr;
- + int ret = 0;
- +
- + if (rfs_log_start(inode->i_sb, RFS_LOG_WRITE, inode))
- + return -EIO;
- +
- + if ((page_start_offset + from) > mmu_private) {
- + if (page_start_offset > mmu_private)
- + newfrom = 0;
- + else
- + newfrom = mmu_private_in_page;
- + } else
- + newfrom = from;
- +
- + if (page_start_offset > mmu_private) {
- + /* zerofill the previous hole pages */
- + ret = extend_with_zerofill(inode, mmu_private, page_start_offset);
- + if (ret)
- + goto out;
- + }
- +
- + ret = rfs_block_prepare_write(inode, page, newfrom, to-1, rfs_get_block);
- + if (ret)
- + goto out;
- +
- + if (from > newfrom) {
- + /* memset & commit the previous hole in page */
- + kaddr = page_address(page);
- + memset(kaddr+newfrom, 0, from-newfrom);
- + flush_dcache_page(page);
- + rfs_block_commit_write(inode, page, newfrom, from);
- + kunmap(page);
- + }
- +
- + return ret;
- +
- +out:
- + RFS_I(inode)->trunc_start = RFS_I(inode)->mmu_private;
- + rfs_log_end(inode->i_sb, ret);
- +
- + return ret;
- +}
- +
- +#else
- +static int rfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
- +{
- + struct inode *inode = page->mapping->host;
- + int ret = 0;
- +
- + if (rfs_log_start(inode->i_sb, RFS_LOG_WRITE, inode))
- + return -EIO;
- +
- + ret = cont_prepare_write(page, from, to, rfs_get_block,
- + &(RFS_I(inode)->mmu_private));
- +
- + if (ret) {
- + RFS_I(inode)->trunc_start = RFS_I(inode)->mmu_private;
- + rfs_log_end(inode->i_sb, ret);
- + }
- +
- + return ret;
- +}
- +#endif
- +
- +/**
- + * write a specified page
- + * @param file to write file
- + * @param page page descriptor
- + * @param from start position in page
- + * @param to end position in page
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_commit_write(struct file *file, struct page *page, unsigned from, unsigned to)
- +{
- + struct inode *inode = page->mapping->host;
- + int ret;
- +
- +#ifdef RFS_FOR_2_6
- + ret = generic_commit_write(file, page, from, to);
- +#else
- + down(&RFS_I(inode)->data_mutex);
- + ret = generic_commit_write(file, page, from, to);
- + up(&RFS_I(inode)->data_mutex);
- +#endif
- +
- + if (rfs_log_end(inode->i_sb, ret))
- + return -EIO;
- +
- + return ret;
- +}
- +
- +struct address_space_operations rfs_aops = {
- + .readpage = rfs_readpage,
- + .writepage = rfs_writepage,
- + .sync_page = block_sync_page,
- + .prepare_write = rfs_prepare_write,
- + .commit_write = rfs_commit_write,
- + .direct_IO = rfs_direct_IO,
- +#ifdef RFS_FOR_2_6
- + .readpages = rfs_readpages,
- + .writepages = rfs_writepages,
- +#endif
- +};
- +
- +/*
- + * get an unique inode number
- + * @param dir parent directory
- + * @param index dir entry's offset in parent dir's cluster(s)
- + * @param[out] ino return ino
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_iunique(struct inode *dir, unsigned int index, unsigned long *ino)
- +{
- + struct super_block *sb = dir->i_sb;
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- +
- + /*
- + * NOTE: RFS uses hash value for iunique
- + * which is byte address of index >> bits of dir entry's size.
- + * 0 ~ 15th entries are reserved, because reserved area has one sector
- + * at least. Especially 1 belongs to root inode
- + */
- + if ((RFS_I(dir)->start_clu != sbi->root_clu) || IS_FAT32(sbi)) {
- + unsigned int offset, cluster_offset;
- + unsigned int prev, next;
- + int err;
- +
- + /* in FAT32 root dir or sub-directories */
- + offset = index << DENTRY_SIZE_BITS;
- + cluster_offset = offset >> sbi->cluster_bits;
- + fat_lock(sb);
- + err = find_cluster(sb, RFS_I(dir)->start_clu,
- + cluster_offset, &prev, &next);
- + fat_unlock(sb);
- + if (err)
- + return err;
- +
- + offset &= sbi->cluster_size - 1;
- + offset += (START_BLOCK(prev, sb) << sb->s_blocksize_bits);
- + *ino = offset >> DENTRY_SIZE_BITS;
- + } else {
- + /* in root directory */
- + *ino = (sbi->root_start_addr >> DENTRY_SIZE_BITS) + index;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * fill up the RFS-specific inode
- + * @param inode inode
- + * @param ep dir entry
- + * @param p_start_clu start cluster number of parent inode
- + * @param dentry dir entry index
- + * @return return 0 on success, errno on failure
- + *
- + * it is only invoked when new inode is allocated
- + */
- +int fill_inode(struct inode *inode, struct rfs_dir_entry *ep, unsigned int p_start_clu, unsigned int dentry)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int type;
- + unsigned int size;
- + int err = 0, link_count = 0;
- + int num_clus = 0;
- +
- + /* fill the RFS-specific inode info */
- + RFS_I(inode)->p_start_clu = p_start_clu;
- + RFS_I(inode)->index = dentry;
- + RFS_I(inode)->hint_last_clu = 0;
- + RFS_I(inode)->hint_last_offset = 0;
- + RFS_I(inode)->i_state = RFS_I_ALLOC;
- +
- + /* sanity code */
- + if (START_CLUSTER(ep) && IS_INVAL_CLU(RFS_SB(sb), START_CLUSTER(ep))) {
- + err = -EIO;
- + RFS_BUG("dir entry(%u, %u) has corrupted start_clu(%u)\n",
- + p_start_clu, dentry, START_CLUSTER(ep));
- + goto bad_inode;
- + }
- +
- + /* set start_clu and last_clu in private */
- + if (RFS_POOL_I(sb) &&
- + (START_CLUSTER(ep) == RFS_POOL_I(sb)->start_cluster)) {
- + RFS_I(inode)->start_clu = RFS_POOL_I(sb)->start_cluster;
- + RFS_I(inode)->last_clu = RFS_POOL_I(sb)->last_cluster;
- + } else if (START_CLUSTER(ep)) {
- + /* pool dose exits or dir entry is not for pool file */
- + unsigned int last_clu;
- +
- + RFS_I(inode)->start_clu = START_CLUSTER(ep);
- + num_clus = find_last_cluster(inode, &last_clu);
- + if (num_clus < 0) {
- + err = num_clus;
- + DEBUG(DL0, "No last cluster (err : %d)\n", err);
- + goto bad_inode;
- + }
- + RFS_I(inode)->last_clu = last_clu;
- + } else {
- + /* cluster was not assigned to inode */
- + RFS_I(inode)->start_clu = CLU_TAIL;
- + RFS_I(inode)->last_clu = CLU_TAIL;
- + }
- +
- + spin_lock_init(&RFS_I(inode)->write_lock);
- +
- + type = entry_type(ep);
- +
- + /* set i_size, i_mode, i_op, i_fop, i_mapping, i_nlink */
- + if (type == TYPE_DIR) {
- + /* directory always has zero-size by fat spec */
- + inode->i_size = num_clus << RFS_SB(sb)->cluster_bits;
- +
- + inode->i_mode = S_IFDIR;
- + inode->i_op = &rfs_dir_inode_operations;
- + inode->i_fop = &rfs_dir_operations;
- + link_count = count_subdir(sb, RFS_I(inode)->start_clu);
- + if (unlikely(link_count < 2)) {
- + err = link_count;
- + goto bad_inode;
- + } else {
- + inode->i_nlink = link_count;
- + }
- + } else if (type == TYPE_FILE) {
- + if ((ep->cmsec & SYMLINK_MARK) == SYMLINK_MARK) {
- + inode->i_mode = S_IFLNK;
- + inode->i_op = &page_symlink_inode_operations;
- + inode->i_mapping->a_ops = &rfs_aops;
- + } else {
- + inode->i_mode = S_IFREG;
- + inode->i_op = &rfs_file_inode_operations;
- + inode->i_fop = &rfs_file_operations;
- + inode->i_mapping->a_ops = &rfs_aops;
- + }
- + inode->i_nlink = 1;
- +
- + /* set inode->i_size */
- + if (RFS_POOL_I(sb) && (START_CLUSTER(ep) ==
- + RFS_POOL_I(sb)->start_cluster)) {
- + /* just temporary */
- + inode->i_size = RFS_SB(sb)->cluster_size;
- + } else {
- + int rcv_clus;
- +
- + size = GET32(ep->size);
- +
- + rcv_clus = (size + RFS_SB(sb)->cluster_size - 1)
- + >> RFS_SB(sb)->cluster_bits;
- +
- + if (rcv_clus < num_clus) {
- + /* RFS-log : data replay */
- + DEBUG(DL0, "data recover(%u, %u, %u):"
- + " %d != %d",
- + RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + RFS_I(inode)->start_clu,
- + num_clus, rcv_clus);
- +
- + inode->i_size = size;
- + RFS_I(inode)->trunc_start = num_clus <<
- + RFS_SB(sb)->cluster_bits;
- + rfs_truncate(inode);
- + } else if (rcv_clus > num_clus) {
- + /* just set inode->i_size not modify ep->size */
- + DEBUG(DL0, "size recover (%u, %u, %u):"
- + " %d != %d",
- + RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + RFS_I(inode)->start_clu,
- + num_clus, rcv_clus);
- + inode->i_size = num_clus <<
- + RFS_SB(sb)->cluster_bits;
- + } else
- + inode->i_size = size;
- + }
- + } else {
- + DEBUG(DL0, "dos entry type(%x) is invalid\n", type);
- + err = -EINVAL;
- + goto bad_inode;
- + }
- +
- + inode->i_mode |= 0777;
- + if (ep->attr & ATTR_READONLY) /* handle read-only case */
- + inode->i_mode &= ~0222;
- +
- + inode->i_uid = 0;
- + inode->i_gid = 0;
- + inode->i_version = 0;
- + GET_ENTRY_TIME(ep, inode);
- +
- + inode->i_blksize = sb->s_blocksize;
- +
- + inode->i_blocks = (inode->i_size + SECTOR_SIZE - 1) >> SECTOR_BITS;
- +
- + set_mmu_private(inode, inode->i_size);
- +
- +#ifdef RFS_FOR_2_4
- + init_MUTEX(&RFS_I(inode)->data_mutex);
- + init_timer(&RFS_I(inode)->timer);
- + RFS_I(inode)->timer.function = rfs_data_wakeup;
- + RFS_I(inode)->timer.data = (unsigned long) RFS_I(inode);
- +#endif
- +
- + return 0;
- +
- +bad_inode:
- + make_bad_inode(inode);
- + return err;
- +}
- +
- +/**
- + * create a new inode
- + * @param dir inode of parent directory
- + * @param dentry dentry associated with inode will be created
- + * @param type inode type
- + * @return return inode pointer on success, erro on failure
- + */
- +struct inode *rfs_new_inode(struct inode *dir, struct dentry *dentry, unsigned int type)
- +{
- + struct inode *inode = NULL;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned long new_ino;
- + unsigned int index;
- + int ret = 0;
- +
- + /* get a new inode */
- + if (!(inode = new_inode(dir->i_sb)))
- + return ERR_PTR(-ENOMEM); /* memory error */
- +
- + /* initialize dir entry and extend entries */
- + index = build_entry(dir, inode, 0, type, dentry->d_name.name);
- + if ((int) index < 0) {
- + ret = index;
- + goto alloc_failure;
- + }
- +
- + ep = get_entry(dir, index, &bh);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + goto alloc_failure;
- + }
- +
- + /* fill inode info */
- + ret = fill_inode(inode, ep, RFS_I(dir)->start_clu, index);
- + if (ret)
- + goto alloc_failure;
- +
- + if (type == TYPE_DIR) {
- + /* increase parent's i_nlink */
- + dir->i_nlink++;
- + } else if (type == TYPE_FILE || type == TYPE_SYMLINK) {
- + /* initialize it when only create time */
- + inode->i_mapping->nrpages = 0;
- + }
- +
- + /* get new inode number */
- + ret = rfs_iunique(dir, index, &new_ino);
- + if (ret) {
- + if (type == TYPE_DIR) {
- + inode->i_nlink--;
- + dir->i_nlink--;
- + }
- + goto alloc_failure;
- + }
- + inode->i_ino = new_ino;
- + insert_inode_hash(inode);
- +
- + /*
- + * we already reigsterd the inode to the transaction
- + * in the above build_entry()
- + */
- + mark_inode_dirty(inode);
- +
- + dir->i_mtime = dir->i_atime = CURRENT_TIME;
- + rfs_mark_inode_dirty(dir);
- +
- + brelse(bh);
- + return inode;
- +
- +alloc_failure:
- + inode->i_nlink--;
- + RFS_BUG_ON(inode->i_nlink);
- + iput(inode);
- +
- + if (!IS_ERR(bh))
- + brelse(bh);
- +
- + return ERR_PTR(ret);
- +}
- +
- +/**
- + * delete blocks of inode and clear inode
- + * @param inode inode will be removed
- + * @return return 0 on success, errno on failure
- + *
- + * It will be invoked at the last iput if i_nlink is zero
- + */
- +void rfs_delete_inode(struct inode *inode)
- +{
- +#ifdef RFS_FOR_2_4
- + lock_kernel();
- +#endif
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
- + truncate_inode_pages(&inode->i_data, 0);
- +#endif
- +
- + if (!is_bad_inode(inode)) {
- + /* RFS-log : start of transaction */
- + if (rfs_log_start(inode->i_sb, RFS_LOG_DEL_INODE, inode))
- + goto out;
- +
- + /* actual de-allocation of clusters */
- + rfs_detach_candidate(inode);
- +
- + /* RFS-log : end of transaction */
- + rfs_log_end(inode->i_sb, 0);
- + }
- +
- +out:
- + clear_inode(inode);
- +
- +#ifdef RFS_FOR_2_4
- + unlock_kernel();
- +#endif
- +}
- +
- +/**
- + * deallocate clusters and remove entries for inode
- + * @param dir parent directory inode
- + * @param inode inode will be removed
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_delete_entry(struct inode *dir, struct inode *inode)
- +{
- + struct buffer_head *bh = NULL;
- + int ret;
- +
- + /*
- + * free cluster(s) that were allocated to inode
- + * will be reserved such as candidate free clusters
- + */
- + ret = rfs_attach_candidate(inode);
- + if (ret)
- + return ret;
- +
- + /* remove dos & extent entries */
- + ret = remove_entry(dir, RFS_I(inode)->index, &bh);
- + brelse(bh);
- + if (ret)
- + return ret;
- +
- + /* decrease i_nlink */
- + inode->i_nlink--;
- + if (S_ISDIR(inode->i_mode)) {
- + inode->i_nlink--;
- + dir->i_nlink--;
- + }
- + if (unlikely(inode->i_nlink)) {
- + RFS_BUG("nlink of inode is not zero (%u) \n", inode->i_nlink);
- + return -EIO;
- + }
- +
- + inode->i_mtime = inode->i_atime = CURRENT_TIME;
- + dir->i_mtime = dir->i_atime = CURRENT_TIME;
- +
- + /* change ino to avoid sharing */
- + remove_inode_hash(inode);
- +
- + /* do not change the order of statements for sync with write_inode() */
- + spin_lock(&RFS_I(inode)->write_lock);
- + inode->i_ino = RFS_SB(dir->i_sb)->highest_d_ino;
- + RFS_I(inode)->i_state = RFS_I_FREE;
- + spin_unlock(&RFS_I(inode)->write_lock);
- +
- + insert_inode_hash(inode);
- +
- + /*
- + * we already reigsterd the inode to the transaction
- + * in the above remove_entry()
- + */
- + mark_inode_dirty(inode);
- + rfs_mark_inode_dirty(dir);
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_RFS_IGET4
- +/**
- + * fill up the in-core inode
- + * @param inode created new inode
- + * @param private private argument to fill
- + *
- + * it has a same role with fill_inode
- + */
- +void rfs_read_inode2(struct inode *inode, void *private)
- +{
- + struct rfs_iget4_args *args;
- +
- + args = (struct rfs_iget4_args *) private;
- +
- + fill_inode(inode, args->ep, args->p_start_clu, args->index);
- +}
- +#endif
- +
- +/**
- + * write a inode to dir entry
- + * @param inode inode
- + * @param data_commit flush dirty data and update dir entry's size
- + * @param wait flag whether inode info is flushed
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_sync_inode(struct inode *inode, int data_commit, int wait)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned long backup_ino;
- + int ret = 0;
- + int committed = 0;
- +#ifdef RFS_FOR_2_6
- + int err = 0;
- +#endif
- +
- + if (inode->i_ino == ROOT_INO)
- + return 0;
- +
- + CHECK_RFS_INODE(inode, -EINVAL);
- +
- + /* update size of directory entry except directory */
- + if (!S_ISDIR(inode->i_mode) && data_commit) {
- + DEBUG(DL3, "%u, %u, %u, %llu",
- + RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + RFS_I(inode)->start_clu,
- + inode->i_size);
- +
- +#ifdef RFS_FOR_2_6
- + ret = filemap_fdatawrite(inode->i_mapping);
- + err = filemap_fdatawait(inode->i_mapping);
- + if (err && !ret)
- + ret = err;
- +#else
- + rfs_data_down(RFS_I(inode));
- + ret = fsync_inode_data_buffers(inode);
- + rfs_data_up(RFS_I(inode));
- +#endif
- + if (!ret)
- + committed = 1;
- + }
- +
- + backup_ino = inode->i_ino;
- + ep = get_entry_with_cluster(sb, RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index, &bh);
- + if (IS_ERR(ep))
- + return -EIO;
- +
- + spin_lock(&RFS_I(inode)->write_lock);
- +
- + /*
- + * confirm that inode is not changed by rename/unlink
- + * during getting entry
- + */
- + if (backup_ino != inode->i_ino || RFS_I(inode)->i_state == RFS_I_FREE) {
- + spin_unlock(&RFS_I(inode)->write_lock);
- + brelse(bh);
- + return 0;
- + }
- +
- + /* if data are committed, update entry's size */
- + if (committed)
- + SET32(ep->size, inode->i_size);
- +
- + /* update directory entry's start cluster number */
- + if (RFS_I(inode)->start_clu != CLU_TAIL) {
- + /* sanity check : no case of overwriting non empty file */
- + if ((START_CLUSTER(ep) != 0) &&
- + (START_CLUSTER(ep) != RFS_I(inode)->start_clu)) {
- + spin_unlock(&RFS_I(inode)->write_lock);
- + brelse(bh);
- + RFS_BUG("inode's start cluster is corrupted (%u, %u)\n",
- + START_CLUSTER(ep),
- + RFS_I(inode)->start_clu);
- + return -EIO;
- + }
- +
- + SET16(ep->start_clu_lo, RFS_I(inode)->start_clu);
- + SET16(ep->start_clu_hi, RFS_I(inode)->start_clu >> 16);
- + } else {
- + SET16(ep->start_clu_lo, 0);
- + SET16(ep->start_clu_hi, 0);
- + }
- +
- + /*
- + * Update directory entry's mtime and mdate into inode's mtime
- + * We mistake inode's mtime for current time at RFS-1.2.0 ver
- + * to update directory entry's mtime and mdate
- + */
- + set_entry_time(ep, inode->i_mtime);
- +
- + /*
- + * All changes are done. After unlock, create() on the unlinked entry
- + * can change buffer as it wants
- + */
- + spin_unlock(&RFS_I(inode)->write_lock);
- +
- + if (wait) {
- + mark_buffer_dirty(bh);
- +#ifdef RFS_FOR_2_6
- + err = sync_dirty_buffer(bh);
- + if (err && !ret)
- + ret = err;
- +#else
- + ll_rw_block(WRITE, 1, &bh);
- + wait_on_buffer(bh);
- +#endif
- + } else
- + rfs_mark_buffer_dirty(bh, sb);
- +
- + brelse(bh);
- + return ret;
- +}
- +
- +/**
- + * write inode
- + * @param inode inode
- + * @param wait flag whether inode info is flushed
- + * @return return 0 on success, errno on failure for 2.6
- + */
- +#ifdef RFS_FOR_2_6
- +int rfs_write_inode(struct inode *inode, int wait)
- +#else
- +void rfs_write_inode(struct inode *inode, int wait)
- +#endif
- +{
- + int ret = 0;
- +
- + if (current->flags & PF_MEMALLOC)
- + goto out;
- +
- + /* We return 0 despite teh read-only mode. see ext3_write_inode */
- + if (IS_RDONLY(inode))
- + goto out;
- +
- + if (RFS_I(inode)->i_state == RFS_I_FREE)
- + goto out;
- +
- +#ifdef RFS_FOR_2_4
- + lock_kernel();
- +#endif
- +
- + ret = rfs_sync_inode(inode, 1, wait);
- +
- +#ifdef RFS_FOR_2_4
- + unlock_kernel();
- +#endif
- +
- +out:
- +#ifdef RFS_FOR_2_6
- + return ret;
- +#else
- + return;
- +#endif
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Kconfig linux-2.6.17.14.rfs/fs/rfs/Kconfig
- --- linux-2.6.17.14.orig/fs/rfs/Kconfig 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Kconfig 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,46 @@
- +menu "RFS Filesystem"
- +
- +config RFS_FS
- + tristate "Robust FAT Filesystem (RFS) support"
- + help
- + Samsung Robust File System support.
- +
- +config RFS_VFAT
- + bool "FAT && long file name support"
- + depends on RFS_FS
- +
- +config RFS_SYNC_ON_CLOSE
- + bool "Sync on close support"
- + depends on RFS_FS
- +
- +config RFS_NLS
- + bool "Support NLS on RFS filesystem"
- + select NLS
- + depends on RFS_VFAT
- +
- +config RFS_DEFAULT_CODEPAGE
- + string "Use default NLS codepage"
- + default "cp949"
- + depends on RFS_NLS
- +
- +config RFS_FAT_DEBUG
- + bool "FAT Debug Message support"
- + depends on RFS_FS
- +
- +config RFS_FAT_DEBUG_VERBOSE
- + int "Debugging versosity (0 = quiet, 3 = noisy)"
- + default 0
- + depends on RFS_FAT_DEBUG
- +
- +config RFS_MAPDESTROY
- + bool
- +
- +config RFS_PRE_ALLOC
- + int
- + default 50
- +
- +config RFS_VERSION
- + string
- + default "1.2.2p1-rc2"
- +
- +endmenu
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/log.c linux-2.6.17.14.rfs/fs/rfs/log.c
- --- linux-2.6.17.14.orig/fs/rfs/log.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/log.c 2008-06-19 04:02:08.000000000 +0200
- @@ -0,0 +1,1340 @@
- +/**
- + * @file fs/rfs/log.c
- + * @brief functions for logging
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/bitops.h>
- +#include <linux/rfs_fs.h>
- +#include "rfs.h"
- +#include "log.h"
- +
- +#ifdef CONFIG_RFS_FAT_DEBUG
- +#define CHECK_MUTEX(X, Y) \
- +do { \
- + if ((X) != (Y)) { \
- + RFS_BUG("RFS-log : lock is broken %d " \
- + "(should be %d)\n", \
- + (int)(X), (int)(Y)); \
- + } \
- +} while (0)
- +#else
- +#define CHECK_MUTEX(...) do { } while (0)
- +#endif
- +
- +static int rfs_log_open(struct super_block *sb);
- +static int rfs_make_logfile(struct super_block *sb);
- +static int rfs_log_start_nolock(struct super_block *sb, unsigned int type,
- + struct inode *inode);
- +static int pre_alloc_clusters(struct inode *inode);
- +static int commit_deferred_tr(struct super_block *sb, unsigned long ino);
- +
- +/**
- + * mark buffer dirty & register buffer to the transaction if we are inside transaction
- + * @param bh buffer
- + * @param sb super block
- + */
- +void mark_buffer_tr_dirty(struct buffer_head *bh, struct super_block *sb)
- +{
- +#ifdef RFS_FOR_2_4
- + struct inode *tr_inode = &(RFS_LOG_I(sb)->tr_buf_inode);
- +
- + mark_buffer_dirty(bh);
- +
- + if (RFS_LOG_I(sb) && get_log_lock_owner(sb) == current->pid) {
- + /* inside transaction */
- + buffer_insert_inode_queue(bh, tr_inode);
- + }
- +#endif
- +#ifdef RFS_FOR_2_6
- + mark_buffer_dirty(bh);
- +#endif
- +}
- +
- +/**
- + * mark inode dirty & write inode if we are inside transaction
- + * @param inode inode
- + */
- +void mark_inode_tr_dirty(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- +
- + mark_inode_dirty(inode);
- + if (RFS_LOG_I(sb) && get_log_lock_owner(sb) == current->pid) {
- + /* inside transaction */
- + rfs_sync_inode(inode, 0, 0);
- + }
- +}
- +
- +/**
- + * commit rfs meta-data
- + * @param sb super block
- + */
- +int rfs_meta_commit(struct super_block *sb)
- +{
- +#ifdef RFS_FOR_2_4
- + struct inode *tr_inode = &(RFS_LOG_I(sb)->tr_buf_inode);
- +#endif
- + struct inode *inode = RFS_LOG_I(sb)->inode;
- + int err = 0, ret = 0;
- +
- + /* fat cache sync */
- + fat_lock(sb);
- + rfs_fcache_sync(sb, 0);
- + fat_unlock(sb);
- +
- + /* other transactions except write register dir entry */
- + if (tr_pre_alloc(sb) && inode)
- + ret = rfs_sync_inode(inode, 0, 0);
- +
- + /* rfs meta-data include link path */
- + if (RFS_LOG_I(sb)->type == RFS_LOG_SYMLINK &&
- + (inode = RFS_LOG_I(sb)->symlink_inode)) {
- + err = rfs_sync_inode(inode, 1, 0);
- + if (err && !ret)
- + ret = err;
- + }
- +
- +
- +#ifdef RFS_FOR_2_6
- + err = sync_blockdev(sb->s_bdev);
- +#else
- + err = fsync_inode_buffers(tr_inode);
- +#endif
- + if (err && !ret)
- + ret = err;
- +
- + return ret;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * commit rfs data
- + * @param sb super block
- + * @param wait write and wait for inode?
- + */
- +int rfs_data_commit(struct inode *inode, int wait)
- +{
- + int ret;
- + ret = rfs_sync_inode(inode, 1, wait);
- +
- + return ret;
- +}
- +#endif
- +
- +/**
- + * Does transaction deferredly commit?
- + * @param sb super block
- + * @return if transaction is feasible for deferred commit, then return TRUE,
- + * otherwise return FALSE.
- + */
- +int tr_deferred_commit(struct super_block *sb)
- +{
- + if (RFS_LOG_I(sb)->type == RFS_LOG_WRITE)
- + return TRUE;
- +
- + return FALSE;
- +}
- +
- +/**
- + * Does transaction need pre allocation?
- + * @param sb super block
- + * @return if transaction is feasible for pre-allocation, then return TRUE,
- + * otherwise return FALSE.
- + */
- +int tr_pre_alloc(struct super_block *sb)
- +{
- + if (!RFS_LOG_I(sb)) {
- + /* special case : creation of pool file */
- + return FALSE;
- + }
- +
- + /* must include all tr_deferred_commit() */
- + if (RFS_LOG_I(sb)->type == RFS_LOG_WRITE ||
- + RFS_LOG_I(sb)->type == RFS_LOG_TRUNCATE_F)
- + return TRUE;
- +
- + return FALSE;
- +}
- +
- +/*****************************************************************************/
- +/* log init/exit functions */
- +/*****************************************************************************/
- +/**
- + * init logfile
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +static int init_logfile(struct super_block *sb)
- +{
- + unsigned int i;
- +
- + for (i = 0; i < RFS_LOG_MAX_COUNT; i++) {
- + if (rfs_log_read(sb, i))
- + return -EIO;
- +
- + memset(RFS_LOG(sb), (unsigned char) RFS_LOG_NONE, SECTOR_SIZE);
- + mark_buffer_dirty(RFS_LOG_I(sb)->bh);
- + }
- +
- + brelse(RFS_LOG_I(sb)->bh);
- + RFS_LOG_I(sb)->bh = NULL;
- + RFS_LOG_I(sb)->log = NULL;
- +
- + rfs_sync_vol(sb);
- +
- + return 0;
- +}
- +
- +/**
- + * initialize log
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_init(struct super_block *sb)
- +{
- + int ret;
- +
- + if (sizeof(struct rfs_trans_log) > SECTOR_SIZE) {
- + DPRINTK("RFS-log : Current log record is %u bytes."
- + "Log record must be 512 bytes at most\n",
- + sizeof(struct rfs_trans_log));
- + return -EINVAL;
- + }
- +
- + if ((ret = rfs_log_open(sb)) == -ENOENT) { /* there is no logfile */
- + if ((ret = rfs_make_logfile(sb))) {
- + DPRINTK("RFS-log(%d) : Couldn't make log file."
- + " RFS cannot mount without logfile\n", ret);
- + if (ret == -ENOSPC) {
- + printk(KERN_WARNING "Please check there is"
- + " enough space to create logfile"
- + " (MIN : %u bytes)\n",
- + RFS_LOG_MAX_COUNT * SECTOR_SIZE);
- + }
- + return ret;
- + }
- +
- + /* open again */
- + if ((ret = rfs_log_open(sb))) {
- + /* I/O error */
- + DPRINTK("RFS-log(%d) : Although it was made"
- + " can't open log file\n", ret);
- + return -EIO;
- + }
- +
- + /* initialize log file to prevent confusing garbage value */
- + if ((ret = init_logfile(sb))) {
- + /* I/O error */
- + DPRINTK("RFS-log(%d) : Cannot init logfile\n", ret);
- + return -EIO;
- + }
- + } else if (ret) {
- + /* I/O error */
- + DPRINTK("RFS-log(%d) : Can't open log file\n", ret);
- + return -EIO;
- + } else { /* success in openning log */
- + ret = rfs_log_replay(sb);
- + if (ret) {
- + /* I/O error */
- + DPRINTK("RFS-log(%d) : Fail to replay log\n", ret);
- + return -EIO;
- + }
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * clean up log
- + * @param sb super block
- + * cleanup log info at normal umount
- + */
- +void rfs_log_cleanup(struct super_block *sb)
- +{
- + if (!RFS_LOG_I(sb))
- + return;
- +
- + commit_deferred_tr(sb, 0);
- + brelse(RFS_LOG_I(sb)->bh);
- + kfree(RFS_LOG_I(sb)->log_mutex);
- + kfree(RFS_LOG_I(sb));
- + RFS_SB(sb)->log_info = NULL;
- +}
- +
- +/*****************************************************************************/
- +/* log open function */
- +/*****************************************************************************/
- +/**
- + * open logfile
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +static int rfs_log_open(struct super_block *sb)
- +{
- + struct inode *root_dir;
- + struct inode *inode;
- + struct dentry *root_dentry;
- + struct rfs_dir_entry *ep;
- + struct buffer_head *bh = NULL;
- + struct rfs_log_info *log_info;
- + unsigned int i;
- + unsigned int iblock;
- + int index;
- + int ret = 0;
- +
- + root_dentry = sb->s_root;
- + root_dir = root_dentry->d_inode;
- +
- + index = find_entry(root_dir, RFS_LOG_FILE_NAME, &bh, TYPE_FILE);
- + if (index < 0) {
- + DEBUG(DL0, "can't find log (%d)", index);
- + ret = -ENOENT;
- + goto rel_bh;
- + }
- +
- + ep = get_entry(root_dir, index, &bh);
- + if (IS_ERR(ep)) {
- + /* I/O error */
- + DEBUG(DL0, "can't get entry in root_dir");
- + ret = PTR_ERR(ep);
- + goto rel_bh;
- + }
- +
- + /* get a new inode */
- + if (!(inode = new_inode(root_dir->i_sb))) {
- + /* memory error */
- + DEBUG(DL0, "Can not alloc inode for logfile");
- + ret = -ENOMEM;
- + goto rel_bh;
- + }
- +
- + /* fill inode info */
- + ret = fill_inode(inode, ep, RFS_SB(sb)->root_clu, index);
- + if (ret)
- + goto rel_bh;
- +
- + log_info = kmalloc(sizeof(struct rfs_log_info), GFP_KERNEL);
- + if (!log_info) {
- + /* memory error */
- + DEBUG(DL0, "memory allocation error");
- + ret = -ENOMEM;
- + goto out;
- + }
- +
- + /* log size is 9th power of 2 (512B) */
- + log_info->secs_per_blk = sb->s_blocksize >> 9;
- + log_info->secs_per_blk_bits =
- + (unsigned int) ffs(log_info->secs_per_blk) - 1;
- +
- + DEBUG(DL3, "sectors per block : %u its bit : %u\n",
- + log_info->secs_per_blk, log_info->secs_per_blk_bits);
- +
- + for (i = 0; i < RFS_LOG_MAX_COUNT; i++) {
- + iblock = i >> log_info->secs_per_blk_bits;
- + ret = rfs_bmap(inode, iblock, &(log_info->blocks[i]));
- + if (ret) {
- + kfree(log_info);
- + goto out;
- + }
- + }
- + log_info->type = RFS_LOG_NONE;
- + log_info->sequence = 1;
- + log_info->bh = NULL;
- + log_info->inode = NULL;
- + log_info->isec = 0;
- +
- + /* init fields for deferred commit */
- + log_info->alloc_index = 0;
- + log_info->numof_pre_alloc = 0;
- +
- + log_info->start_cluster = RFS_I(inode)->start_clu;
- +
- + log_info->log_mutex = kmalloc(sizeof(struct rfs_semaphore), GFP_KERNEL);
- + if (!(log_info->log_mutex)) {
- + /* memory error */
- + DEBUG(DL0, "memory allocation failed");
- + kfree(log_info);
- + ret = -ENOMEM;
- + goto out;
- + }
- +
- +#ifdef RFS_FOR_2_4
- + INIT_LIST_HEAD(&(log_info->tr_buf_inode.i_dirty_buffers));
- +#endif
- + RFS_SB(sb)->log_info = (void *) log_info;
- +
- + init_log_lock(sb);
- +out:
- + iput(inode);
- +rel_bh:
- + brelse(bh);
- + return ret;
- +}
- +
- +/*****************************************************************************/
- +/* logging functions */
- +/*****************************************************************************/
- +/**
- + * force to commit write transaction
- + * @param sb super block
- + * @param inode inode for target file or NULL for volume sync
- + * @return 0 on success, errno on failure
- + * @pre It can be called in middle of transaction, so
- + * it should get a lock for committing transaction
- + *
- + * Never sleep holding super lock. Log lock can protects RFS key data
- + */
- +int rfs_log_force_commit(struct super_block *sb, struct inode *inode)
- +{
- + int ret;
- +
- + if (inode && inode != RFS_LOG_I(sb)->inode)
- + return 0;
- +
- + lock_log(sb);
- +
- + if (!inode) {
- + unlock_super(sb);
- + ret = commit_deferred_tr(sb, 0);
- + unlock_log(sb);
- + lock_super(sb);
- + } else {
- + ret = commit_deferred_tr(sb, inode->i_ino);
- + unlock_log(sb);
- + }
- +
- + return ret;
- +}
- +
- +/**
- + * release remaining pre-allocations
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +static int release_pre_alloc(struct super_block *sb)
- +{
- + unsigned int i;
- + int ret = 0;
- +
- + if (RFS_LOG_I(sb)->alloc_index >= RFS_LOG_I(sb)->numof_pre_alloc)
- + goto out;
- +
- + fat_lock(sb);
- + if (RFS_LOG_I(sb)->where == RFS_POOL) {
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- + struct rfs_log_info *rli = RFS_LOG_I(sb);
- + unsigned int value;
- +
- + for (i = rli->alloc_index;
- + i < rli->numof_pre_alloc - 1; i++) {
- + ret = fat_read(sb, rli->pre_alloc_clus[i],
- + &value);
- + if (ret) {
- + printk("can not read fat table\n");
- + BUG();
- + }
- +
- + if (value != rli->pre_alloc_clus[i + 1]) {
- + printk("free cluster from fat is not zero\n");
- + BUG();
- + }
- + }
- +#endif
- +
- + for (i = RFS_LOG_I(sb)->alloc_index;
- + i < RFS_LOG_I(sb)->numof_pre_alloc; i++) {
- + ret = fat_write(sb, RFS_LOG_I(sb)->pre_alloc_clus[i],
- + CLU_FREE);
- + if (ret)
- + break;
- +
- + DEBUG(DL3, "chained cluster %u freed",
- + RFS_LOG_I(sb)->pre_alloc_clus[i]);
- + }
- + } else { /* RFS_LOG_I(sb)->where == RFS_FAT_TABLE */
- + /* pre-allocated clusters had CLU_FREE */
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- + struct rfs_log_info *rli = RFS_LOG_I(sb);
- + unsigned int value;
- +
- + for (i = rli->alloc_index;
- + i < rli->numof_pre_alloc; i++) {
- + ret = fat_read(sb, rli->pre_alloc_clus[i],
- + &value);
- + if (ret) {
- + printk("can not read fat table\n");
- + BUG();
- + }
- + if (value != CLU_FREE) {
- + printk("free cluster from fat is not zero\n");
- + BUG();
- + }
- + }
- +#endif
- + i = RFS_LOG_I(sb)->numof_pre_alloc;
- + RFS_SB(sb)->search_ptr = RFS_LOG_I(sb)->pre_alloc_clus[
- + RFS_LOG_I(sb)->alloc_index];
- + }
- +
- + if (i != RFS_LOG_I(sb)->numof_pre_alloc)
- + DPRINTK("RFS-log : critical error!! FAT chain is crashed\n");
- +
- + fat_unlock(sb);
- +out:
- + RFS_LOG_I(sb)->alloc_index = RFS_LOG_I(sb)->numof_pre_alloc = 0;
- + return ret;
- +}
- +
- +/**
- + * commit deferred transaction for pre-allocation
- + * @param sb super block
- + * @param ino inode number
- + * @return 0 on success, errno on failure
- + */
- +static int commit_deferred_tr(struct super_block *sb, unsigned long ino)
- +{
- + int ret = 0;
- +
- + if (tr_deferred_commit(sb) &&
- + (!ino || (RFS_LOG_I(sb)->inode && (RFS_LOG_I(sb)->inode->i_ino == ino)))) {
- + ret = release_pre_alloc(sb);
- + if (ret) /* I/O error */
- + return ret;
- +
- + ret = rfs_meta_commit(sb);
- + if (ret)
- + return ret;
- +
- + DEBUG(DL3, "where pre clusters are : %d", RFS_LOG_I(sb)->where);
- + if (rfs_log_mark_end(sb, RFS_SUBLOG_COMMIT)) {
- + /* I/O error */
- + DPRINTK("RFS-log : Couldn't commit write"
- + " transaction\n");
- + return -EIO;
- + }
- + sb->s_dirt = 0;
- + }
- +
- + return ret;
- +}
- +
- +/**
- + * get a lock and mark start of transaction
- + * @param sb super block
- + * @param log_type log type
- + * @param inode relative inode
- + * @return 0 on success, errno on failure
- + * @post locking log has been remained until trasaction ends
- + *
- + * pre-allocation is commited if there is no further write to the same file
- + * as that of previous transaction
- + */
- +int rfs_log_start(struct super_block *sb, unsigned int log_type,
- + struct inode *inode)
- +{
- + int ret;
- +
- + /*
- + * Exception handling : Inode for root can be clear
- + * if mounting rfs is failed during initialization of log,
- + * and then rfs_delete_inode is called.
- + * So, logging for delete_inode must handle this exceptional case.
- + */
- + if (!RFS_LOG_I(sb)) {
- + if (log_type == RFS_LOG_DEL_INODE) {
- + return 0;
- + } else {
- + RFS_BUG("RFS-log : System crashed\n");
- + return -EIO;
- + }
- + }
- +
- + /* check nested transaction */
- + /* Any process cannot set owner to own pid without a mutex */
- + if (lock_log(sb) >= 2) {
- + /* recursive acquisition */
- + return 0;
- + }
- +
- + if (tr_deferred_commit(sb)) {
- + if ((log_type == RFS_LOG_I(sb)->type) &&
- + (RFS_LOG_I(sb)->inode == inode)) {
- + /* write data in the same file */
- + return 0;
- + }
- + /* different transaction; flush prev write-transaction */
- + DEBUG(DL2, "prev : write, cur : %d, ino : %lu", log_type, inode->i_ino);
- + ret = commit_deferred_tr(sb, RFS_LOG_I(sb)->inode->i_ino);
- + if (ret) /* I/O error */
- + goto err;
- + }
- +
- +
- + if (rfs_log_start_nolock(sb, log_type, inode)) {
- + DPRINTK("RFS-log : Couldn't start log\n");
- + ret = -EIO;
- + goto err;
- + }
- +
- + return 0;
- +err:
- + unlock_log(sb);
- +
- + return ret;
- +}
- +
- +/**
- + * mark end of transaction and release lock
- + * @param sb super block
- + * @param result result of transaction (0 : success, others : failure)
- + * @return 0 on success, errno on failure
- + * @pre trasaction must have started
- + *
- + * mark EOT and flush it unless transaction is for pre-allocation.
- + */
- +int rfs_log_end(struct super_block *sb, int result)
- +{
- + unsigned int sub_type = RFS_SUBLOG_COMMIT;
- + int ret = 0;
- +
- + /*
- + * Exception handling : Filtered all faulty transactions which
- + * start without log info except for delete_inode, which
- + * must be handled.
- + * Please reference a comment in rfs_log_start().
- + */
- + if (!RFS_LOG_I(sb))
- + return 0;
- +
- + /* recursive lock */
- + /* Any process cannot increase ref_count without a mutex */
- + if (get_log_lock_depth(sb) > 1) {
- + unlock_log(sb);
- + DEBUG(DL2, "release self recursive lock (ref: %d)",
- + get_log_lock_depth(sb));
- + return 0;
- + }
- +
- + /* transaction did not change any meta data */
- + if (is_empty_tr(sb)) {
- + DEBUG(DL3, "empty transaction");
- +
- + brelse(RFS_LOG_I(sb)->bh);
- + RFS_LOG_I(sb)->bh = NULL;
- + RFS_LOG_I(sb)->log = NULL;
- + RFS_LOG_I(sb)->type = RFS_LOG_NONE;
- +
- + goto rel_lock;
- + }
- +
- + DEBUG(DL2, "result : %d, type : %d", result, RFS_LOG_I(sb)->type);
- + if (result)
- + sub_type = RFS_SUBLOG_ABORT;
- +
- + if (tr_deferred_commit(sb) && (sub_type == RFS_SUBLOG_COMMIT)) {
- + DEBUG(DL2, "deferred commit");
- + ret = 0;
- + sb->s_dirt = 1;
- + goto rel_lock;
- + }
- +
- + if (tr_pre_alloc(sb)) {
- + ret = release_pre_alloc(sb);
- + if (ret) /* I/O error */
- + goto rel_lock;
- + }
- +
- + ret = rfs_meta_commit(sb);
- + if (ret)
- + goto rel_lock;
- +
- + ret = rfs_log_mark_end(sb, sub_type);
- +
- +rel_lock:
- + CHECK_MUTEX(get_log_lock_depth(sb), 1);
- +
- + unlock_log(sb);
- +
- + return ret;
- +}
- +
- +/**
- + * logging for entry to be built
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param numof_entries the number of entries
- + * @return 0 on success, errno on failure
- + * @pre trasaction must have been started
- + */
- +int rfs_log_build_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int numof_entries)
- +{
- + if (!RFS_LOG_I(sb)) {
- + /* special case : pool file is created at format */
- + return 0;
- + }
- +
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + SET32(RFS_LOG(sb)->type, MK_LOG_TYPE(RFS_LOG_I(sb)->type,
- + RFS_SUBLOG_BUILD_ENTRY));
- + SET32(RFS_LOG(sb)->log_entry.pdir, pdir);
- + SET32(RFS_LOG(sb)->log_entry.entry, entry);
- + SET32(RFS_LOG(sb)->log_entry.numof_entries, numof_entries);
- +
- + DEBUG(DL2, "pdir : %u, entry : %u, numof_entries : %u",
- + pdir, entry, numof_entries);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +
- +/**
- + * logging for entry to be removed
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param numof_entries the number of entries
- + * @param undel_buf the original value at position of deletion mark
- + * @return 0 on success, errno on failure
- + * @pre trasaction must have been started
- + */
- +int rfs_log_remove_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int numof_entries,
- + unsigned char *undel_buf)
- +{
- + unsigned int i;
- +
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + SET32(RFS_LOG(sb)->type, MK_LOG_TYPE(RFS_LOG_I(sb)->type,
- + RFS_SUBLOG_REMOVE_ENTRY));
- + SET32(RFS_LOG(sb)->log_entry.pdir, pdir);
- + SET32(RFS_LOG(sb)->log_entry.entry, entry);
- + SET32(RFS_LOG(sb)->log_entry.numof_entries, numof_entries);
- + for (i = 0; i < numof_entries; i++)
- + RFS_LOG(sb)->log_entry.undel_buf[i] = undel_buf[i];
- +
- + DEBUG(DL2, "pdir : %u, entry : %u, numof_entries : %u",
- + pdir, entry, numof_entries);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * logging for entry to be updated
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param from_size the original size used to undo
- + * @param to_size the changed size used to redo
- + * @return 0 on success, errno on failure
- + * @pre trasaction must have been started
- + */
- +int rfs_log_update_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int from_size,
- + unsigned int to_size)
- +{
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + SET32(RFS_LOG(sb)->type, MK_LOG_TYPE(RFS_LOG_I(sb)->type,
- + RFS_SUBLOG_UPDATE_ENTRY));
- + SET32(RFS_LOG(sb)->log_entry.pdir, pdir);
- + SET32(RFS_LOG(sb)->log_entry.entry, entry);
- + SET32(RFS_LOG(sb)->log_entry.from_size, from_size);
- + SET32(RFS_LOG(sb)->log_entry.to_size, to_size);
- +
- + DEBUG(DL2, "pdir : %u, entry : %u, from : %u, to : %u",
- + pdir, entry, from_size, to_size);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +#endif
- +
- +/**
- + * logging for updation of chain
- + * @param sb super block
- + * @param subtype sub transaction
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param pool_next next cluster in pool related to chain
- + * @param pool_prev previous cluster in pool related to chain
- + * @param pool_next next cluster in pool related to chain
- + * @param target_prev previous cluster in file or pool related to chain
- + * @param target_next next cluster in file or pool related to chain
- + * @param numof_clus the number of clusters in chain
- + * @param clus the clusters in chain
- + */
- +static void rfs_log_update_chain(struct super_block *sb, unsigned int subtype,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus)
- +{
- + unsigned int i;
- +
- + SET32(RFS_LOG(sb)->type, MK_LOG_TYPE(RFS_LOG_I(sb)->type, subtype));
- + DEBUG(DL2, "SUBTYPE : %u", subtype);
- +
- + SET32(RFS_LOG(sb)->log_fat.pdir, pdir);
- + SET32(RFS_LOG(sb)->log_fat.entry, entry);
- +
- + SET32(RFS_LOG(sb)->log_fat.p_last_clu, RFS_POOL_I(sb)->last_cluster);
- + SET32(RFS_LOG(sb)->log_fat.p_num_clus, RFS_POOL_I(sb)->num_clusters);
- + SET32(RFS_LOG(sb)->log_fat.p_c_start_clu,
- + RFS_POOL_I(sb)->c_start_cluster);
- + SET32(RFS_LOG(sb)->log_fat.p_c_last_clu,
- + RFS_POOL_I(sb)->c_last_cluster);
- + DEBUG(DL2, "POOL_INFO : <%u, %u>, <%u, %u>",
- + RFS_POOL_I(sb)->last_cluster,
- + RFS_POOL_I(sb)->num_clusters,
- + RFS_POOL_I(sb)->c_start_cluster,
- + RFS_POOL_I(sb)->c_last_cluster);
- +
- + SET32(RFS_LOG(sb)->log_fat.p_prev_clu, pool_prev);
- + SET32(RFS_LOG(sb)->log_fat.p_next_clu, pool_next);
- + DEBUG(DL2, "POOL_CHAIN : <%u, %u>", pool_prev, pool_next);
- +
- + SET32(RFS_LOG(sb)->log_fat.t_prev_clu, target_prev);
- + SET32(RFS_LOG(sb)->log_fat.t_next_clu, target_next);
- + DEBUG(DL2, "TARGET_CHAIN : <%u, %u>", target_prev, target_next);
- +
- + SET32(RFS_LOG(sb)->log_fat.numof_clus, numof_clus);
- + for (i = 0; i < numof_clus; i++) {
- + DEBUG(DL3, "%u, ", clus[i]);
- + SET32(RFS_LOG(sb)->log_fat.clus[i], clus[i]);
- + }
- +
- + DEBUG(DL2, "NUMOF_LOG_CLU : <%u>", numof_clus);
- +}
- +
- +/**
- + * logging alloc chain from fat table or pool
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param pool_prev previous cluster of detached chain in pool
- + * @param pool_next next cluster of detached chain in pool
- + * @param target_prev previous cluster of extented chain in file
- + * @param target_next next cluster of extented chain in file
- + * @param numof_clus the number of clusters in chain
- + * @param clus the clusters in chain
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_alloc_chain(struct super_block *sb,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus)
- +{
- + unsigned int subtype;
- + int ret;
- +
- + if (!RFS_LOG_I(sb)) {
- + /* special case : poolfile is created
- + before logfile at format */
- + return 0;
- + }
- +
- + if ((!is_empty_tr(sb)) && tr_pre_alloc(sb)) {
- + struct inode *inode = RFS_LOG_I(sb)->inode;
- + unsigned int type = RFS_LOG_I(sb)->type;
- +
- + /* commit pre-allocation windows */
- + ret = rfs_meta_commit(sb);
- + if (ret)
- + return ret;
- +
- + if (rfs_log_mark_end(sb, RFS_SUBLOG_COMMIT))
- + return -EIO;
- +
- + if (rfs_log_start_nolock(sb, type, inode))
- + return -EIO;
- + }
- +
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + subtype = (pool_prev == CLU_TAIL) ? RFS_SUBLOG_ALLOC_CHAIN :
- + RFS_SUBLOG_GET_CHAIN;
- +
- + rfs_log_update_chain(sb, subtype, pdir, entry, pool_prev, pool_next,
- + target_prev, target_next, numof_clus, clus);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * logging dealloc chain to fat table or pool
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param pool_prev previous cluster of chain to be attached in pool
- + * @param pool_next next cluster of chain to be attached in pool
- + * @param target_prev previous cluster of free-chain in file
- + * @param target_next next cluster of free-chain in file
- + * @param numof_clus the number of clusters in chain
- + * @param clus the clusters in chain
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_free_chain(struct super_block *sb,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus)
- +{
- + unsigned int subtype;
- +
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + subtype = (pool_prev == CLU_TAIL) ? RFS_SUBLOG_FREE_CHAIN :
- + RFS_SUBLOG_PUT_CHAIN;
- +
- + rfs_log_update_chain(sb, subtype, pdir, entry, pool_prev, pool_next,
- + target_prev, target_next, numof_clus, clus);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +#endif
- +
- +/**
- + * logging move candidate segment to deleted partition in pool
- + * @param sb super block
- + * @param pdir the start cluster of parent
- + * @param entry entry index
- + * @param pool_prev previous cluster of chain to be moved in pool
- + * @param pool_next next cluster of chain to be moved in pool
- + * @param target_prev previous cluster of segment(chain)
- + * @param target_next next cluster of segment(chain)
- + * @param numof_clus the number of clusters in chain
- + * @param clus the clusters in chain
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_move_chain(struct super_block *sb,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus)
- +{
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + rfs_log_update_chain(sb, RFS_SUBLOG_MOVE_CHAIN, pdir, entry,
- + pool_prev, pool_next, target_prev, target_next,
- + numof_clus, clus);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +
- +/**
- + * logging updation of pool file
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_update_pool(struct super_block *sb)
- +{
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + rfs_log_update_chain(sb, RFS_SUBLOG_UPDATE_POOL, CLU_TAIL, -1,
- + CLU_TAIL, CLU_TAIL, CLU_TAIL, CLU_TAIL, 0, NULL);
- +
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + return 0;
- +}
- +
- +/**
- + * logging start of transaction without a lock
- + * @param sb super block
- + * @param type log type
- + * @param inode modified inode
- + * @return 0 on success, errno on failure
- + */
- +static int rfs_log_start_nolock(struct super_block *sb, unsigned int type,
- + struct inode *inode)
- +{
- + extern int sanity_check_pool(struct super_block *);
- +
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- + extern int sanity_check_pool(struct super_block *sb);
- +
- + sanity_check_pool(sb);
- +#endif
- +
- +#ifdef _RFS_INTERNAL_SANITY_CHECK
- + sanity_check_pool(sb);
- +#endif
- +
- + RFS_LOG_I(sb)->type = type;
- + RFS_LOG_I(sb)->inode = inode;
- + RFS_LOG_I(sb)->dirty = FALSE;
- +
- + return 0;
- +}
- +
- +/**
- + * mark an end of transaction
- + * @param sb super block
- + * @param sub_type commit or abort
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_mark_end(struct super_block *sb, unsigned int sub_type)
- +{
- + if (rfs_log_read(sb, RFS_LOG_I(sb)->isec))
- + return -EIO;
- +
- + set_bit(BH_RFS_LOG_COMMIT, &(RFS_LOG_I(sb)->bh->b_state));
- +
- + SET32(RFS_LOG(sb)->type, MK_LOG_TYPE(RFS_LOG_I(sb)->type, sub_type));
- + if (rfs_log_write(RFS_LOG_I(sb)))
- + return -EIO;
- +
- + RFS_LOG_I(sb)->inode = NULL;
- + RFS_LOG_I(sb)->type = RFS_LOG_NONE;
- +
- + /* destroy stl mapping */
- + if (IS_XSR(sb->s_dev))
- + rfs_map_destroy(sb);
- +
- + return 0;
- +}
- +
- +/*****************************************************************************/
- +/* log read / write functions */
- +/*****************************************************************************/
- +/**
- + * read log
- + * @param sb super block
- + * @param isec logical sector to read
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_read(struct super_block *sb, unsigned int isec)
- +{
- + struct buffer_head *bh;
- + int state = BH_RFS_LOG;
- + int sec_off;
- +
- + if (RFS_LOG_I(sb)->bh)
- + brelse(RFS_LOG_I(sb)->bh);
- +
- + /* rotational logfile */
- + if (isec >= RFS_LOG_MAX_COUNT) {
- + RFS_BUG("RFS-log : can't read log record\n");
- + return -EIO;
- + }
- +
- + DEBUG(DL3, "read log [%dth : 0x%x]\n", isec, (unsigned int) RFS_LOG_I(sb)->sequence);
- +
- + if (is_empty_tr(sb))
- + state = BH_RFS_LOG_START;
- +
- + bh = rfs_bread(sb, RFS_LOG_I(sb)->blocks[isec], state);
- + if (!bh) {
- + RFS_BUG("RFS-log : Couldn't read log\n");
- + return -EIO;
- + }
- +
- + RFS_LOG_I(sb)->bh = bh;
- +
- + /* point next log record */
- + if (isec == RFS_LOG_MAX_COUNT - 1)
- + RFS_LOG_I(sb)->isec = 0;
- + else
- + RFS_LOG_I(sb)->isec = isec + 1;
- +
- + sec_off = isec & (RFS_LOG_I(sb)->secs_per_blk - 1);
- + RFS_LOG_I(sb)->log = (struct rfs_trans_log *) ((bh->b_data) +
- + (sec_off << SECTOR_BITS));
- +
- + return 0;
- +}
- +
- +/**
- + * write log to logfile and release it
- + * @param log_info rfs's log structure
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_write(struct rfs_log_info *log_info)
- +{
- + int ret = 0;
- +
- + if (!log_info->bh) {
- + RFS_BUG("RFS-log : No buffer head for log\n");
- + return -EIO;
- + }
- +
- + SET64(log_info->log->sequence, (log_info->sequence)++);
- + mark_buffer_dirty(log_info->bh);
- +#ifdef RFS_FOR_2_6
- + ret = sync_dirty_buffer(log_info->bh);
- +#else
- + ll_rw_block(WRITE, 1, &(log_info->bh));
- + wait_on_buffer(log_info->bh);
- +#endif
- +
- + brelse(log_info->bh);
- + log_info->bh = NULL;
- + log_info->log = NULL;
- + log_info->dirty = TRUE;
- +
- + return ret;
- +}
- +
- +/*****************************************************************************/
- +/* misc. */
- +/*****************************************************************************/
- +/**
- + * make logfile
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + */
- +static int rfs_make_logfile(struct super_block *sb)
- +{
- + struct inode tmp_inode;
- + struct inode *root_dir;
- + struct dentry *root_dentry;
- + struct rfs_dir_entry *ep;
- + struct buffer_head *bh = NULL;
- + unsigned int file_size;
- + unsigned int numof_clus;
- + unsigned int *clus;
- + unsigned int clu = 0;
- + unsigned int i;
- + unsigned int index;
- + int ret;
- +
- + root_dentry = sb->s_root;
- + root_dir = root_dentry->d_inode;
- +
- + /* scan enough space to create log file */
- + file_size = RFS_LOG_MAX_COUNT * SECTOR_SIZE;
- + numof_clus = file_size >> RFS_SB(sb)->cluster_bits;
- + if (file_size & (RFS_SB(sb)->cluster_size - 1))
- + numof_clus++;
- +
- + DEBUG(DL3, "file_size : %u, numof_clus : %u\n", file_size, numof_clus);
- + DEBUG(DL3, "sector_size : %lu, cluster_size : %u", sb->s_blocksize,
- + RFS_SB(sb)->cluster_size);
- +
- + clus = kmalloc(numof_clus * sizeof(unsigned int), GFP_KERNEL);
- + if (!clus)
- + return -ENOMEM;
- +
- + for (i = 0; i < numof_clus; i++) {
- + ret = find_free_cluster(root_dir, &clu);
- + if (ret) {
- + kfree(clus);
- + return -EACCES;
- + }
- + clus[i] = clu;
- + }
- +
- + if (i < numof_clus) { /* insufficient space */
- + kfree(clus);
- + return -ENOSPC;
- + }
- +
- + ret = fat_write(sb, clus[0], CLU_TAIL);
- + if (ret) {
- + kfree(clus);
- + return ret;
- + }
- +
- + index = build_entry(root_dir, NULL, clus[0], TYPE_FILE,
- + RFS_LOG_FILE_NAME);
- + if ((int) index < 0) {
- + kfree(clus);
- + return index;
- + }
- +
- + ep = get_entry(root_dir, index, &bh);
- + if (IS_ERR(ep)) {
- + kfree(clus);
- + brelse(bh);
- + return PTR_ERR(ep);
- + }
- +
- + /* set tmp_inode for log file */
- + tmp_inode.i_sb = sb;
- + RFS_I(&tmp_inode)->start_clu = clus[0];
- +
- + /* allocate enough clusters */
- + for (i = 1; i < numof_clus; i++) {
- + ret = append_new_cluster(&tmp_inode, clus[i - 1], clus[i]);
- + if (ret) {
- + kfree(clus);
- + brelse(bh);
- + return ret;
- + }
- + }
- +
- + ep->attr |= (ATTR_READONLY | ATTR_SYSTEM | ATTR_HIDDEN);
- + SET32(ep->size, file_size);
- + mark_buffer_dirty(bh);
- + brelse(bh);
- +
- + kfree(clus);
- + return 0;
- +}
- +
- +/**
- + * return a pre-allocated cluster
- + * @param inode inode of file to be extended
- + * @param new_clu out-var to save free cluster number
- + * @return 0 on success, errno on failure
- + */
- +int rfs_log_get_cluster(struct inode *inode, unsigned int *new_clu)
- +{
- + struct super_block *sb = inode->i_sb;
- + int ret;
- +
- + /* set default invalid value */
- + *new_clu = CLU_TAIL;
- +
- + if (RFS_LOG_I(sb)->alloc_index >= RFS_LOG_I(sb)->numof_pre_alloc) {
- + ret = pre_alloc_clusters(inode);
- + if (ret)
- + return ret;
- + }
- +
- + if (likely(RFS_LOG_I(sb)->alloc_index <
- + RFS_LOG_I(sb)->numof_pre_alloc)) {
- + *new_clu = RFS_LOG_I(sb)->pre_alloc_clus[
- + (RFS_LOG_I(sb)->alloc_index)++];
- + } else {
- + RFS_BUG("RFS-log : pre-allocation corruption\n");
- + return -EIO;
- + }
- +
- + DEBUG(DL3, "alloc_cluster : %u", *new_clu);
- + return 0;
- +}
- +
- +/**
- + * do pre-allocation
- + * @param inode inode of file to be written
- + * @return 0 on success, errno on failure
- + *
- + * pre-allocate clusters and save them in log buffer
- + */
- +static int pre_alloc_clusters(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- + unsigned int count = 0;
- + unsigned int p_prev_clu, p_next_clu;
- + unsigned int t_next_clu = NOT_ASSIGNED;
- + unsigned int content = NOT_ASSIGNED;
- + unsigned int i;
- + int err;
- +
- + /* first, find free clusters in free chain pool */
- + err = rfs_shrink_pool_chain(sb, RFS_LOG_I(sb)->pre_alloc_clus,
- + RFS_LOG_PRE_ALLOC, &count, &p_next_clu);
- + if (err)
- + return err;
- +
- + p_prev_clu = RFS_POOL_I(sb)->start_cluster;
- + RFS_LOG_I(sb)->where = RFS_POOL;
- +
- + /* if there are no free clusters in pool file,
- + find free clusters in fat table */
- + if (!count) {
- + for (i = VALID_CLU; i < sbi->num_clusters; i++,
- + sbi->search_ptr++) {
- + /* search free cluster from hint(search_ptr) */
- + if (sbi->search_ptr >= sbi->num_clusters)
- + sbi->search_ptr = VALID_CLU;
- +
- + err = fat_read(sb, sbi->search_ptr, &content);
- + if (err)
- + return err;
- +
- + if (content == CLU_FREE) {
- + RFS_LOG_I(sb)->pre_alloc_clus[count++] =
- + sbi->search_ptr;
- +
- + if (count >= RFS_LOG_PRE_ALLOC)
- + break;
- + }
- + }
- + if (!count)
- + return -ENOSPC;
- +
- + p_prev_clu = p_next_clu = CLU_TAIL;
- + RFS_LOG_I(sb)->where = RFS_FAT_TABLE;
- + }
- + RFS_LOG_I(sb)->alloc_index = 0;
- + RFS_LOG_I(sb)->numof_pre_alloc = count;
- +
- + /* in case of unlinked inode, t_next_clu is the head of other chain */
- + if (RFS_I(inode)->last_clu != CLU_TAIL) {
- + err = fat_read(sb, RFS_I(inode)->last_clu, &t_next_clu);
- + if (err)
- + return err;
- + } else
- + t_next_clu = CLU_TAIL;
- +
- + if ((err = rfs_log_alloc_chain(sb, RFS_I(inode)->p_start_clu,
- + RFS_I(inode)->index,
- + p_prev_clu, p_next_clu,
- + RFS_I(inode)->last_clu, t_next_clu,
- + RFS_LOG_I(sb)->numof_pre_alloc,
- + RFS_LOG_I(sb)->pre_alloc_clus))) {
- + return err;
- + }
- +
- + if (RFS_LOG_I(sb)->where == RFS_POOL) {
- + if ((err = rfs_get_pool(sb, p_next_clu, count)))
- + return err;
- + }
- +
- + return 0;
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/log.h linux-2.6.17.14.rfs/fs/rfs/log.h
- --- linux-2.6.17.14.orig/fs/rfs/log.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/log.h 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,254 @@
- +/**
- + * @file fs/rfs/log.h
- + * @brief header file for log
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#ifndef _LINUX_RFS_LOG_H
- +#define _LINUX_RFS_LOG_H
- +
- +#ifdef __KERNEL__
- +#include <linux/sched.h>
- +#include <linux/rfs_fs.h>
- +#else
- +#define u8 unsigned char
- +#define u16 unsigned short
- +#define u32 unsigned int
- +#define u64 unsigned long long
- +#endif
- +
- +/***************************************************************************/
- +/* RFS Common definitions */
- +/***************************************************************************/
- +/* max value is set with extra space */
- +#define RFS_LOG_MAX_ENTRIES 464
- +#define RFS_LOG_MAX_CLUSTERS 112
- +
- +
- +#if defined(CONFIG_RFS_PRE_ALLOC) && (CONFIG_RFS_PRE_ALLOC < RFS_LOG_MAX_CLUSTERS)
- +#define RFS_LOG_PRE_ALLOC CONFIG_RFS_PRE_ALLOC
- +#else
- +#define RFS_LOG_PRE_ALLOC RFS_LOG_MAX_CLUSTERS
- +#endif
- +
- +/* logfile used for unlink */
- +#define RFS_LOG_FILE_NAME "$RFS_LOG.LO$"
- +
- +#define RFS_LOG_FILE_LEN 12
- +
- +/***************************************************************************/
- +/* RFS dependant definitions */
- +/***************************************************************************/
- +#define RFS_LOG_MAX_COUNT 256
- +
- +/* transaction type */
- +#define RFS_LOG_NONE 0x0000
- +#define RFS_LOG_CREATE 0x0001
- +#define RFS_LOG_RENAME 0x0002
- +#define RFS_LOG_WRITE 0x0003
- +#define RFS_LOG_TRUNCATE_F 0x0004
- +#define RFS_LOG_TRUNCATE_B 0x0005
- +#define RFS_LOG_UNLINK 0x0006
- +#define RFS_LOG_DEL_INODE 0x0007
- +#define RFS_LOG_SYMLINK 0x0008
- +#define RFS_LOG_REPLAY 0x0010
- +#define RFS_LOG_INVALID 0xffff
- +
- +/* sub transaction type */
- +#define RFS_SUBLOG_COMMIT 0x0000
- +#define RFS_SUBLOG_ABORT 0x0001
- +#define RFS_SUBLOG_START 0x0002
- +#define RFS_SUBLOG_ALLOC_CHAIN 0x0003
- +#define RFS_SUBLOG_BUILD_ENTRY 0x0005
- +#define RFS_SUBLOG_REMOVE_ENTRY 0x0006
- +#define RFS_SUBLOG_UPDATE_ENTRY 0x0007
- +#define RFS_SUBLOG_GET_CHAIN 0x0009
- +#define RFS_SUBLOG_MOVE_CHAIN 0x000B
- +#define RFS_SUBLOG_UPDATE_POOL 0x000C
- +
- +
- +/* logtype specific MACRO */
- +#define MK_LOG_TYPE(X, Y) (((X) << 16) | (Y))
- +#define GET_LOG(X) ((X) >> 16)
- +#define GET_SUBLOG(X) ((X) & 0x0000ffff)
- +
- +/* where pre-alloction happens */
- +#define RFS_POOL 1
- +#define RFS_FAT_TABLE 2
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +typedef u32 rfs_log_seq_t;
- +#else
- +typedef u64 rfs_log_seq_t;
- +#endif
- +
- +struct rfs_log_entry {
- + u32 pdir;
- + u32 entry;
- + u32 numof_entries;
- + u32 from_size;
- + u32 to_size;
- + u8 undel_buf[RFS_LOG_MAX_ENTRIES];
- +};
- +
- +struct rfs_log_fat {
- + /* two fields for minimal fat entry infos */
- + u32 pdir;
- + u32 entry;
- +
- + /* four fields for pool contents */
- + u32 p_last_clu;
- + u32 p_num_clus;
- + u32 p_c_start_clu;
- + u32 p_c_last_clu;
- +
- + /* two fields for pool chain */
- + u32 p_prev_clu;
- + u32 p_next_clu;
- +
- + /* two fields for target chain */
- + u32 t_prev_clu;
- + u32 t_next_clu;
- +
- + u32 numof_clus;
- + u32 clus[RFS_LOG_MAX_CLUSTERS];
- +};
- +
- +struct rfs_trans_log {
- + u32 type;
- + union {
- + rfs_log_seq_t sequence;
- + u64 dummy; /* place holder for compatibility */
- + };
- + union {
- + struct rfs_log_fat log_fat;
- + struct rfs_log_entry log_entry;
- + };
- +} __attribute__ ((packed));
- +
- +#ifdef __KERNEL__
- +struct rfs_log_info {
- + unsigned long blocks[RFS_LOG_MAX_COUNT];
- + unsigned int secs_per_blk;
- + unsigned int secs_per_blk_bits;
- + unsigned int isec;
- + unsigned int type;
- + rfs_log_seq_t sequence;
- + int dirty;
- +
- + void *log_mutex;
- +
- + unsigned int numof_pre_alloc;
- + unsigned int alloc_index;
- + unsigned int pre_alloc_clus[RFS_LOG_MAX_CLUSTERS];
- + int where;
- +
- + struct inode *inode; /* target file */
- + struct buffer_head *bh;
- + struct rfs_trans_log *log;
- +
- + unsigned int start_cluster; /* for logfile itself */
- +#ifdef RFS_FOR_2_4
- + struct inode tr_buf_inode; /* in order to link transaction dirty buffers */
- +#endif
- + struct inode *symlink_inode; /* in order to point the symlink inode */
- +};
- +
- +/* get rfs log info */
- +static inline struct rfs_log_info *RFS_LOG_I(struct super_block *sb)
- +{
- + return (struct rfs_log_info *)(RFS_SB(sb)->log_info);
- +}
- +
- +/* get rfs log */
- +static inline struct rfs_trans_log *RFS_LOG(struct super_block *sb)
- +{
- + return ((struct rfs_log_info *)(RFS_SB(sb)->log_info))->log;
- +}
- +
- +static inline void rfs_sync_bh(struct buffer_head *bh)
- +{
- + if (!bh)
- + return;
- +
- + if (buffer_dirty(bh)) {
- + ll_rw_block(WRITE, 1, &bh);
- + wait_on_buffer(bh);
- + } else if (buffer_locked(bh))
- + wait_on_buffer(bh);
- +}
- +
- +static inline int is_empty_tr(struct super_block *sb)
- +{
- + if (!(RFS_LOG_I(sb)->dirty))
- + return 1;
- +
- + return 0;
- +}
- +
- +static inline int tr_in_replay(struct super_block *sb)
- +{
- + if (RFS_LOG_I(sb)->type == RFS_LOG_REPLAY)
- + return 1;
- + return 0;
- +}
- +
- +/* called by FAT */
- +int rfs_log_init(struct super_block *sb);
- +int rfs_log_start(struct super_block *sb, unsigned int log_type,
- + struct inode *inode);
- +int rfs_log_end(struct super_block *sb, int result);
- +int rfs_log_build_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int numof_entries);
- +int rfs_log_remove_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int numof_entries,
- + unsigned char *undel_buf);
- +int rfs_log_update_entry(struct super_block *sb, unsigned int pdir,
- + unsigned int entry, unsigned int from_size,
- + unsigned int to_size);
- +int rfs_log_alloc_chain(struct super_block *sb,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus);
- +int rfs_log_move_chain(struct super_block *sb,
- + unsigned int pdir, unsigned int entry,
- + unsigned int pool_prev, unsigned int pool_next,
- + unsigned int target_prev, unsigned int target_next,
- + unsigned int numof_clus, unsigned int *clus);
- +int rfs_log_update_pool(struct super_block *sb);
- +int tr_pre_alloc(struct super_block *sb);
- +int rfs_log_get_cluster(struct inode *inode, unsigned int *new_clu);
- +void rfs_log_cleanup(struct super_block *sb);
- +int rfs_log_force_commit(struct super_block *sb, struct inode *inode);
- +int rfs_log_read(struct super_block *sb, unsigned int isec);
- +int rfs_log_write(struct rfs_log_info *log_info);
- +int rfs_log_mark_end(struct super_block *sb, unsigned int sub_type);
- +int rfs_meta_commit(struct super_block *);
- +int tr_deferred_commit(struct super_block *sb);
- +
- +int rfs_log_replay(struct super_block *sb);
- +#endif /* __KERNEL__ */
- +
- +#endif /* _LINUX_RFS_FS_H */
- +
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/log_replay.c linux-2.6.17.14.rfs/fs/rfs/log_replay.c
- --- linux-2.6.17.14.orig/fs/rfs/log_replay.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/log_replay.c 2008-06-19 04:02:08.000000000 +0200
- @@ -0,0 +1,691 @@
- +/**
- + * @file fs/rfs/log_replay.c
- + * @brief functions for replaying log
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/rfs_fs.h>
- +#include "rfs.h"
- +#include "log.h"
- +
- +#ifdef LOG_BINARY_SCAN
- +#define SEARCH_MAX(sb, from, to, outvar) \
- + binary_search_max(sb, from, to, outvar)
- +#else
- +#define SEARCH_MAX(sb, from, to, outvar) \
- + sequential_search_max(sb, from, to, outvar)
- +#endif
- +
- +/**
- + * undo alloc chain from pool or fat table
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + *
- + * make target's chain complete and then return allocated chain to pool or
- + * fat table
- + */
- +static int rfs_log_undo_alloc_chain(struct super_block *sb)
- +{
- + struct rfs_log_fat *log_fat;
- + unsigned int numof_clus;
- + unsigned int sub_type;
- + unsigned int i;
- + int ret = 0;
- +
- + sub_type = GET_SUBLOG(GET32(RFS_LOG(sb)->type));
- + log_fat = &(RFS_LOG(sb)->log_fat);
- +
- + if (GET32(log_fat->t_prev_clu) != CLU_TAIL) {
- + /* make target chain complete */
- + if (fat_write(sb, GET32(log_fat->t_prev_clu),
- + GET32(log_fat->t_next_clu)))
- + return -EIO;
- + DEBUG(DL1, "make target %u -> %u", GET32(log_fat->t_prev_clu),
- + GET32(log_fat->t_next_clu));
- + } else if (GET32(log_fat->pdir) != CLU_TAIL) {
- + /*
- + * In case of mkdir, allocating cluster precedes
- + * building entry, which is remarked
- + * by setting CLU_TAIL to pdir
- + */
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- +
- + ep = get_entry_with_cluster(sb, GET32(log_fat->pdir),
- + GET32(log_fat->entry), &bh);
- + if (IS_ERR(ep)) {
- + /*
- + * EFAULT means extension of parent is not flushed and
- + * there is no dir entry for new file.
- + */
- + if (PTR_ERR(ep) != -EFAULT) {
- + brelse(bh);
- + return -EIO;
- + }
- + } else {
- + DEBUG(DL1, "ep's start_clu [%u -> 0]",
- + START_CLUSTER(ep));
- +
- + SET16(ep->start_clu_lo, 0);
- + SET16(ep->start_clu_hi, 0);
- +
- + if (buffer_uptodate(bh))
- + mark_buffer_dirty(bh);
- + }
- +
- + brelse(bh);
- + }
- +
- + numof_clus = GET32(log_fat->numof_clus);
- +
- + if (sub_type == RFS_SUBLOG_GET_CHAIN) {
- + /* link pool prev with head of chain */
- + if (fat_write(sb, GET32(log_fat->p_prev_clu),
- + GET32(log_fat->clus[0])))
- + return -EIO;
- +
- + /* make chain from pool complete */
- + for (i = 0; i < numof_clus - 1; i++) {
- + if (fat_write(sb, GET32(log_fat->clus[i]),
- + GET32(log_fat->clus[i + 1]))) {
- + return -EIO;
- + }
- + }
- +
- + /* link chain with pool next */
- + if (fat_write(sb, GET32(log_fat->clus[i]),
- + GET32(log_fat->p_next_clu)))
- + return -EIO;
- +
- + DEBUG(DL1, "make pool %u -> %u, %u -> %u",
- + GET32(log_fat->p_prev_clu), GET32(log_fat->clus[0]),
- + GET32(log_fat->clus[i]), GET32(log_fat->p_next_clu));
- +
- + DEBUG(DL1, "make target %u -> %u",
- + GET32(log_fat->t_prev_clu), GET32(log_fat->t_next_clu));
- + } else { /* RFS_SUBLOG_ALLOC_CHAIN */
- + /* set free allocated chain */
- + for (i = 0; i < numof_clus; i++) {
- + if (fat_write(sb, GET32(log_fat->clus[i]), CLU_FREE))
- + return -EIO;
- + DEBUG(DL2, "free %u", GET32(log_fat->clus[i]));
- + }
- + }
- +
- + /* update pool file */
- + RFS_POOL_I(sb)->last_cluster = GET32(log_fat->p_last_clu);
- + RFS_POOL_I(sb)->num_clusters = GET32(log_fat->p_num_clus);
- + RFS_POOL_I(sb)->c_start_cluster = GET32(log_fat->p_c_start_clu);
- + RFS_POOL_I(sb)->c_last_cluster = GET32(log_fat->p_c_last_clu);
- +
- + /* rewrite pool file */
- + ret = rfs_update_pool_block(sb);
- + if (ret)
- + return ret;
- +
- + /* resize entry of pool file */
- + ret = rfs_update_pool_entry(sb, GET32(log_fat->p_num_clus),
- + SET_POOL_SIZE);
- +
- + return ret;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * undo dealloc chain to fat table
- + * @param sb super block
- + * @return 0 on success, -EIO on failure
- + *
- + * make sure the logged chain and link the chain with old prev and next
- + */
- +static int rfs_log_undo_free_chain(struct super_block *sb)
- +{
- + struct rfs_log_fat *log_fat;
- + unsigned int numof_clus;
- + unsigned int i;
- +
- + log_fat = &(RFS_LOG(sb)->log_fat);
- + numof_clus = GET32(log_fat->numof_clus);
- +
- + /* link target prev with head of free_chain */
- + if (GET32(log_fat->t_prev_clu) != CLU_TAIL) { /* if not unlink file */
- + if (fat_write(sb, GET32(log_fat->t_prev_clu),
- + GET32(log_fat->clus[0])))
- + return -EIO;
- + } else {
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- +
- + if (numof_clus == 0) {
- + RFS_BUG("RFS-log : no deleted chain\n");
- + return -EIO;
- + }
- +
- + ep = get_entry_with_cluster(sb, GET32(log_fat->pdir),
- + GET32(log_fat->entry), &bh);
- + if (IS_ERR(ep)) {
- + brelse(bh);
- + return -EIO;
- + }
- + DEBUG(DL1, "ep's start_clu [%u -> %u]",
- + START_CLUSTER(ep), GET32(log_fat->clus[0]));
- +
- + SET16(ep->start_clu_lo, GET32(log_fat->clus[0]));
- + SET16(ep->start_clu_hi, GET32(log_fat->clus[0]) >> 16);
- +
- + if (buffer_uptodate(bh))
- + mark_buffer_dirty(bh);
- +
- + brelse(bh);
- + }
- +
- + /* make chain from free clusters */
- + for (i = 0; i < numof_clus - 1; i++) {
- + if (fat_write(sb, GET32(log_fat->clus[i]),
- + GET32(log_fat->clus[i + 1]))) {
- + return -EIO;
- + }
- + }
- +
- + /* link free_chain with target next */
- + if (fat_write(sb, GET32(log_fat->clus[i]), GET32(log_fat->t_next_clu)))
- + return -EIO;
- +
- + DEBUG(DL1, "make target %u -> %u, %u -> %u",
- + GET32(log_fat->t_prev_clu), GET32(log_fat->clus[0]),
- + GET32(log_fat->clus[i]), GET32(log_fat->t_next_clu));
- + return 0;
- +}
- +#endif
- +
- +/**
- + * undo built entries
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + *
- + * for each entry in built entries {
- + * if (entry is built)
- + * entry->name[0] = DELETION_MARK;
- + * }
- + */
- +static int rfs_log_undo_built_entry(struct super_block *sb)
- +{
- + struct rfs_log_entry *log_entry;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- + unsigned int numof_entries;
- + unsigned int i;
- + int ret = 0;
- +
- + log_entry = &(RFS_LOG(sb)->log_entry);
- + numof_entries = GET32(log_entry->numof_entries);
- +
- + for (i = 0; i < numof_entries; i++) {
- + ep = get_entry_with_cluster(sb, GET32(log_entry->pdir),
- + GET32(log_entry->entry) - i, &bh);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + if (ret == -EFAULT) {
- + /* beyond file limit */
- + /* the extended cluster was
- + not flushed, so do nothing
- + for build-entry on it */
- + ret = 0;
- + }
- + goto rel_bh;
- + }
- +
- + if (!IS_FREE(ep->name))
- + ep->name[0] = (unsigned char) DELETE_MARK;
- +
- + if (buffer_uptodate(bh))
- + mark_buffer_dirty(bh);
- + }
- +
- +rel_bh:
- + brelse(bh);
- +
- + return ret;
- +}
- +
- +/**
- + * undo removed entries
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + * @pre the way to delete entries is marking deletion at ep->name[0]
- + *
- + * for each entry in built entries {
- + * entry->name[0] = log_entry->undel_buf[index];
- + * }
- + */
- +static int rfs_log_undo_removed_entry(struct super_block *sb)
- +{
- + struct rfs_log_entry *log_entry;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- + unsigned int numof_entries;
- + unsigned int i;
- + int ret = 0;
- +
- + log_entry = &(RFS_LOG(sb)->log_entry);
- + numof_entries = GET32(log_entry->numof_entries);
- +
- + for (i = 0; i < numof_entries; i++) {
- + ep = get_entry_with_cluster(sb, GET32(log_entry->pdir),
- + GET32(log_entry->entry) - i, &bh);
- + if (IS_ERR(ep)) {
- + /*
- + * In rfs_symlink(), removing entry could happen
- + * for recovery of ENOSPC. In this case,
- + * entries can not be found, so returns EFAULT.
- + */
- + if ((ret = PTR_ERR(ep)) == -EFAULT)
- + ret = 0;
- +
- + goto rel_bh;
- + }
- +
- + ep->name[0] = log_entry->undel_buf[i];
- +
- + if (buffer_uptodate(bh))
- + mark_buffer_dirty(bh);
- + }
- +
- +rel_bh:
- + brelse(bh);
- +
- + return ret;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * undo updated entry
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + *
- + * restore old size
- + */
- +static int rfs_log_undo_updated_entry(struct super_block *sb)
- +{
- + struct rfs_log_entry *log_entry;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- + int ret = 0;
- +
- + log_entry = &(RFS_LOG(sb)->log_entry);
- +
- + ep = get_entry_with_cluster(sb, GET32(log_entry->pdir),
- + GET32(log_entry->entry), &bh);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + goto rel_bh;
- + }
- + SET32(ep->size, GET32(log_entry->from_size));
- + mark_buffer_dirty(bh);
- +
- +rel_bh:
- + brelse(bh);
- +
- + return ret;
- +}
- +#endif
- +
- +/**
- + * undo moving chain (delete chain or delete inode)
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + * @pre logging only head and tail for moved chain
- + *
- + * link logged chain with old prev and old, and rollback pool chain
- + * update pool file
- + */
- +static int rfs_log_undo_move_chain(struct super_block *sb)
- +{
- + struct rfs_log_fat *log_fat;
- + int ret;
- +
- + log_fat = &(RFS_LOG(sb)->log_fat);
- +
- + /* link target prev with head of chain */
- + if (GET32(log_fat->t_prev_clu) != CLU_TAIL) {
- + /* Shrink file to positive size or delete inode */
- + if (fat_write(sb, GET32(log_fat->t_prev_clu),
- + GET32(log_fat->clus[0])))
- + return -EIO;
- + } else if (GET32(log_fat->pdir) != CLU_TAIL) {
- + /*
- + * Shrink file to 0 or unlink file.
- + * In case of run-time rollback of mkdir by -ENOSPC,
- + * when dir entry have not been built yet
- + * log_fat->pdir has CLU_TAIL.
- + */
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep;
- +
- + if (GET32(log_fat->numof_clus) == 0) {
- + RFS_BUG("RFS-log : no deleted chain\n");
- + return -EIO;
- + }
- +
- + ep = get_entry_with_cluster(sb, GET32(log_fat->pdir),
- + GET32(log_fat->entry), &bh);
- + if (IS_ERR(ep)) {
- + brelse(bh);
- + return -EIO;
- + }
- + DEBUG(DL1, "ep's start_clu [%u -> %u]",
- + START_CLUSTER(ep), GET32(log_fat->clus[0]));
- +
- + SET16(ep->start_clu_lo, GET32(log_fat->clus[0]));
- + SET16(ep->start_clu_hi, GET32(log_fat->clus[0]) >> 16);
- +
- + if (buffer_uptodate(bh))
- + mark_buffer_dirty(bh);
- +
- + brelse(bh);
- + }
- +
- + /* link tail of chain with tail of chain */
- + if (fat_write(sb, GET32(log_fat->clus[1]), GET32(log_fat->t_next_clu)))
- + return -EIO;
- +
- + DEBUG(DL1, "make target %u -> %u, %u -> %u",
- + GET32(log_fat->t_prev_clu), GET32(log_fat->clus[0]),
- + GET32(log_fat->clus[1]), GET32(log_fat->t_next_clu));
- +
- + /* make pool chain complete */
- + if (fat_write(sb, GET32(log_fat->p_prev_clu),
- + GET32(log_fat->p_next_clu)))
- + return -EIO;
- +
- + DEBUG(DL1, "make pool %u -> %u",
- + GET32(log_fat->p_prev_clu), GET32(log_fat->p_next_clu));
- +
- + /* update pool file */
- + RFS_POOL_I(sb)->last_cluster = GET32(log_fat->p_last_clu);
- + RFS_POOL_I(sb)->num_clusters = GET32(log_fat->p_num_clus);
- + RFS_POOL_I(sb)->c_start_cluster = GET32(log_fat->p_c_start_clu);
- + RFS_POOL_I(sb)->c_last_cluster = GET32(log_fat->p_c_last_clu);
- +
- + /* rewrite pool file */
- + ret = rfs_update_pool_block(sb);
- + if (ret)
- + return ret;
- +
- + /* resize entry of pool file */
- + ret = rfs_update_pool_entry(sb, GET32(log_fat->p_num_clus),
- + SET_POOL_SIZE);
- + if (ret)
- + return ret;
- +
- + return 0;
- +}
- +
- +/**
- + * undo change of pool file
- + * @param sb super block
- + * @return 0 on success, errno on failure
- + * @pre logging only head and tail for moved chain
- + */
- +static int rfs_log_undo_updated_pool(struct super_block *sb)
- +{
- + struct rfs_log_fat *log_fat;
- + int ret;
- +
- + log_fat = &(RFS_LOG(sb)->log_fat);
- +
- + /* update pool file */
- + RFS_POOL_I(sb)->last_cluster = GET32(log_fat->p_last_clu);
- + RFS_POOL_I(sb)->num_clusters = GET32(log_fat->p_num_clus);
- + RFS_POOL_I(sb)->c_start_cluster = GET32(log_fat->p_c_start_clu);
- + RFS_POOL_I(sb)->c_last_cluster = GET32(log_fat->p_c_last_clu);
- +
- + DEBUG(DL1, "POOL_INFO : <%u, %u>, <%u, %u>",
- + RFS_POOL_I(sb)->last_cluster,
- + RFS_POOL_I(sb)->num_clusters,
- + RFS_POOL_I(sb)->c_start_cluster,
- + RFS_POOL_I(sb)->c_last_cluster);
- +
- + /* rewrite pool file */
- + ret = rfs_update_pool_block(sb);
- + if (ret)
- + return ret;
- +
- + /* resize entry of pool file */
- + ret = rfs_update_pool_entry(sb, GET32(log_fat->p_num_clus),
- + SET_POOL_SIZE);
- + if (ret)
- + return ret;
- +
- + return 0;
- +}
- +
- +/**
- + * sequential search for the index of a log with max sequence
- + * @param sb super block
- + * @param from from index
- + * @param to to index
- + * @param[out] last_index the index of log with max sequence
- + * @return 0 on success, error no on failure.
- + */
- +static int sequential_search_max(struct super_block *sb, unsigned int from,
- + unsigned int to, unsigned int *last_index)
- +{
- + rfs_log_seq_t max_seq, seq;
- + unsigned int i;
- +
- + max_seq = seq = 0;
- + *last_index = -1;
- +
- + DEBUG(DL3, "from : %d, to : %d\n", from, to);
- + for (i = from; i <= to; i++) {
- + if (rfs_log_read(sb, i))
- + return -EIO;
- +
- + seq = GET64(RFS_LOG(sb)->sequence);
- +
- + DEBUG(DL3, "%d, max : 0x%0x, cur : 0x%0x\n",
- + i, (unsigned int) max_seq, (unsigned int) seq);
- + if (max_seq < seq) {
- + max_seq = seq;
- + *last_index = i;
- + }
- + }
- +
- + DEBUG(DL2, "last tr is : %d, max_seq : 0x%0x\n",
- + *last_index, (unsigned int) max_seq);
- +
- + /* notice that it is possible to overflow sequence number */
- + if (max_seq == (rfs_log_seq_t) (~0))
- + return -EILSEQ;
- +
- + return 0;
- +}
- +
- +/**
- + * get the index of a log with max sequence in log file
- + * @param sb super block
- + * @param[out] last_index the index of log with max sequence
- + * @return 0 or -ENOENT on success, other errnos on failure
- + */
- +static int rfs_log_get_trans(struct super_block *sb, int *last_index)
- +{
- + int ret;
- +
- + /* set default value */
- + *last_index = -1;
- +
- + /* check empty log file */
- + if (rfs_log_read(sb, 0))
- + return -EIO;
- +
- + if (GET32(RFS_LOG(sb)->type) == RFS_LOG_NONE)
- + return -ENOENT;
- +
- +
- + ret = sequential_search_max(sb, 0, RFS_LOG_MAX_COUNT - 1, last_index);
- +
- + /*
- + * TODO
- + * Is it necessary to handle overflowing sequence number? (EILSEQ)
- + */
- + if (ret < 0)
- + return ret;
- +
- + return 0;
- +}
- +
- +/**
- + * replay logs from latest uncommited log
- + * @param sb rfs private super block
- + * @return 0 on success, errno on failure
- + * @pre super block should be initialized
- + */
- +int rfs_log_replay(struct super_block *sb)
- +{
- + rfs_log_seq_t prev_seq = 0;
- + /* sub_type should be used as 'int' type */
- + int sub_type = RFS_LOG_NONE;
- + int last_index = 0, index;
- + int ret = 0;
- +
- + if ((ret = rfs_log_get_trans(sb, &last_index))) {
- + if (ret == -ENOENT) {
- + RFS_LOG_I(sb)->isec = 0;
- + ret = 0;
- + }
- + goto out;
- + }
- +
- + ret = rfs_log_read(sb, last_index);
- + if (ret)
- + goto out;
- +
- + index = last_index;
- + RFS_LOG_I(sb)->sequence = GET64(RFS_LOG(sb)->sequence) + 1;
- + RFS_LOG_I(sb)->type = RFS_LOG_REPLAY;
- + sub_type = GET_SUBLOG(GET32(RFS_LOG(sb)->type));
- +
- + /* In order to check continuity of sequence number */
- + prev_seq = GET64(RFS_LOG(sb)->sequence) + 1;
- + while (1) {
- + /*
- + * Note: It's important use 'int' type in swtich condition.
- + * 'unsigned int' condition can't handle it correctly
- + * in gcov mode
- + */
- +
- + /* Are the sequence numbers continuous? */
- + if (prev_seq - 1 != GET64(RFS_LOG(sb)->sequence)) {
- + DPRINTK("Log records are not continuous."
- + "Log file is broken\n");
- + return -EIO;
- + }
- +
- + switch (sub_type) {
- + case RFS_SUBLOG_ALLOC_CHAIN:
- + case RFS_SUBLOG_GET_CHAIN:
- + DEBUG(DL0, "RFS_SUBLOG_ALLOC_CHAIN");
- + ret = rfs_log_undo_alloc_chain(sb);
- + break;
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- + case RFS_SUBLOG_FREE_CHAIN:
- + DEBUG(DL0, "RFS_SUBLOG_FREE_CHAIN");
- + ret = rfs_log_undo_free_chain(sb);
- + break;
- + case RFS_SUBLOG_UPDATE_ENTRY:
- + DEBUG(DL0, "RFS_SUBLOG_UPDATE_ENTRY");
- + ret = rfs_log_undo_updated_entry(sb);
- + break;
- + case RFS_SUBLOG_PUT_CHAIN:
- +#endif
- + case RFS_SUBLOG_MOVE_CHAIN:
- + DEBUG(DL0, "RFS_SUBLOG_MOVE_CHAIN");
- + ret = rfs_log_undo_move_chain(sb);
- + break;
- + case RFS_SUBLOG_BUILD_ENTRY:
- + DEBUG(DL0, "RFS_SUBLOG_BUILD_ENTRY");
- + ret = rfs_log_undo_built_entry(sb);
- + break;
- + case RFS_SUBLOG_REMOVE_ENTRY:
- + DEBUG(DL0, "RFS_SUBLOG_REMOVE_ENTRY");
- + ret = rfs_log_undo_removed_entry(sb);
- + break;
- + case RFS_SUBLOG_UPDATE_POOL:
- + DEBUG(DL0, "RFS_SUBLOG_UPDATE_POOL");
- + ret = rfs_log_undo_updated_pool(sb);
- + break;
- + case RFS_SUBLOG_ABORT: /* transaction is aborted */
- + DEBUG(DL0, "Latest transaction is aborted."
- + " Strongly recommend to check filesystem\n");
- + case RFS_SUBLOG_COMMIT: /* transaction completes */
- + case RFS_SUBLOG_START: /* transaction dose nothing */
- + /* do nothing. do not need to commit */
- + DEBUG(DL0, "RFS_SUBLOG_COMMIT");
- + if (last_index == index) {
- + brelse(RFS_LOG_I(sb)->bh);
- + RFS_LOG_I(sb)->bh = NULL;
- + RFS_LOG_I(sb)->log = NULL;
- + goto out;
- + }
- +
- + goto commit;
- + default:
- + RFS_BUG("RFS-log : Unsupporting log record\n");
- + ret = -EIO;
- + }
- +
- + if (ret)
- + goto out;
- +
- + if (index == 0)
- + index = RFS_LOG_MAX_COUNT;
- +
- + /* the prev seq is greater than current by 1 */
- + prev_seq--;
- +
- + /* get next log */
- + ret = rfs_log_read(sb, --index);
- + if (ret)
- + goto out;
- +
- + sub_type = GET_SUBLOG(GET32(RFS_LOG(sb)->type));
- + }
- +commit:
- + rfs_sync_vol(sb);
- +
- + /* commit mark */
- + if (last_index == RFS_LOG_MAX_COUNT - 1)
- + RFS_LOG_I(sb)->isec = 0;
- + else
- + RFS_LOG_I(sb)->isec = last_index + 1;
- +
- + ret = rfs_log_mark_end(sb, RFS_SUBLOG_COMMIT);
- +
- + DEBUG(DL0, "end mark commit");
- + return ret;
- +out:
- + RFS_LOG_I(sb)->type = RFS_LOG_NONE;
- +
- + return ret;
- +}
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Makefile linux-2.6.17.14.rfs/fs/rfs/Makefile
- --- linux-2.6.17.14.orig/fs/rfs/Makefile 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Makefile 2008-06-19 04:02:10.000000000 +0200
- @@ -0,0 +1,32 @@
- +#
- +# Makefile for the linux rfs-filesystem
- +#
- +
- +ifeq ($(CONFIG_GCOV_PROFILE),y)
- +ifeq ($(PATCHLEVEL),4)
- +include Makefile.gcov
- +else
- +include $(srctree)/fs/rfs/Makefile.gcov
- +endif
- +# In profile mode, we always turn on log replay test without power-off
- +EXTRA_CFLAGS += -D_RFS_INTERNAL_RECOVERY_TEST
- +endif
- +
- +#
- +# RFS internal configurations
- +#
- +
- +# trace writing block
- +#EXTRA_CFLAGS += -D_RFS_INTERNAL_STAT_BH
- +
- +# Flush user cache
- +#EXTRA_CFLAGS += -D_RFS_INTERNAL_FLUSH_CACHE
- +
- +# Sanity check
- +#EXTRA_CFLAGS += -D_RFS_INTERNAL_SANITY_CHECK
- +
- +ifeq ($(PATCHLEVEL),4)
- +include Makefile.24
- +else
- +include $(srctree)/fs/rfs/Makefile.26
- +endif
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Makefile.24 linux-2.6.17.14.rfs/fs/rfs/Makefile.24
- --- linux-2.6.17.14.orig/fs/rfs/Makefile.24 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Makefile.24 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,14 @@
- +#
- +# Makefile for the linux rfs-filesystem (kernel 2.4)
- +#
- +
- +O_TARGET := rfs.o
- +
- +obj-y += cluster.o code_convert.o dos.o
- +obj-y += dir.o file.o inode.o namei.o super.o
- +obj-y += log.o log_replay.o
- +obj-y += rfs_24.o
- +
- +obj-m += $(O_TARGET)
- +
- +include $(TOPDIR)/Rules.make
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Makefile.26 linux-2.6.17.14.rfs/fs/rfs/Makefile.26
- --- linux-2.6.17.14.orig/fs/rfs/Makefile.26 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Makefile.26 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,10 @@
- +#
- +# Makefile for the linux rfs-filesystem (kernel 2.6)
- +#
- +
- +obj-$(CONFIG_RFS_FS) += rfs.o
- +
- +rfs-y += cluster.o code_convert.o dos.o
- +rfs-y += dir.o file.o inode.o namei.o super.o
- +rfs-y += log.o log_replay.o
- +rfs-y += rfs_26.o
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/Makefile.gcov linux-2.6.17.14.rfs/fs/rfs/Makefile.gcov
- --- linux-2.6.17.14.orig/fs/rfs/Makefile.gcov 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/Makefile.gcov 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,17 @@
- +#
- +# Makefile for the linux rfs-filesystem for gcov
- +#
- +
- +CFLAGS_cluster.o += $(GCOV_FLAGS)
- +CFLAGS_code_convert.o += $(GCOV_FLAGS)
- +CFLAGS_dir.o += $(GCOV_FLAGS)
- +CFLAGS_dos.o += $(GCOV_FLAGS)
- +CFLAGS_file.o += $(GCOV_FLAGS)
- +CFLAGS_inode.o += $(GCOV_FLAGS)
- +CFLAGS_log.o += $(GCOV_FLAGS)
- +CFLAGS_log_replay.o += $(GCOV_FLAGS)
- +CFLAGS_namei.o += $(GCOV_FLAGS)
- +CFLAGS_rfs_24.o += $(GCOV_FLAGS)
- +CFLAGS_rfs_26.o += $(GCOV_FLAGS)
- +CFLAGS_super.o += $(GCOV_FLAGS)
- +CC = $(CROSS_COMPILE)gcc
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/namei.c linux-2.6.17.14.rfs/fs/rfs/namei.c
- --- linux-2.6.17.14.orig/fs/rfs/namei.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/namei.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,1093 @@
- +/**
- + * @file fs/rfs/namei.c
- + * @brief Here is adaptation layer between the VFS and RFS filesystem for inode ops
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/rfs_fs.h>
- +#include <linux/sched.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +#define SLASH '/'
- +
- +#define ROOT_MTIME(sb) (sb->s_root->d_inode->i_mtime)
- +
- +/* structure for rename */
- +struct rename_info {
- + struct inode *old_dir;
- + struct inode *old_inode;
- + struct inode *new_dir;
- + unsigned int *new_index;
- + const char *new_name;
- +};
- +
- +/**
- + * Function extending a directory on request of saving larger entries that left entries.
- + * @param dir inode corresponding to extending directory
- + * @param start_clu return value to contain a start cluster value when this function is used to create new directory
- + * @return zero on success, if not, negative error code.
- + *
- + * extend_dir invokes alloc_cluster in it, and assumes that alloc_cluster
- + * takes care of not only allocating new cluster but also updating FAT table.
- + * In addition, this function is used to create new directory.
- + */
- +static struct buffer_head *extend_dir(struct inode * dir, unsigned int *start_clu) {
- +
- + struct super_block *sb = dir->i_sb;
- + struct buffer_head *bh, *ret = NULL;
- + unsigned int start_block, last_block, cur_block;
- + unsigned int new_clu;
- + int err;
- +
- + if ((RFS_I(dir)->start_clu == RFS_SB(sb)->root_clu)
- + && !IS_FAT32(RFS_SB(sb)))
- + return ERR_PTR(-ENOSPC);
- +
- + if ((dir->i_size + RFS_SB(sb)->cluster_size) >
- + (MAX_DIR_DENTRY << DENTRY_SIZE_BITS))
- + return ERR_PTR(-ENOSPC);
- +
- + /* alloc new cluster */
- + err = alloc_cluster(dir, &new_clu);
- + if (err)
- + return ERR_PTR(err);
- +
- + if (start_clu)
- + *start_clu = new_clu;
- +
- + /* reset new cluster with zero to initalize directory entry */
- + start_block = START_BLOCK(new_clu, sb);
- + last_block = start_block + RFS_SB(sb)->blks_per_clu;
- + cur_block = start_block;
- + do {
- + if ((bh = sb_getblk(sb, cur_block))) {
- + memset(bh->b_data, 0x00 , sb->s_blocksize);
- +
- +#ifdef RFS_FOR_2_6
- + set_buffer_uptodate(bh);
- +#else
- + mark_buffer_uptodate(bh, 1);
- +#endif
- + rfs_mark_buffer_dirty(bh, sb);
- + if (!ret)
- + ret = bh;
- + else
- + brelse(bh);
- + }
- + } while (++cur_block < last_block);
- +
- + if (!ret) /* I/O error */
- + return ERR_PTR(-EIO);
- +
- + /* new inode is also initialized by 0 */
- + dir->i_size += RFS_SB(sb)->cluster_size;
- + RFS_I(dir)->mmu_private += RFS_SB(sb)->cluster_size;
- +
- + return ret;
- +}
- +
- +/**
- + * allocate a cluster for new directory
- + * @param inode new inode
- + * @param start_clu allocated cluster to inode
- + * @return zero on success, or errno
- + */
- +static inline int init_dir_clu(struct inode *inode, unsigned int *start_clu)
- +{
- + struct buffer_head *bh = NULL;
- + int ret;
- +
- + /* CLU_TAIL is temporary value for cluster allocation */
- + RFS_I(inode)->start_clu = CLU_TAIL;
- + RFS_I(inode)->mmu_private = 0; /* set 0 for extend_dir() */
- +
- + /*
- + * RFS-log : setting CLU_TAIL to inode's p_start_clu informs
- + * that inode does not have a entry yet
- + */
- + RFS_I(inode)->p_start_clu = CLU_TAIL;
- + bh = extend_dir(inode, start_clu);
- + if (IS_ERR(bh))
- + return PTR_ERR(bh);
- +
- + ret = init_new_dir(inode);
- +
- + brelse(bh);
- + return ret;
- +}
- +
- +/**
- + * Function to find proper postion where new entry is stored
- + * @param dir inode relating to parent directory
- + * @param slots the number of entries to be saved
- + * @param bh buffer head containing directory entry
- + * @return a offset of empty slot of a current directory on sucess, negative value on falure.
- + *
- + * This function is invoked by upper functions, upper functions are different
- + * according the FAT type of current system.
- + */
- +static int find_empty_entry (struct inode *dir, int slots, struct buffer_head **bh) {
- +
- + struct super_block *sb = dir->i_sb;
- + struct buffer_head *new_bh;
- + struct rfs_dir_entry *ep;
- + unsigned int cpos = 0, free = 0;
- + int nr_clus = 0;
- +
- + while (1) {
- + ep = get_entry(dir, cpos, bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT)
- + break;
- + else
- + return PTR_ERR(ep);
- + }
- +
- + if (IS_FREE(ep->name)) {
- + if (++free == slots)
- + return cpos;
- + } else {
- + free = 0;
- + }
- +
- + cpos++;
- + }
- +
- + /* If fail to find requested slots, decide whether to extend directory */
- + if ((RFS_I(dir)->start_clu == RFS_SB(sb)->root_clu) &&
- + (!IS_FAT32(RFS_SB(sb)))) {
- + return -ENOSPC;
- + }
- +
- + /* calculate the number of cluster */
- + nr_clus = (slots - free +
- + (RFS_SB(sb)->cluster_size >> DENTRY_SIZE_BITS) - 1) /
- + (RFS_SB(sb)->cluster_size >> DENTRY_SIZE_BITS);
- + if (nr_clus > GET_FREE_CLUS(RFS_SB(sb)))
- + return -ENOSPC;
- +
- + /* extend entry */
- + while (nr_clus-- > 0) {
- + new_bh = extend_dir(dir, NULL);
- + if (IS_ERR(new_bh))
- + return PTR_ERR(new_bh);
- + }
- +
- + while (1) {
- + ep = get_entry(dir, cpos, &new_bh);
- + if (IS_ERR(ep)) {
- + if (PTR_ERR(ep) == -EFAULT)
- + return -ENOSPC;
- + else
- + return PTR_ERR(ep);
- + }
- + if (++free == slots)
- + break;
- + cpos++;
- + }
- +
- + *bh = new_bh;
- + /* return accumulated offset regardless of cluster */
- + return cpos;
- +}
- +
- +#ifndef CONFIG_RFS_VFAT
- +/**
- + * compare two dos names
- + * @param dentry dentry to be computed
- + * @param a file name cached in dentry cache
- + * @param b file name to be compared for corresponding dentry
- + * @return return 0 on success, errno on failure
- + *
- + * If either of the names are invalid, do the standard name comparison
- + */
- +
- +static int rfs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
- +{
- + u8 a_dosname[DOS_NAME_LENGTH], b_dosname[DOS_NAME_LENGTH];
- + char valid_name[NAME_MAX + 1];
- + int err = 0;
- +
- + if (a->len > NAME_MAX || b->len > NAME_MAX) /* out-of-range input */
- + return -EINVAL;
- +
- + memcpy(valid_name, a->name, a->len);
- + valid_name[a->len] = '\0';
- + err = convert_cstring_to_dosname(a_dosname, valid_name, NULL, FALSE);
- + if (err < 0)
- + goto out;
- +
- + memcpy(valid_name, b->name, b->len);
- + valid_name[b->len] = '\0';
- + err = convert_cstring_to_dosname(b_dosname, valid_name, NULL, FALSE);
- + if (err < 0)
- + goto out;
- + err = memcmp(a_dosname, b_dosname, DOS_NAME_LENGTH);
- +
- +out:
- + return err;
- +}
- +
- +/**
- + * compute the hash for the dos name corresponding to the dentry
- + * @param dentry dentry to be computed
- + * @param qstr parameter to contain resulting hash value
- + * @return return 0
- + */
- +static int rfs_dentry_hash(struct dentry *dentry, struct qstr *qstr)
- +{
- + unsigned char hash_name[DOS_NAME_LENGTH];
- + char valid_name[NAME_MAX + 1];
- + int err;
- +
- + if (qstr->len > NAME_MAX) /* out-of-range input */
- + return 0;
- +
- + memcpy(valid_name, qstr->name, qstr->len);
- + valid_name[qstr->len] = '\0';
- + err = convert_cstring_to_dosname(hash_name, valid_name, NULL, FALSE);
- + if (!err)
- + qstr->hash = full_name_hash(hash_name, DOS_NAME_LENGTH);
- +
- + return 0;
- +}
- +
- +struct dentry_operations rfs_dentry_operations = {
- + .d_hash = rfs_dentry_hash,
- + .d_compare = rfs_dentry_cmp,
- +};
- +
- +/**
- + * create a dir entry and fill it
- + * @param dir parent dir inode
- + * @param inode inode of new dir entry
- + * @param start_clu start cluster number for itself
- + * @param type entry type
- + * @param name object name
- + * @return return index(pointer to save dir entry index) on success, errno on failure
- + */
- +int build_entry_short(struct inode *dir, struct inode *inode, unsigned int start_clu, unsigned int type, const char *name)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned char dosname[DOS_NAME_LENGTH];
- + unsigned char is_mixed = 0;
- + unsigned int index;
- + int ret = 0;
- +
- + /* convert a cstring into dosname */
- + ret = mk_dosname(dir, name, dosname, &is_mixed, NULL);
- + if (ret < 0)
- + goto out;
- +
- + /* find empty dir entry */
- + index = find_empty_entry(dir, 1, &bh);
- + if ((int) index < 0) {
- + ret = index;
- + goto out;
- + }
- +
- + /* allocate a new cluster for directory */
- + if (type == TYPE_DIR && inode)
- + if ((ret = init_dir_clu(inode, &start_clu)) < 0)
- + goto out;
- +
- + ret = index;
- + /* get dir entry pointer */
- + ep = get_entry(dir, index, &bh);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + goto out;
- + }
- +
- + /* RFS-log sub trans */
- + if (rfs_log_build_entry(dir->i_sb, RFS_I(dir)->start_clu, index, 1)) {
- + ret = -EIO;
- + goto out;
- + }
- +
- + /* init dir entry */
- + init_dir_entry(dir, ep, type, start_clu, dosname, &is_mixed);
- +
- + rfs_mark_buffer_dirty(bh, dir->i_sb);
- +
- +out:
- + brelse(bh);
- + return ret;
- +}
- +
- +#else /* CONFIG_RFS_VFAT */
- +
- +/**
- + * create a dir entry and extend entries
- + * @param dir parent dir inode
- + * @param inode inode of new dir entry
- + * @param start_clu start cluster number for itself
- + * @param type entry type
- + * @param name object name
- + * @return return index(pointer to save dir entry index) on success, errno on failure
- + */
- +int build_entry_long(struct inode *dir, struct inode *inode, unsigned int start_clu, unsigned int type, const char *name)
- +{
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL, *tmp_ep = NULL;
- + struct rfs_ext_entry *extp = NULL;
- + unsigned char checksum;
- + unsigned short uname[UNICODE_NAME_LENGTH];
- + unsigned char dosname[DOS_NAME_LENGTH];
- + unsigned int num_entries, i;
- + unsigned char is_mixed = 0;
- + unsigned int index;
- + int ret = 0;
- +
- + /*
- + * convert a cstring into dosname &
- + * return the count of needed extend slots
- + */
- + if ((ret = mk_dosname(dir, name, dosname, &is_mixed, uname)) < 0)
- + return ret;
- +
- + /* ret has the number of the extend slot */
- + num_entries = ret + 1;
- +
- + /* find empty dir entry */
- + index = find_empty_entry(dir, num_entries, &bh);
- + if ((int) index < 0) {
- + ret = index;
- + goto out;
- + }
- +
- + /* allocate a new cluster for directory */
- + if (type == TYPE_DIR && inode)
- + if ((ret = init_dir_clu(inode, &start_clu)) < 0)
- + goto out;
- +
- + ret = index;
- + /* get dir entry pointer */
- + ep = get_entry(dir, index, &bh);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + goto out;
- + }
- +
- + /* RFS-log sub trans */
- + if (rfs_log_build_entry(dir->i_sb, RFS_I(dir)->start_clu, index,
- + num_entries)) {
- + ret = -EIO;
- + goto out;
- + }
- +
- + /* init dir entry */
- + init_dir_entry(dir, ep, type, start_clu, dosname, &is_mixed);
- +
- + rfs_mark_buffer_dirty(bh, dir->i_sb);
- +
- + /* only have dos entry */
- + if (num_entries == 1)
- + goto out;
- +
- + checksum = calc_checksum(dosname);
- +
- + /* init extend entries */
- + for (i = 1; i < num_entries; i++) {
- + tmp_ep = get_entry(dir, index - i, &bh);
- + if (IS_ERR(tmp_ep)) {
- + ret = PTR_ERR(ep);
- + goto out;
- + }
- + extp = (struct rfs_ext_entry *)tmp_ep;
- + if (init_ext_entry(extp, TYPE_EXTEND,
- + ((i == (num_entries - 1))? i + EXT_END_MARK: i),
- + &(uname[EXT_UNAME_LENGTH * (i - 1)]),
- + checksum) < 0) { /* out-of-range input */
- + ret = -EIO;
- + goto out;
- + }
- +
- + rfs_mark_buffer_dirty(bh, dir->i_sb);
- + }
- +
- +out:
- + brelse(bh);
- +
- + return ret;
- +}
- +
- +#endif /* !CONFIG_RFS_VFAT */
- +
- +/**
- + * find inode for dir entry and return
- + * @param sb super block
- + * @param ino inode number of dir entry
- + * @param p_start_clu parent's start cluster
- + * @param index dir entry's offset in parent dir
- + * @param ep dir entry
- + * @return inode of dir entry on success or errno on failure
- + */
- +static inline struct inode *rfs_iget(struct super_block *sb, unsigned long ino, unsigned int p_start_clu, unsigned int index, struct rfs_dir_entry *ep)
- +{
- +#ifndef CONFIG_RFS_IGET4
- + struct inode *inode = iget_locked(sb, ino);
- +
- + if (inode && (inode->i_state & I_NEW)) {
- + fill_inode(inode, ep, p_start_clu, index);
- + unlock_new_inode(inode);
- + }
- +#else
- + struct inode *inode;
- + struct rfs_iget4_args args;
- +
- + args.ep = ep;
- + args.p_start_clu = p_start_clu;
- + args.index = index;
- +
- + /*
- + * Some kernel version(under Linux kernel version 2.4.25) does not
- + * support iget_locked.
- + */
- + inode = iget4(sb, ino, NULL, (void *) (&args));
- +#endif
- +
- + return inode;
- +}
- +
- +/**
- + * find dir entry for inode in parent dir and return a dir entry & entry index
- + * @param dir parent dir inode
- + * @param inode inode for itself
- + * @param bh buffer head included dir entry for inode
- + * @param name object name to search
- + * @param type dir entry type
- + * @return return dir entry on success, errno on failure
- + */
- +static struct rfs_dir_entry *search_entry(struct inode *dir, struct inode *inode, struct buffer_head **bh, const char *name, unsigned char type)
- +{
- + if (!inode) /* out-of-range input */
- + return ERR_PTR(-EINVAL);
- +
- + CHECK_RFS_INODE(inode, ERR_PTR(-EINVAL));
- +
- + return get_entry(dir, RFS_I(inode)->index, bh);
- +}
- +
- +/**
- + * lookup inode associated with dentry
- + * @param dir inode of parent directory
- + * @param dentry dentry for itself
- + * @param nd namei data structure
- + * @return return dentry object on success, errno on failure
- + *
- + * if inode doesn't exist, allocate new inode, fill it, and associated with dentry
- + */
- +#ifdef RFS_FOR_2_6
- +static struct dentry *rfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
- +#else
- +static struct dentry *rfs_lookup(struct inode *dir, struct dentry *dentry)
- +#endif
- +{
- + struct inode *inode = NULL;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + unsigned int index;
- + unsigned long ino;
- + int ret = 0;
- +
- + /* check the name length */
- + if (dentry->d_name.len > NAME_MAX)
- + return ERR_PTR(-ENAMETOOLONG);
- +
- +#ifndef CONFIG_RFS_VFAT
- + dentry->d_op = &rfs_dentry_operations;
- +#endif
- +
- + /* find dir entry */
- + ret = find_entry(dir, dentry->d_name.name, &bh, TYPE_ALL);
- + if (ret < 0) {
- + if (ret == -ENOENT)
- + goto add;
- + if ((ret != -EINVAL) && (ret != -ENAMETOOLONG))
- + ret = -EIO;
- + goto out;
- + }
- + index = ret;
- +
- + ep = get_entry(dir, index, &bh);
- + if (IS_ERR(ep)) {
- + ret = -EIO;
- + goto out;
- + }
- +
- + /* get unique inode number */
- + ret = rfs_iunique(dir, index, &ino);
- + if (ret)
- + goto out;
- +
- + inode = rfs_iget(dir->i_sb, ino, RFS_I(dir)->start_clu, index, ep);
- + if (!inode) {
- + ret = -EACCES;
- + goto out;
- + }
- +
- +#ifdef CONFIG_RFS_VFAT
- + do {
- + struct dentry *alias = NULL;
- +
- + alias = d_find_alias(inode);
- + if (alias) {
- + if (d_invalidate(alias) == 0) {
- + dput(alias);
- + } else {
- + iput(inode);
- + brelse(bh);
- + return alias;
- + }
- + }
- + } while (0);
- +#endif
- +add:
- + brelse(bh);
- +#ifdef RFS_FOR_2_6
- + return d_splice_alias(inode, dentry);
- +#else
- + d_add(dentry, inode);
- + return NULL;
- +#endif
- +out:
- + brelse(bh);
- + return ERR_PTR(ret);
- +}
- +
- +/**
- + * create a new file
- + * @param dir inode of parent directory
- + * @param dentry dentry corresponding with a file will be created
- + * @param mode mode
- + * @param nd namei data structure
- + * @return return 0 on success, errno on failure
- + */
- +#ifdef RFS_FOR_2_6
- +static int rfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
- +#else
- +static int rfs_create(struct inode *dir, struct dentry *dentry, int mode)
- +#endif
- +{
- + struct inode *inode = NULL;
- + int ret;
- +
- + /* check the validity */
- + if (IS_RDONLY(dir))
- + return -EROFS;
- +
- + if (rfs_log_start(dir->i_sb, RFS_LOG_CREATE, dir))
- + return -EIO;
- +
- + /* create a new inode */
- + inode = rfs_new_inode(dir, dentry, TYPE_FILE);
- + if (IS_ERR(inode))
- + ret = PTR_ERR(inode);
- + else
- + ret = 0;
- +
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(dir->i_sb, ret))
- + return -EIO;
- +
- + if (!ret) /* attach inode to dentry */
- + d_instantiate(dentry, inode);
- + return ret;
- +}
- +
- +/**
- + * check whether a file of inode has start cluster number of RFS reserved files
- + * @param inode inode of checked file
- + * @param name name of checked file
- + * @return return 0 if inode has different start cluster number, else return errno
- + *
- + * special files for fast unlink & logging are reserved files
- + */
- +int check_reserved_files(struct inode *inode, const char *name)
- +{
- + if (inode) {
- + struct super_block *sb = inode->i_sb;
- + unsigned int cluster = RFS_I(inode)->start_clu;
- +
- + if (cluster == RFS_POOL_I(sb)->start_cluster)
- + return -EPERM;
- + if (cluster == RFS_LOG_I(sb)->start_cluster)
- + return -EPERM;
- + } else if (name) {
- + int len = strlen(name);
- +
- + if ((len != RFS_LOG_FILE_LEN) || (len != RFS_POOL_FILE_LEN))
- + return 0;
- +
- + if (!strncmp(name, RFS_LOG_FILE_NAME, RFS_LOG_FILE_LEN))
- + return -EPERM;
- +
- + if (!strncmp(name, RFS_POOL_FILE_NAME, RFS_POOL_FILE_LEN))
- + return -EPERM;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * remove a file
- + * @param dir parent directory inode
- + * @param dentry dentry corresponding with a file will be removed
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_unlink(struct inode *dir, struct dentry *dentry)
- +{
- + struct inode *inode = dentry->d_inode;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + int ret;
- +
- + /* check the validity */
- + if (IS_RDONLY(dir))
- + return -EROFS;
- +
- + /* check the name length */
- + if (dentry->d_name.len > NAME_MAX)
- + return -ENAMETOOLONG;
- +
- + /* check the system files */
- + if (check_reserved_files(inode, NULL))
- + return -EPERM;
- +
- + /* find dir entry */
- + ep = search_entry(dir, inode, &bh, dentry->d_name.name, TYPE_FILE);
- + if (IS_ERR(ep)) {
- + brelse(bh);
- + return PTR_ERR(ep);
- + }
- +
- + if ((ep->attr & ATTR_READONLY) && (!capable(CAP_SYS_ADMIN))) {
- + brelse(bh);
- + return -EPERM;
- + }
- + brelse(bh);
- +
- + /* RFS-log : start unlink */
- + if (rfs_log_start(dir->i_sb, RFS_LOG_UNLINK, dir))
- + return -EIO;
- +
- + /* remove dir entry and dealloc all clusters for inode allocated */
- + ret = rfs_delete_entry(dir, inode);
- +
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(dir->i_sb, ret))
- + return -EIO;
- +
- + return ret;
- +}
- +
- +/**
- + * create a new directory
- + * @param dir parent directory inode
- + * @param dentry dentry corresponding with a directory will be created
- + * @param mode mode
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
- +{
- + struct inode *inode = NULL;
- + int ret;
- +
- + /* check the validity */
- + if (IS_RDONLY(dir))
- + return -EROFS;
- +
- + /* RFS-log : start mkdir */
- + if (rfs_log_start(dir->i_sb, RFS_LOG_CREATE, dir))
- + return -EIO;
- +
- + /* create a new inode */
- + inode = rfs_new_inode(dir, dentry, TYPE_DIR);
- + if (IS_ERR(inode))
- + ret = PTR_ERR(inode);
- + else
- + ret = 0;
- +
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(dir->i_sb, ret))
- + return -EIO;
- +
- + if (!ret) /* attach inode to dentry */
- + d_instantiate(dentry, inode);
- +
- + return ret;
- +}
- +
- +/**
- + * remove a directory
- + * @param dir parent directory inode
- + * @param dentry dentry corresponding with a directory will be removed
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_rmdir(struct inode *dir, struct dentry *dentry)
- +{
- + struct inode *inode = dentry->d_inode;
- + struct buffer_head *bh = NULL;
- + struct rfs_dir_entry *ep = NULL;
- + int ret;
- +
- + /* check the validity */
- + if (IS_RDONLY(dir))
- + return -EROFS;
- +
- + /* check the name length */
- + if (dentry->d_name.len > NAME_MAX)
- + return -ENAMETOOLONG;
- +
- + /* find dir entry */
- + ep = search_entry(dir, inode, &bh, dentry->d_name.name, TYPE_DIR);
- + if (IS_ERR(ep)) {
- + ret = PTR_ERR(ep);
- + brelse(bh);
- + return ret;
- + }
- + brelse(bh);
- +
- + if (((dir->i_ino == ROOT_INO) && (RFS_I(inode)->index < 0)) ||
- + ((dir->i_ino != ROOT_INO) && (RFS_I(inode)->index < 2)))
- + return -ENOENT;
- +
- + /* check whether directory is empty */
- + ret = is_dir_empty(inode);
- + if (ret)
- + return ret;
- +
- + /* RFS-log : start rmdir */
- + if (rfs_log_start(dir->i_sb, RFS_LOG_UNLINK, dir))
- + return -EIO;
- +
- + /* remove directory */
- + ret = rfs_delete_entry(dir, inode);
- +
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(dir->i_sb, ret))
- + return -EIO;
- +
- + return ret;
- +}
- +
- +/**
- + * change a directory
- + * @param r_info argument to move
- + * @return return 0 on success, errno on failure
- + */
- +static int move_to_dir(struct rename_info *r_info)
- +{
- + struct super_block *sb = r_info->old_dir->i_sb;
- + unsigned int old_index = RFS_I(r_info->old_inode)->index;
- + unsigned int *new_index = r_info->new_index;
- + struct buffer_head *old_bh = NULL;
- + struct buffer_head *new_bh = NULL;
- + struct rfs_dir_entry *old_ep = NULL;
- + struct rfs_dir_entry *new_ep = NULL;
- + unsigned int old_type;
- + int ret = 0;
- +
- + old_ep = get_entry(r_info->old_dir, old_index, &old_bh);
- + if (IS_ERR(old_ep)) {
- + ret = PTR_ERR(old_ep);
- + goto out;
- + }
- +
- + old_type = entry_type(old_ep);
- + if (old_type == TYPE_DIR || old_type == TYPE_FILE ||
- + old_type == TYPE_SYMLINK)
- + ret = build_entry(r_info->new_dir, NULL, START_CLUSTER(old_ep),
- + old_type, r_info->new_name);
- + else
- + ret = -EINVAL; /* out-of-range input */
- + if (ret < 0)
- + goto out;
- +
- + *new_index = (unsigned int) ret;
- +
- + /* update mtime of new entry */
- + new_ep = get_entry(r_info->new_dir, *new_index, &new_bh);
- + if (IS_ERR(new_ep)) {
- + ret = PTR_ERR(new_ep);
- + goto out;
- + }
- +
- + SET16(new_ep->mtime, GET16(old_ep->mtime));
- + SET16(new_ep->mdate, GET16(old_ep->mdate));
- + SET32(new_ep->size, GET32(old_ep->size));
- +
- + rfs_mark_buffer_dirty(new_bh, sb);
- +
- + if (old_type == TYPE_DIR) {
- + /* change pointer of parent dir */
- + old_ep = get_entry(r_info->old_inode, 1, &old_bh);
- + if (IS_ERR(old_ep)) {
- + ret = PTR_ERR(old_ep);
- + goto out;
- + }
- + if (ROOT_INO != r_info->new_dir->i_ino) {
- +
- + new_ep = get_entry(r_info->new_dir, 0, &new_bh);
- + if (IS_ERR(new_ep)) {
- + ret = PTR_ERR(new_ep);
- + goto out;
- + }
- + memcpy(old_ep, new_ep, sizeof(struct rfs_dir_entry));
- + memcpy(old_ep->name, DOTDOT, DOS_NAME_LENGTH);
- + } else {
- + SET16(old_ep->start_clu_lo, RFS_SB(sb)->root_clu);
- + SET16(old_ep->start_clu_hi, RFS_SB(sb)->root_clu >> 16);
- + set_entry_time(old_ep, ROOT_MTIME(sb));
- + }
- + rfs_mark_buffer_dirty(old_bh, sb);
- + }
- +
- + /* delete old entry */
- + ret = remove_entry(r_info->old_dir, old_index, &old_bh);
- +out:
- + brelse(new_bh);
- + brelse(old_bh);
- + return ret;
- +}
- +
- +/**
- + * change name or location of a file or directory
- + * @param old_dir old parent directory
- + * @param old_dentry old dentry
- + * @param new_dir new parent directory
- + * @param new_dentry new dentry
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
- +{
- + struct inode *old_inode = old_dentry->d_inode;
- + struct inode *new_inode = new_dentry->d_inode;
- + struct qstr *old_qname = &old_dentry->d_name;
- + struct qstr *new_qname = &new_dentry->d_name;
- + struct buffer_head *old_bh = NULL, *new_bh = NULL;
- + struct rfs_dir_entry *old_ep = NULL, *new_ep = NULL;
- + struct rename_info r_info;
- + unsigned long new_ino;
- + unsigned int new_index = 0;
- + int is_exist = FALSE;
- + int ret;
- +
- + /* check the validity */
- + if (IS_RDONLY(old_dir))
- + return -EROFS;
- +
- + /* check the name length */
- + if ((old_qname->len > NAME_MAX) ||
- + (new_qname->len > NAME_MAX))
- + return -ENAMETOOLONG;
- +
- + /* find old dir entry */
- + old_ep = search_entry(old_dir, old_inode, &old_bh,
- + old_qname->name, TYPE_ALL);
- + if (IS_ERR(old_ep)) {
- + ret = PTR_ERR(old_ep);
- + brelse(old_bh);
- + return ret;
- + }
- +
- + if (old_ep->attr & ATTR_READONLY) {
- + brelse(old_bh);
- + return -EPERM;
- + }
- +
- + brelse(old_bh);
- +
- + /* check permission */
- + if (check_reserved_files(old_inode, NULL))
- + return -EPERM;
- +
- + /* if new_inode is NULL, compare name */
- + if (check_reserved_files(new_inode, new_qname->name))
- + return -EPERM;
- +
- + if ((old_dir->i_ino != ROOT_INO) && (RFS_I(old_inode)->index < 2))
- + return -EINVAL;
- +
- + /* find new dir entry if exists, remove it */
- + new_ep = search_entry(new_dir, new_inode, &new_bh,
- + new_qname->name, TYPE_ALL);
- + if (!IS_ERR(new_ep)) {
- + if (S_ISDIR(new_inode->i_mode)) {
- + ret = is_dir_empty(new_inode);
- + if (ret) {
- + brelse(new_bh);
- + return ret;
- + }
- + }
- + /* mark is_exist for later deletion */
- + is_exist = TRUE;
- + }
- + brelse(new_bh);
- +
- + /* fill rename info */
- + r_info.old_dir = old_dir;
- + r_info.old_inode = old_inode;
- + r_info.new_dir = new_dir;
- + r_info.new_index = &new_index;
- + r_info.new_name = new_qname->name;
- +
- + /* RFS-log : start rename */
- + if (rfs_log_start(old_dir->i_sb, RFS_LOG_RENAME, old_inode))
- + return -EIO;
- +
- + ret = move_to_dir(&r_info);
- + if (ret)
- + goto end_log;
- +
- + /* delete destination, if exist */
- + if (is_exist == TRUE) {
- + ret = rfs_delete_entry(new_dir, new_inode);
- + if (ret)
- + goto end_log;
- + }
- +
- + /* update inode fields */
- + ret = rfs_iunique(new_dir, new_index, &new_ino);
- + if (ret)
- + goto end_log;
- +
- + remove_inode_hash(old_inode);
- +
- + /*
- + * don't change the order of statements for
- + * synchronization with rfs_sync_inode() in locked area
- + */
- + spin_lock(&RFS_I(old_inode)->write_lock);
- + old_inode->i_ino = new_ino;
- + RFS_I(old_inode)->p_start_clu = RFS_I(new_dir)->start_clu;
- + RFS_I(old_inode)->index = new_index;
- + spin_unlock(&RFS_I(old_inode)->write_lock);
- +
- + insert_inode_hash(old_inode);
- + /*
- + * we already reigsterd the inode to the transaction
- + * in the above move_to_dir()
- + */
- + mark_inode_dirty(old_inode);
- +
- + if (old_dir != new_dir) {
- + if (S_ISDIR(old_inode->i_mode)) {
- + old_dir->i_nlink--;
- + new_dir->i_nlink++;
- + }
- + new_dir->i_mtime = CURRENT_TIME;
- + rfs_mark_inode_dirty(new_dir);
- + }
- +
- + old_dir->i_mtime = CURRENT_TIME;
- + rfs_mark_inode_dirty(old_dir);
- +
- +end_log:
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(old_dir->i_sb, ret))
- + return -EIO;
- +
- + return ret;
- +}
- +
- +/**
- + * generate a symlink file
- + * @param dir parent directory inode
- + * @param dentry dentry corresponding with new symlink file
- + * @param target full link of target
- + * @return return 0 on success, errno on failure
- + */
- +static int rfs_symlink(struct inode *dir, struct dentry *dentry, const char *target)
- +{
- + struct inode *inode = NULL;
- + int len = strlen(target) + 1;
- + int ret;
- + const char *target_last;
- +
- + /* check the validity */
- + if (IS_RDONLY(dir))
- + return -EROFS;
- +
- + /* check the system files */
- + target_last = strrchr(target, SLASH);
- + if (target_last == NULL)
- + target_last = target;
- + else
- + target_last++;
- +
- + if (check_reserved_files(NULL, target_last))
- + return -EPERM;
- +
- + /* RFS-log : start create-symlink */
- + if (rfs_log_start(dir->i_sb, RFS_LOG_SYMLINK, dir))
- + return -EIO;
- +
- + /* create a symlink file */
- + inode = rfs_new_inode(dir, dentry, TYPE_SYMLINK);
- + if (IS_ERR(inode)) {
- + ret = PTR_ERR(inode);
- + goto end_log;
- + }
- +
- +#ifdef RFS_FOR_2_6
- + ret = page_symlink(inode, target, len);
- +#else
- + ret = block_symlink(inode, target, len);
- +#endif
- + if (ret == -ENOSPC) {
- + rfs_delete_entry(dir, inode);
- + iput(inode);
- + }
- +
- +end_log:
- + /* for transaction sync, we remember the inode of symbolic link. */
- + RFS_LOG_I(dir->i_sb)->symlink_inode = (ret) ? NULL : inode;
- +
- + /* If failed, RFS-log can't rollback due to media error */
- + if (rfs_log_end(dir->i_sb, ret))
- + return -EIO;
- +
- + if (!ret) /* attach inode to dentry */
- + d_instantiate(dentry, inode);
- +
- + return ret;
- +}
- +
- +struct inode_operations rfs_dir_inode_operations = {
- + .create = rfs_create,
- + .lookup = rfs_lookup,
- + .unlink = rfs_unlink,
- + .symlink = rfs_symlink,
- + .mkdir = rfs_mkdir,
- + .rmdir = rfs_rmdir,
- + .rename = rfs_rename,
- + .permission = rfs_permission,
- + .setattr = rfs_setattr,
- +};
- +
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/rfs_24.c linux-2.6.17.14.rfs/fs/rfs/rfs_24.c
- --- linux-2.6.17.14.orig/fs/rfs/rfs_24.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/rfs_24.c 2008-06-19 04:02:08.000000000 +0200
- @@ -0,0 +1,344 @@
- +/**
- + * @file fs/rfs/rfs_24.c
- + * @brief Kernel version 2.4 specified functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/module.h>
- +#include <linux/init.h>
- +#include <linux/iobuf.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +/**
- + * prepare the blocks and map them
- + * @param inode inode
- + * @param page page pointer
- + * @param from start offset within page
- + * @param to last offset within page
- + * @param get_block get_block funciton
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_block_prepare_write(struct inode * inode, struct page * page, unsigned from, unsigned to, get_block_t *get_block)
- +{
- + struct buffer_head *bh, *head;
- + unsigned long block;
- + unsigned block_start, block_end, blocksize, bbits;
- + int err = 0;
- + char *kaddr = kmap(page);
- +
- + bbits = inode->i_blkbits;
- + blocksize = 1 << bbits;
- +
- + if (!page->buffers)
- + create_empty_buffers(page, inode->i_dev, blocksize);
- + head = page->buffers;
- +
- + block = page->index << (PAGE_CACHE_SHIFT - bbits); /* start block # */
- +
- + /* we allocate buffers and map them */
- + for(bh = head, block_start = 0; bh != head || !block_start;
- + block++, block_start = block_end + 1, bh = bh->b_this_page) {
- + if (!bh) {
- + err = -EIO;
- + RFS_BUG("can't get buffer head\n");
- + goto out;
- + }
- + block_end = block_start + blocksize - 1;
- + if (block_end < from) {
- + continue;
- + } else if (block_start > to) {
- + break;
- + }
- + clear_bit(BH_New, &bh->b_state);
- +
- + /* map new buffer if necessary*/
- + if (!buffer_mapped(bh) || (inode->i_size <= (block<<(inode->i_sb->s_blocksize_bits)))) {
- + err = get_block(inode, block, bh, 1);
- + if (err) {
- + DEBUG(DL1, "no block\n");
- + goto out;
- + }
- + if (buffer_new(bh) && block_end > to) {
- + memset(kaddr+to+1, 0, block_end-to);
- + continue;
- + }
- + }
- + if (!buffer_uptodate(bh) &&
- + (block_start < from || block_end > to)) {
- + ll_rw_block(READ, 1, &bh);
- + wait_on_buffer(bh);
- + if (!buffer_uptodate(bh)) {
- + err = -EIO;
- + goto out;
- + }
- + }
- + }
- +out:
- + flush_dcache_page(page);
- + kunmap_atomic(kaddr, KM_USER0);
- + return err;
- +}
- +
- +/**
- + * block commit write
- + * @param inode inode
- + * @param page page pointer
- + * @param from start offset within page
- + * @param to last offset within page
- + * @return return 0 on success, errno on failure
- + */
- +int rfs_block_commit_write(struct inode *inode, struct page *page,
- + unsigned from, unsigned to)
- +{
- + unsigned block_start, block_end;
- + unsigned blocksize;
- + struct buffer_head *bh, *head;
- +
- + blocksize = 1 << inode->i_blkbits;
- +
- + for(bh = head = page->buffers, block_start = 0;
- + bh != head || !block_start;
- + block_start=block_end + 1, bh = bh->b_this_page) {
- + block_end = block_start + blocksize - 1;
- + if (block_end < from)
- + continue;
- + else if (block_start > to)
- + break;
- + else {
- + set_bit(BH_Uptodate, &bh->b_state);
- + __mark_buffer_dirty(bh);
- + down(&RFS_I(inode)->data_mutex);
- + buffer_insert_inode_data_queue(bh, inode);
- + up(&RFS_I(inode)->data_mutex);
- + }
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * wakeup function of logging process
- + * @param __data : super block
- + */
- +void rfs_log_wakeup(unsigned long __data)
- +{
- + struct super_block *sb = (struct super_block *) __data;
- + struct task_struct *p = RFS_SB(sb)->sleep_proc;
- +
- + mod_timer(&RFS_SB(sb)->timer, DATA_EXPIRES(jiffies));
- + if (p->state == TASK_UNINTERRUPTIBLE)
- + wake_up_process(p);
- +}
- +
- +/**
- + * wakeup function for a process committing data
- + * @param __data : private inode
- + */
- +void rfs_data_wakeup(unsigned long __data)
- +{
- + struct rfs_inode_info *rfsi = (struct rfs_inode_info *) __data;
- + struct task_struct *p = rfsi->sleep_proc;
- +
- + mod_timer(&rfsi->timer, DATA_EXPIRES(jiffies));
- + if (p->state == TASK_UNINTERRUPTIBLE)
- + wake_up_process(p);
- +}
- +
- +/**
- + * rfs_direct_IO - directly read/write from/to user space buffers without cache
- + * @param rw read/write type
- + * @param inode inode
- + * @param iobuf iobuf
- + * @param blocknr number of blocks
- + * @param blocksize block size
- + * @return write/read size on success, errno on failure
- + */
- +int rfs_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf, unsigned long blocknr, int blocksize)
- +{
- + struct super_block *sb = inode->i_sb;
- + loff_t offset, old_size = inode->i_size;
- + unsigned int alloc_clus = 0;
- + int zerofilled = FALSE, ret;
- +
- + offset = blocknr << sb->s_blocksize_bits;
- +
- + if (rw == WRITE) {
- + unsigned int clu_size, clu_bits;
- + unsigned int req_clus, free_clus;
- +
- + clu_size = RFS_SB(sb)->cluster_size;
- + clu_bits = RFS_SB(sb)->cluster_bits;
- +
- + /* compare the number of required clusters with free clusters */
- + alloc_clus = (inode->i_size + clu_size - 1) >> clu_bits;
- + req_clus = (offset + iobuf->length + clu_size - 1) >> clu_bits;
- + if (req_clus > alloc_clus)
- + req_clus -= alloc_clus; /* required clusters */
- + else
- + req_clus = 0;
- +
- + if (rfs_log_start(inode->i_sb, RFS_LOG_WRITE, inode))
- + return -EIO;
- +
- + free_clus = GET_FREE_CLUS(RFS_SB(sb));
- + if (req_clus > free_clus) {
- + DEBUG(DL1, "req_clus = %d free_clus = %d \n",
- + req_clus, free_clus);
- + ret = -ENOSPC;
- + goto end_log;
- + }
- +
- + /* lseek case in direct IO */
- + if (offset > old_size) {
- + /*
- + * NOTE: In spite of direc IO,
- + * we use page cache for extend_with_zeorfill
- + */
- + ret = extend_with_zerofill(inode,
- + (u32) old_size, (u32) offset);
- + if (ret)
- + goto end_log;
- +
- + inode->i_size = offset;
- + set_mmu_private(inode, offset);
- + zerofilled = TRUE;
- + }
- + }
- +
- + ret = generic_direct_IO(rw, inode, iobuf,
- + blocknr, blocksize, rfs_get_block);
- +
- + if (rw == WRITE) {
- + if (ret == -EINVAL) {
- + int err;
- +
- + /*
- + * free some clusters if only unaligned offset & length
- + * of iobuf exists which were allocated at direct IO op
- + */
- + err = dealloc_clusters(inode, alloc_clus);
- + if (!err) {
- + inode->i_size = old_size;
- + set_mmu_private(inode, old_size);
- + }
- +
- + /* invalidate hint info */
- + rfs_invalidate_hint(inode);
- + }
- +
- +end_log:
- + if (rfs_log_end(inode->i_sb, (ret >= 0) ? 0 : -EIO))
- + return -EIO;
- +
- + if (!ret && zerofilled)
- + ret = fsync_inode_data_buffers(inode);
- + }
- +
- + return ret;
- +}
- +
- +/**
- + * build the super block
- + * @param sb super block
- + * @param data private data
- + * @param silent verbose flag
- + * @return return super block pointer on success, null on failure
- + *
- + * initialize the super block, system file such as logfile & poolfile and recovery error by sudden power off
- + */
- +static struct super_block *rfs_read_super(struct super_block *sb, void *data, int silent)
- +{
- + struct super_block *res = NULL;
- + unsigned int used_clusters;
- + int err;
- +
- + res = rfs_common_read_super(sb, data, silent);
- + if (!res)
- + return NULL;
- +#ifdef CONFIG_RFS_VFAT
- + RFS_SB(sb)->options.isvfat = TRUE;
- +#else
- + if (IS_FAT32(RFS_SB(res))) {
- + DPRINTK("invalid fat type\n");
- + return NULL;
- + }
- +
- + sb->s_root->d_op = &rfs_dentry_operations;
- + RFS_SB(sb)->options.isvfat = FALSE;
- +#endif
- + sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
- +
- + if (rfs_init_pool(sb)) {
- + DPRINTK("fast unlink can not be supported\n");
- + return NULL;
- + }
- +
- + if (rfs_log_init(sb)) {
- + DPRINTK("RFS-log : Not supported\n");
- + return NULL;
- + }
- +
- + if (rfs_remove_candidates(sb)) {
- + DPRINTK("Can not remove candidate segments in pool file\n");
- + return NULL;
- + }
- +
- + /* update total number of used clusters */
- + err = count_used_clusters(sb, &used_clusters);
- + if (err) { /* I/O error */
- + DPRINTK("FAT has something wrong\n");
- + return NULL;
- + }
- +
- + RFS_SB(sb)->num_used_clusters = used_clusters
- + - (RFS_POOL_I(sb)->num_clusters - POOL_RESERVED_CLUSTER);
- +
- + return res;
- +}
- +
- +static DECLARE_FSTYPE_DEV(rfs_fs_type, "rfs", rfs_read_super);
- +
- +/**
- + * register RFS file system
- + */
- +static int __init init_rfs_fs(void)
- +{
- + return register_filesystem(&rfs_fs_type);
- +}
- +
- +/**
- + * unregister RFS file system
- + */
- +static void __exit exit_rfs_fs(void)
- +{
- + unregister_filesystem(&rfs_fs_type);
- +}
- +
- +module_init(init_rfs_fs);
- +module_exit(exit_rfs_fs);
- +
- +MODULE_LICENSE("Samsung, Proprietary");
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/rfs_26.c linux-2.6.17.14.rfs/fs/rfs/rfs_26.c
- --- linux-2.6.17.14.orig/fs/rfs/rfs_26.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/rfs_26.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,407 @@
- +/**
- + * @file fs/rfs/rfs_26.c
- + * @brief Kernel version 2.6 specified functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/module.h>
- +#include <linux/init.h>
- +#include <linux/uio.h>
- +#include <linux/writeback.h>
- +#include <linux/rfs_fs.h>
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +
- +#ifdef RFS_FOR_2_6_17
- +/*
- + * In linux 2.6.17 or more, the callback function in direct io is changed.
- + * Now, it is used to single get block instead of multiple get blocks.
- + */
- +#define rfs_get_blocks rfs_get_block
- +
- +#else /* !RFS_FOR_2_6_17 */
- +/**
- + * Function to translate a logical block into physical block
- + * @param inode inode
- + * @param iblock logical block number
- + * @param max_blocks dummy variable new
- + * @param bh_result buffer head pointer
- + * @param create control flag
- + * @return zero on success, negative value on failure
- + *
- + * This function is only invoked by direct IO
- + */
- +static int rfs_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create)
- +{
- + int ret;
- +
- + ret = rfs_get_block(inode, iblock, bh_result, create);
- + if (!ret)
- + bh_result->b_size = (1 << inode->i_blkbits);
- + return ret;
- +}
- +#endif /* RFS_FOR_2_6_17 */
- +
- +#ifndef RFS_FOR_2_6_18
- +/*
- + * In linux 2.6.18 or more, struct writeback_control file is changed
- + * from start, end to range_start, range_end
- + */
- +#define range_start start
- +#define range_end end
- +#endif /* !RFS_FOR_2_6_18 */
- +
- +/**
- + * Write and wait upon the last page for inode
- + * @param inode inode pointer
- + * @return zero on success, negative value on failure
- + *
- + * This is a data integrity operation for a combination of
- + * zerofill and direct IO write
- + */
- +static int sync_last_page(struct inode *inode)
- +{
- + loff_t lstart = (i_size_read(inode) - 1) & PAGE_CACHE_MASK;
- + struct address_space *mapping = inode->i_mapping;
- + struct writeback_control wbc = {
- + .sync_mode = WB_SYNC_ALL,
- + .range_start = lstart,
- + .range_end = lstart + PAGE_CACHE_SIZE - 1,
- + };
- +
- + /*
- + * Note: There's race condition. We don't use page cache operation
- + * directly.
- + */
- + return mapping->a_ops->writepages(mapping, &wbc);
- +}
- +
- +#define rfs_flush_cache(iov, nr_segs) do { } while (0)
- +
- +/**
- + * RFS function excuting direct I/O operation
- + * @param rw I/O command
- + * @param iocb VFS kiocb pointer
- + * @param iov VFS iovc pointer
- + * @param offset I/O offset
- + * @param nr_segs the number segments
- + * @return written or read date size on sucess, negative value on failure
- + */
- +ssize_t rfs_direct_IO(int rw, struct kiocb * iocb, const struct iovec *iov,
- + loff_t offset, unsigned long nr_segs)
- +{
- + struct inode *inode = iocb->ki_filp->f_mapping->host;
- + struct super_block *sb = inode->i_sb;
- + int ret = 0;
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +/*
- + * Note: We *MUST* use the correct API in direct IO.
- + * It is correct place when use gcov
- + */
- +#define loff_t off_t
- +#endif
- +
- + if (rw == WRITE) {
- + unsigned int clu_size, clu_bits;
- + unsigned int alloc_clus, req_clus, free_clus;
- + size_t write_len = iov_length(iov, nr_segs);
- + loff_t i_size = i_size_read(inode);
- +
- + clu_size = RFS_SB(sb)->cluster_size;
- + clu_bits = RFS_SB(sb)->cluster_bits;
- +
- + /* compare the number of required clusters with free clusters */
- + alloc_clus = (i_size + clu_size - 1) >> clu_bits;
- + req_clus = (offset + write_len + clu_size - 1) >> clu_bits;
- + if (req_clus > alloc_clus)
- + req_clus -= alloc_clus;
- + else
- + req_clus = 0;
- +
- + if (rfs_log_start(inode->i_sb, RFS_LOG_WRITE, inode))
- + return -EIO;
- +
- + free_clus = GET_FREE_CLUS(RFS_SB(sb));
- + if (req_clus > free_clus) {
- + DEBUG(DL1, "req_clus = %d free_clus = %d \n",
- + req_clus, free_clus);
- + ret = -ENOSPC;
- + goto end_log;
- + }
- +
- + /*
- + * lseek case in direct IO
- + * Note: We have to cast 'offset' as loff_t
- + * to correct operation in kernel gcov
- + * the loff_t means off_t in gcov mode
- + */
- + if ((loff_t) offset > i_size) {
- + /*
- + * NOTE: In spite of direc IO,
- + * we use page cache for extend_with_zerofill
- + */
- + ret = extend_with_zerofill(inode,
- + (u32) i_size,
- + (u32) offset);
- + if (ret)
- + goto end_log;
- +
- + i_size_write(inode, offset);
- + set_mmu_private(inode, offset);
- +
- + ret = sync_last_page(inode);
- + if (ret)
- + /*
- + * it is possible that real allocated clusters
- + * and size in dir entry can be different
- + */
- + goto end_log;
- + }
- + }
- +
- + /* flush cache */
- + rfs_flush_cache(iov, nr_segs);
- +
- + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
- + offset, nr_segs, rfs_get_blocks, NULL);
- +
- + if (rw == WRITE) {
- +end_log:
- + if (rfs_log_end(inode->i_sb, (ret >= 0) ? 0 : -EIO))
- + return -EIO;
- + }
- +
- + return ret;
- +}
- +
- +#ifdef CONFIG_GCOV_PROFILE
- +#undef loff_t
- +#endif
- +
- +/**
- + * Function to build super block structure
- + * @param sb super block pointer
- + * @param data pointer for an optional date
- + * @param silent control flag for error message
- + * @return zero on success, a negative error code on failure
- + *
- + * Initialize the super block, system file such as logfile & poolfile and recovery error by sudden power off
- + */
- +static int rfs_fill_super(struct super_block *sb, void *data, int silent)
- +{
- + unsigned int used_clusters;
- + int err = 0;
- +
- + sb = rfs_common_read_super(sb, data, silent);
- + if (!sb)
- + return -EINVAL;
- +
- +#ifdef CONFIG_RFS_VFAT
- + RFS_SB(sb)->options.isvfat = TRUE;
- +#else
- + if (IS_FAT32(RFS_SB(sb))) {
- + DPRINTK("invalid fat type\n");
- + return -EINVAL;
- + }
- +
- + RFS_SB(sb)->options.isvfat = FALSE;
- +
- + sb->s_root->d_op = &rfs_dentry_operations;
- +#endif
- + sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
- +
- + if (rfs_init_pool(sb)) {
- + DPRINTK("fast unlink can not be supported\n");
- + return -EINVAL;
- + }
- +
- + if (rfs_log_init(sb)) {
- + DPRINTK("RFS-log : Not supported\n");
- + return -EINVAL;
- + }
- +
- + if (rfs_remove_candidates(sb)) {
- + DPRINTK("Can not remove candidate segments in pool file\n");
- + return -EIO;
- + }
- +
- + /* update total number of used clusters */
- + err = count_used_clusters(sb, &used_clusters);
- + if (err) { /* I/O error */
- + DPRINTK("FAT has something wrong\n");
- + return err;
- + }
- +
- + RFS_SB(sb)->num_used_clusters = used_clusters
- + - (RFS_POOL_I(sb)->num_clusters - POOL_RESERVED_CLUSTER);
- +
- + return err;
- +}
- +
- +/* local variable definition */
- +static kmem_cache_t *rfs_inode_cachep = NULL;
- +
- +/* static function definition */
- +/**
- + * Function to initialized a newly create rfs specific inode structure
- + * @param foo memory pointer for new inode structure
- + * @param cachep a pointer for inode cache
- + * @param flags control flag
- + */
- +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
- +{
- + struct rfs_inode_info *ei = (struct rfs_inode_info *) foo;
- +
- + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
- + SLAB_CTOR_CONSTRUCTOR)
- + inode_init_once(&ei->vfs_inode);
- +}
- +
- +/**
- + * Function to initialize an inode cache
- + */
- +static int __init rfs_init_inodecache(void)
- +{
- + rfs_inode_cachep = kmem_cache_create("rfs_inode_cache",
- + sizeof(struct rfs_inode_info),
- + 0, SLAB_RECLAIM_ACCOUNT,
- + init_once, NULL);
- + if (!rfs_inode_cachep)
- + return -ENOMEM;
- +
- + return 0;
- +}
- +
- +/**
- + * Function to destroy an inode cache
- + */
- +static void rfs_destroy_inodecache(void)
- +{
- + /*
- + * kmem_cache_destroy return type is changed
- + * from 'int' to 'void' after 2.6.19
- + */
- + kmem_cache_destroy(rfs_inode_cachep);
- +}
- +
- +/**
- + * Function to allocate rfs specific inode and associate it with vfs inode
- + * @param sb super block pointer
- + * @return a pointer of new inode on success, NULL on failure
- + */
- +struct inode *rfs_alloc_inode(struct super_block *sb)
- +{
- + struct rfs_inode_info *new;
- +
- + new = kmem_cache_alloc(rfs_inode_cachep, SLAB_KERNEL);
- + if (!new)
- + return NULL;
- +
- + /* initialize rfs inode info, if necessary */
- + new->i_state = RFS_I_ALLOC;
- +
- + return &new->vfs_inode;
- +}
- +/**
- + * Function to deallocate rfs specific inode
- + * @param inode inode pointer
- + */
- +void rfs_destroy_inode(struct inode *inode)
- +{
- + if (!inode)
- + printk("inode is NULL \n");
- +
- + kmem_cache_free(rfs_inode_cachep, RFS_I(inode));
- +}
- +
- +/**
- + * Interface function for super block initialization
- + * @param fs_type filesystem type
- + * @param flags flag
- + * @param dev_name name of file system
- + * @param data private date
- + * @return a pointer of super block on success, negative error code on failure
- + */
- +#ifdef RFS_FOR_2_6_18
- +static int rfs_get_sb(struct file_system_type *fs_type,
- + int flags, const char *dev_name, void *data, struct vfsmount *mnt)
- +{
- + return get_sb_bdev(fs_type, flags, dev_name, data, rfs_fill_super, mnt);
- +}
- +#else
- +static struct super_block *rfs_get_sb(struct file_system_type *fs_type,
- + int flags, const char *dev_name, void *data)
- +{
- + return get_sb_bdev(fs_type, flags, dev_name, data, rfs_fill_super);
- +}
- +#endif /* RFS_FOR_2_6_18 */
- +
- +/* rfs filesystem type defintion */
- +static struct file_system_type rfs_fs_type = {
- + .owner = THIS_MODULE,
- + .name = "rfs",
- + .get_sb = rfs_get_sb,
- + .kill_sb = kill_block_super,
- + .fs_flags = FS_REQUIRES_DEV,
- +};
- +
- +/**
- + * Init function for rfs
- + */
- +static int __init init_rfs_fs(void)
- +{
- + int err = 0;
- +
- + /* init inode cache */
- + err = rfs_init_inodecache();
- + if (err)
- + goto fail_init;
- +
- + err = register_filesystem(&rfs_fs_type);
- + if (err)
- + goto fail_register;
- +
- + return 0;
- +
- +fail_register:
- + rfs_destroy_inodecache();
- +fail_init:
- + return err;
- +}
- +
- +/**
- + * Exit function for rfs
- + */
- +static void __exit exit_rfs_fs(void)
- +{
- + rfs_destroy_inodecache();
- + unregister_filesystem(&rfs_fs_type);
- +}
- +
- +module_init(init_rfs_fs);
- +module_exit(exit_rfs_fs);
- +
- +MODULE_LICENSE("Samsung, Proprietary");
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/rfs.h linux-2.6.17.14.rfs/fs/rfs/rfs.h
- --- linux-2.6.17.14.orig/fs/rfs/rfs.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/rfs.h 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,279 @@
- +/**
- + * @file fs/rfs/rfs.h
- + * @brief local header file for rfs
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#ifndef _RFS_H
- +#define _RFS_H
- +
- +#include <linux/sched.h>
- +#include <linux/version.h>
- +#include <asm/semaphore.h>
- +
- +/*
- + * kernel version macro
- + */
- +#undef RFS_FOR_2_4
- +#undef RFS_FOR_2_6
- +#undef RFS_FOR_2_6_17
- +#undef RFS_FOR_2_6_18
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +#define RFS_FOR_2_6 1
- +#else
- +#define RFS_FOR_2_4 1
- +#endif
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
- +#define RFS_FOR_2_6_18 1
- +#endif
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
- +#define RFS_FOR_2_6_17 1
- +#endif
- +
- +
- +typedef enum rfs_lock_type {
- + RFS_FAT_LOCK,
- + RFS_LOG_LOCK,
- +} rfs_lock_type_t;
- +
- +/*
- + * for locking
- + */
- +#define fat_lock(sb) rfs_down(RFS_SB(sb)->fat_mutex, RFS_FAT_LOCK)
- +#define fat_unlock(sb) rfs_up(RFS_SB(sb)->fat_mutex, RFS_FAT_LOCK)
- +#define init_fat_lock(sb) rfs_init_mutex(RFS_SB(sb)->fat_mutex, sb)
- +
- +#define lock_log(sb) rfs_down(RFS_LOG_I(sb)->log_mutex, RFS_LOG_LOCK)
- +#define unlock_log(sb) rfs_up(RFS_LOG_I(sb)->log_mutex, RFS_LOG_LOCK)
- +#define init_log_lock(sb) rfs_init_mutex(RFS_LOG_I(sb)->log_mutex, sb)
- +#define get_log_lock_depth(sb) rfs_get_lock_depth(RFS_LOG_I(sb)->log_mutex)
- +#define get_log_lock_owner(sb) rfs_get_lock_owner(RFS_LOG_I(sb)->log_mutex)
- +
- +#define IS_LOG_LOCK(type) ((type) == RFS_LOG_LOCK)
- +
- +/*
- + * for debugging
- + */
- +#define DL0 (0) /* Quiet */
- +#define DL1 (1) /* Audible */
- +#define DL2 (2) /* Loud */
- +#define DL3 (3) /* Noisy */
- +
- +#define DPRINTK(format, args...) \
- +do { \
- + printk("%s[%d]: " format, __func__ , __LINE__, ##args); \
- +} while (0)
- +
- +#undef DEBUG
- +
- +#ifdef CONFIG_RFS_FAT_DEBUG
- +
- +#define DEBUG(n, format, arg...) \
- +do { \
- + if (n <= CONFIG_RFS_FAT_DEBUG_VERBOSE) { \
- + printk("%s[%d]: " format "\n", \
- + __func__, __LINE__, ##arg); \
- + } \
- +} while(0)
- +
- +#define RFS_BUG(format, args...) \
- +do { \
- + DPRINTK(format, ##args); \
- + BUG(); \
- +} while (0)
- +
- +#define RFS_BUG_ON(condition) \
- +do { \
- + BUG_ON(condition); \
- +} while (0)
- +
- +#else
- +
- +#define DEBUG(n, arg...) do { } while (0)
- +#define RFS_BUG(format, args...) DPRINTK(format, ##args)
- +#define RFS_BUG_ON(condition) do { } while (0)
- +
- +#endif /* CONFIG_RFS_FAT_DEBUG */
- +
- +#define DATA_EXPIRES(j) ((j) + CONFIG_RFS_LOG_WAKEUP_DELAY)
- +
- +#define IS_INVAL_CLU(sbi, clu) \
- + ((clu < VALID_CLU || clu >= (sbi)->num_clusters) ? 1 : 0)
- +
- +#define CHECK_RFS_INODE(inode, errno) \
- +do { \
- + if (inode->i_ino != ROOT_INO) { \
- + if (RFS_I(inode)->p_start_clu >= \
- + RFS_SB(inode->i_sb)->num_clusters \
- + || RFS_I(inode)->start_clu < VALID_CLU) { \
- + RFS_BUG("out of range (%u, %u, %u)", \
- + RFS_I(inode)->index, \
- + RFS_I(inode)->p_start_clu, \
- + RFS_I(inode)->start_clu); \
- + return errno; \
- + } \
- + } \
- +} while (0)
- +
- +struct rfs_semaphore {
- + struct semaphore mutex;
- + pid_t owner;
- + int depth;
- + struct super_block *sb;
- +};
- +
- +void rfs_truncate(struct inode *);
- +int rfs_sync_inode(struct inode *, int, int);
- +
- +/**
- + * down the mutex
- + * @param lock a specific lock structure
- + * @param type lock type
- + * @return return the modified lock depth
- +*/
- +static inline int rfs_down(struct rfs_semaphore *lock, rfs_lock_type_t type)
- +{
- + if (lock->owner == current->pid && lock->depth >= 1) {
- + /* recursive lock */
- + lock->depth++;
- + DEBUG(DL3, "owner = %d depth = %d", lock->owner, lock->depth);
- + return lock->depth;
- + }
- +
- + /* first acquire */
- + down(&lock->mutex);
- +
- +#ifdef RFS_FOR_2_4
- + if (IS_LOG_LOCK(type)) {
- + /* register timer to avoid indefinite hang in wait_on_buffer */
- + RFS_SB(lock->sb)->sleep_proc = current;
- + RFS_SB(lock->sb)->timer.expires = DATA_EXPIRES(jiffies);
- + add_timer(&RFS_SB(lock->sb)->timer);
- + }
- +#endif
- +
- + lock->owner = current->pid;
- + lock->depth++;
- +
- + DEBUG(DL3, "owner = %d depth = %d", lock->owner, lock->depth);
- +
- + return lock->depth;
- +}
- +
- +/**
- + * up the mutex
- + * @param lock a specific lock structure
- + * @param type lock type
- + * @return return the modified lock depth
- + */
- +static inline int rfs_up(struct rfs_semaphore *lock, rfs_lock_type_t type)
- +{
- + if (lock->depth > 1) {
- + lock->depth--;
- + } else {
- + DEBUG(DL3, "owner = %d depth = %d", lock->owner, lock->depth);
- +
- + lock->owner = -1;
- + lock->depth--;
- +
- +#ifdef RFS_FOR_2_4
- + if (IS_LOG_LOCK(type))
- + del_timer_sync(&RFS_SB(lock->sb)->timer);
- +#endif
- +
- + up(&lock->mutex);
- + }
- + return lock->depth;
- +}
- +
- +#ifdef RFS_FOR_2_4
- +/**
- + * down data mutex of ordered transaction for kernel2.4
- + * @param rfsi a native inode info
- + */
- +static inline void rfs_data_down(struct rfs_inode_info *rfsi)
- +{
- + down(&rfsi->data_mutex);
- +
- + /* register timer to avoid indefinite hang in wait_on_buffer */
- + rfsi->sleep_proc = current;
- + rfsi->timer.expires = DATA_EXPIRES(jiffies);
- + add_timer(&rfsi->timer);
- +}
- +
- +/**
- + * up data mutex of ordered transaction for kernel2.4
- + * @param rfsi a private inode
- + */
- +static inline void rfs_data_up(struct rfs_inode_info *rfsi)
- +{
- + del_timer_sync(&rfsi->timer);
- + up(&rfsi->data_mutex);
- +}
- +#endif
- +
- +/**
- + * init the mutex
- + * @param lock a specific lock structure
- + * @param sb super block
- +*/
- +static inline void rfs_init_mutex(struct rfs_semaphore *lock,
- + struct super_block *sb)
- +{
- + init_MUTEX(&lock->mutex);
- + lock->owner = -1;
- + lock->depth = 0;
- + lock->sb = sb;
- +}
- +
- +/**
- + * get the current depth
- + * @param lock a specific lock structure
- + * @return return the current lock depth
- + */
- +static inline int rfs_get_lock_depth(struct rfs_semaphore *lock)
- +{
- + return lock->depth;
- +}
- +
- +/**
- + * get the current lock owner
- + * @param lock a specific lock structure
- + * @return return the current lock owner
- + */
- +static inline int rfs_get_lock_owner(struct rfs_semaphore *lock)
- +{
- + return lock->owner;
- +}
- +
- +inline static struct buffer_head * rfs_bread(struct super_block *sb,
- + int block, int rfs_state)
- +{
- + struct buffer_head *bh;
- + bh = sb_bread(sb, block);
- +
- + return bh;
- +}
- +#endif /* _RFS_H */
- diff -NurbB linux-2.6.17.14.orig/fs/rfs/super.c linux-2.6.17.14.rfs/fs/rfs/super.c
- --- linux-2.6.17.14.orig/fs/rfs/super.c 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/fs/rfs/super.c 2008-06-19 04:02:09.000000000 +0200
- @@ -0,0 +1,668 @@
- +/**
- + * @file fs/rfs/super.c
- + * @brief super block and init functions
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + *
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/bitops.h>
- +#include <linux/blkdev.h>
- +#include <linux/rfs_fs.h>
- +#include <linux/nls.h>
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +#include <linux/parser.h>
- +#include <linux/writeback.h>
- +#include <linux/statfs.h>
- +#endif
- +
- +#include "rfs.h"
- +#include "log.h"
- +
- +#ifdef RFS_FOR_2_6
- +enum {
- + opt_codepage, opt_err,
- +};
- +
- +static match_table_t rfs_tokens = {
- + {opt_codepage, "codepage=%s"},
- + {opt_err, NULL}
- +};
- +#endif
- +
- +#ifdef CONFIG_RFS_NLS
- +static const char rfs_default_codepage[] = CONFIG_RFS_DEFAULT_CODEPAGE;
- +#endif
- +
- +/**
- + * write the super block especially commit the previous transaction and flush the fat cache
- + * @param sb super block
- + */
- +static void rfs_write_super(struct super_block *sb)
- +{
- + if ((sb->s_flags & MS_RDONLY))
- + return;
- +
- + rfs_log_force_commit(sb, NULL);
- +
- + sb->s_dirt = 0;
- +}
- +
- +/**
- + * get statistics on a file system
- + * @param sb super block
- + * @param stat structure to fill stat info
- + * @return return 0
- + */
- +#ifdef RFS_FOR_2_6_18
- +static int rfs_statfs(struct dentry *dentry, struct kstatfs *stat)
- +#elif RFS_FOR_2_6
- +static int rfs_statfs(struct super_block *sb, struct kstatfs *stat)
- +#else
- +static int rfs_statfs(struct super_block *sb, struct statfs *stat)
- +#endif
- +{
- +#ifdef RFS_FOR_2_6_18
- + struct super_block *sb = dentry->d_sb;
- +#endif
- + int used_clusters = RFS_SB(sb)->num_used_clusters;
- +
- + stat->f_type = RFS_MAGIC;
- + stat->f_bsize = RFS_SB(sb)->cluster_size;
- + stat->f_blocks = RFS_SB(sb)->num_clusters;
- + stat->f_bfree = RFS_SB(sb)->num_clusters - used_clusters;
- + stat->f_bavail = stat->f_bfree;
- + stat->f_namelen = MAX_NAME_LENGTH;
- +
- + return 0;
- +}
- +
- +/**
- + * allow to remount to make a writable file system readonly
- + * @param sb super block
- + * @param flags to chang the mount flags
- + * @param data private data
- + * @return return 0
- + */
- +static int rfs_remount(struct super_block *sb, int *flags, char *data)
- +{
- + if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
- + if (*flags & MS_RDONLY) {
- + sb->s_flags |= MS_RDONLY;
- + *flags |= MS_RDONLY;
- + }
- + } else {
- + if (!(sb->s_flags & MS_RDONLY)) {
- + sb->s_flags &= ~MS_RDONLY;
- + *flags &= ~MS_RDONLY;
- + }
- + }
- +
- + *flags |= MS_NOATIME | MS_NODIRATIME;
- +
- + return 0;
- +}
- +
- +/**
- + * release the super block
- + * @param sb super block
- + *
- + * release log, internal fat cache, and the pool file memory
- + */
- +static void rfs_put_super(struct super_block *sb)
- +{
- +#ifdef RFS_FOR_2_6
- + struct rfs_sb_info *sbi = RFS_SB(sb);
- +#endif
- +
- + /* It precedes rfs_fcache_release because
- + it enventually calls rfs_fcache_sync */
- + rfs_log_cleanup(sb);
- +
- + rfs_fcache_release(sb);
- +
- + rfs_release_pool(sb);
- +
- + kfree(RFS_SB(sb)->fat_mutex);
- +
- +#ifdef RFS_FOR_2_6
- + if (!sbi) {
- + RFS_BUG("rfs-specific sb is corrrupted\n");
- + return;
- + }
- +
- + sb->s_fs_info = NULL;
- + kfree(sbi);
- +#endif
- + return;
- +}
- +
- +static struct super_operations rfs_sops = {
- +#ifdef RFS_FOR_2_6
- + .alloc_inode = rfs_alloc_inode,
- + .destroy_inode = rfs_destroy_inode,
- +#endif
- +#ifdef CONFIG_RFS_IGET4
- + .read_inode2 = rfs_read_inode2,
- +#endif
- + .write_inode = rfs_write_inode,
- + .delete_inode = rfs_delete_inode,
- + .put_super = rfs_put_super,
- + .write_super = rfs_write_super,
- + .statfs = rfs_statfs,
- + .remount_fs = rfs_remount,
- +};
- +
- +/**
- + * get the partition boot record of the super block
- + * @param sb super block
- + * @param[out] res buffer head contains pbr info which will be released by caller
- + * @param[out] pbr_sector number of pbr sector
- + * @return return 0 on success, errno on failure
- + */
- +static int get_pbr_info(struct super_block *sb, struct buffer_head **res, unsigned int *pbr_sector)
- +{
- + struct buffer_head *bh;
- + struct mbr *mbr_p;
- + struct pbr *pbr_p;
- + struct part_entry *pte_p;
- + unsigned int start_sector;
- +
- + /* read MBR sector */
- + bh = rfs_bread(sb, 0, BH_RFS_MBR);
- + if (!bh) { /* I/O error */
- + DPRINTK("unable to read MBR sector \n");
- + return -EIO;
- + }
- +
- + mbr_p = (struct mbr *) bh->b_data;
- +
- + if ((u16) SIGNATURE != GET16(mbr_p->signature)) {
- + DPRINTK("invalid MBR signature (%x != %x)\n",
- + SIGNATURE, GET16(mbr_p->signature));
- + brelse(bh);
- + return -EINVAL;
- + }
- +
- + /* get partition entry */
- + pte_p = (struct part_entry *) mbr_p->partition;
- + start_sector = GET32(pte_p->start_sector);
- + brelse(bh);
- +
- + /* read PBR sector */
- + bh = rfs_bread(sb, start_sector, BH_RFS_MBR);
- + if (!bh) { /* I/O error */
- + DPRINTK("unable to read PBR sector \n");
- + return -EIO;
- + }
- +
- + pbr_p = (struct pbr *) bh->b_data;
- + if ((u16) SIGNATURE != GET16(pbr_p->signature)) {
- + DPRINTK("invalid boot sector signature (%x != %x)\n",
- + SIGNATURE, GET16(pbr_p->signature));
- + brelse(bh);
- + return -EINVAL;
- + }
- +
- + /* set return value */
- + *res = bh;
- + *pbr_sector = start_sector;
- +
- + return 0;
- +}
- +
- +/**
- + * parse the mount options
- + * @param sb super block
- + * @param options mount options
- + * @return zero on success
- + */
- +int parse_option(struct super_block *sb, char *options)
- +{
- +#ifdef RFS_FOR_2_6
- + substring_t args[MAX_OPT_ARGS];
- +#endif
- + struct rfs_mount_info *opts = &(RFS_SB(sb)->options);
- + char *codepage;
- + char *p;
- +
- + opts->codepage = NULL;
- +
- + if (!options)
- + goto out;
- +
- +#ifdef RFS_FOR_2_6
- + while ((p = strsep(&options, ",")) != NULL) {
- + int token;
- + if (!*p)
- + continue;
- +
- + token = match_token(p, rfs_tokens, args);
- +
- + switch (token) {
- + /* NLS codepage used in disk */
- + case opt_codepage:
- + codepage = match_strdup(&args[0]);
- + if (!codepage)
- + return -ENOENT;
- + opts->codepage = codepage;
- + break;
- + default:
- + return -EINVAL;
- + }
- + }
- +#else
- + p = strtok(options, ",");
- + if (p == NULL)
- + goto out;
- +
- + do {
- + if (!strncmp(p, "codepage=", 9)) {
- + codepage = strchr(p, '=');
- + if (!codepage)
- + return -ENOENT;
- + opts->codepage = codepage + 1;
- + } else {
- + return -EINVAL;
- + }
- + } while((p = strtok(NULL, ",")) != NULL);
- +#endif
- +
- +out:
- +#ifdef CONFIG_RFS_NLS
- + if (opts->codepage == NULL) {
- + if (strcmp(rfs_default_codepage, "")) {
- + opts->codepage = (char *) rfs_default_codepage;
- + DEBUG(DL0, "Use default codepage %s\n", opts->codepage);
- + } else {
- + DPRINTK("If you configure the NLS, you must select codepage\n");
- + return -EINVAL;
- + }
- + }
- +#endif
- + return 0;
- +}
- +
- +/**
- + * fill up the RFS-specific super block
- + * @param sb super block
- + * @param old_blksize original block size of block device
- + * @param[out] new_blksize new block size of the file system will be set
- + * @return return 0 on success, errno on failure
- + *
- + * choose a minimum value between cluster size and block size of block device
- + */
- +static int rfs_build_sb(struct super_block *sb, unsigned long old_blksize, unsigned long *new_blksize)
- +{
- + struct buffer_head *bh;
- + struct pbr *pbr_p;
- + struct bpb *bpb_p;
- + unsigned int pbr_sector = 0;
- + unsigned short sector_size, sector_bits;
- + unsigned int num_sectors, num_reserved, fat_sectors, root_sectors;
- + unsigned int fat_start_sector, root_start_sector, data_start_sector;
- + unsigned int num_root_entries = MAX_ROOT_DENTRY, root_clu = 0;
- + unsigned int sectors_per_blk, sectors_per_blk_bits;
- + unsigned int num_blks, block_size;
- + loff_t device_size = sb->s_bdev->bd_inode->i_size;
- +
- + /* get PBR sector */
- + if (get_pbr_info(sb, &bh, &pbr_sector))
- + return -EINVAL;
- +
- + /* fill private info of sb */
- + pbr_p = (struct pbr *) bh->b_data;
- + bpb_p = (struct bpb *) pbr_p->bpb;
- +
- + /* get logical sector size */
- + sector_size = GET16(bpb_p->sector_size);
- + if (!sector_size || ((u32) sector_size > PAGE_CACHE_SIZE)) {
- + DPRINTK("invalid logical sector size : %d\n", sector_size);
- + brelse(bh);
- + return -EINVAL;
- + }
- + sector_bits = ffs(sector_size) - 1;
- +
- + /* get reserved, fat, root sectors */
- + num_reserved = GET16(bpb_p->num_reserved);
- + fat_sectors = GET16(bpb_p->num_fat_sectors);
- + root_sectors = GET16(bpb_p->num_root_entries) << DENTRY_SIZE_BITS;
- + root_sectors = ((root_sectors - 1) >> sector_bits) + 1;
- + if (!fat_sectors && !GET16(bpb_p->num_root_entries)) {
- + /* when fat32 */
- + RFS_SB(sb)->fat_bits = FAT32;
- + fat_sectors = GET32(bpb_p->num_fat32_sectors);
- + root_clu = GET32(bpb_p->root_cluster);
- + root_sectors = 0;
- + num_root_entries = 0;
- + }
- +
- + /* get each area's start sector number */
- + fat_start_sector = pbr_sector + num_reserved;
- + root_start_sector = fat_start_sector + fat_sectors * bpb_p->num_fats;
- + data_start_sector = root_start_sector + root_sectors;
- +
- + /* get total number of sectors on volume */
- + num_sectors = GET16(bpb_p->num_sectors);
- + if (!num_sectors)
- + num_sectors = GET32(bpb_p->num_huge_sectors);
- + /* check whether it is bigger than device size or it is not available */
- + if (!num_sectors || ((num_sectors << sector_bits) > device_size)) {
- + DPRINTK("invalid number of sectors : %u\n", num_sectors);
- + brelse(bh);
- + return -EINVAL;
- + }
- +
- + /* set cluster size */
- + RFS_SB(sb)->cluster_size = bpb_p->sectors_per_clu << sector_bits;
- + RFS_SB(sb)->cluster_bits = ffs(RFS_SB(sb)->cluster_size) - 1;
- +
- + /* get new block size */
- + if (old_blksize > RFS_SB(sb)->cluster_size)
- + block_size = RFS_SB(sb)->cluster_size;
- + else
- + block_size = old_blksize;
- +
- + /*
- + * block size is sector size if block device's block size is not set,
- + * logical sector size is bigger than block size to set,
- + * or start sector of data area is not aligned
- + */
- + if (!block_size || sector_size > block_size ||
- + (data_start_sector << sector_bits) & (block_size - 1))
- + block_size = sector_size;
- +
- + sectors_per_blk = block_size >> sector_bits;
- + sectors_per_blk_bits = ffs(sectors_per_blk) - 1;
- +
- + /* set number of blocks per cluster */
- + RFS_SB(sb)->blks_per_clu = bpb_p->sectors_per_clu >>
- + sectors_per_blk_bits;
- + RFS_SB(sb)->blks_per_clu_bits = ffs(RFS_SB(sb)->blks_per_clu) - 1;
- +
- + /* set start address of fat table area */
- + RFS_SB(sb)->fat_start_addr = fat_start_sector << sector_bits;
- +
- + /* set start address of root directory */
- + RFS_SB(sb)->root_start_addr = root_start_sector << sector_bits;
- + /*
- + * NOTE: although total dir entries in root dir are bigger than 512
- + * RFS only used 512 entries in root dir
- + */
- + RFS_SB(sb)->root_end_addr = RFS_SB(sb)->root_start_addr +
- + (num_root_entries << DENTRY_SIZE_BITS);
- +
- + /* set start block number of data area */
- + RFS_SB(sb)->data_start = data_start_sector >> sectors_per_blk_bits;
- +
- + /* set total number of clusters */
- + num_blks = (num_sectors - data_start_sector) >> sectors_per_blk_bits;
- + RFS_SB(sb)->num_clusters = (num_blks >> RFS_SB(sb)->blks_per_clu_bits)
- + + 2; /* clu 0 & clu 1 */
- +
- + /* set fat type */
- + if (!RFS_SB(sb)->fat_bits) {
- + if (RFS_SB(sb)->num_clusters >= FAT12_THRESHOLD &&
- + RFS_SB(sb)->num_clusters < FAT16_THRESHOLD)
- + RFS_SB(sb)->fat_bits = FAT16;
- + else {
- + DPRINTK("invalid fat type\n");
- + brelse(bh);
- + return -EINVAL;
- + }
- + }
- +
- + /* set root dir's first cluster number, etc. */
- + RFS_SB(sb)->root_clu = root_clu;
- + RFS_SB(sb)->search_ptr = VALID_CLU; /* clu 0 & 1 are reserved */
- +
- + /* release buffer head contains pbr sector */
- + brelse(bh);
- +
- + /* init semaphore for fat table */
- + RFS_SB(sb)->fat_mutex = kmalloc(sizeof(struct rfs_semaphore), GFP_KERNEL);
- + if (!(RFS_SB(sb)->fat_mutex)) { /* memory error */
- + DEBUG(DL0, "memory allocation failed");
- + return -ENOMEM;
- + }
- +
- + init_fat_lock(sb);
- +
- + /* init list for map destroy */
- + INIT_LIST_HEAD(&RFS_SB(sb)->free_chunks);
- +
- + /* new block size of block device will be set */
- + *new_blksize = block_size;
- +
- + /* NLS support */
- +#ifdef CONFIG_RFS_NLS
- + if (RFS_SB(sb)->options.codepage) {
- + RFS_SB(sb)->nls_disk = load_nls(RFS_SB(sb)->options.codepage);
- + if (!RFS_SB(sb)->nls_disk) {
- + DPRINTK("RFS: %s not found\n", RFS_SB(sb)->options.codepage);
- + return -EINVAL;
- + }
- + }
- +#endif
- +
- +#ifdef RFS_FOR_2_4
- + init_timer(&RFS_SB(sb)->timer);
- + RFS_SB(sb)->timer.function = rfs_log_wakeup;
- + RFS_SB(sb)->timer.data = (unsigned long) sb;
- +#endif
- + RFS_SB(sb)->highest_d_ino = (num_sectors << sector_bits) >> DENTRY_SIZE_BITS;
- +
- + return 0;
- +}
- +
- +/**
- + * fill up the root inode
- + * @param inode root inode
- + * @return return 0 on success, errno on failure
- + */
- +static int fill_root_inode(struct inode *inode)
- +{
- + struct super_block *sb = inode->i_sb;
- + unsigned int last_clu;
- + int err = 0, num_clusters = 0;
- +
- + inode->i_ino = ROOT_INO;
- + inode->i_mode = S_IFDIR | 0777;
- + inode->i_uid = 0;
- + inode->i_gid = 0;
- +
- + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- + inode->i_blksize = sb->s_blocksize;
- + inode->i_version = 0;
- +
- + insert_inode_hash(inode);
- +
- + inode->i_op = &rfs_dir_inode_operations;
- + inode->i_fop = &rfs_dir_operations;
- +
- + RFS_I(inode)->start_clu = RFS_SB(sb)->root_clu;
- + RFS_I(inode)->p_start_clu = RFS_SB(sb)->root_clu;
- + RFS_I(inode)->index = 0;
- +
- + if (!IS_FAT32(RFS_SB(sb))) {
- + inode->i_size = RFS_SB(sb)->cluster_size;
- + } else {
- + num_clusters = find_last_cluster(inode, &last_clu);
- + if (num_clusters <= 0) {
- + err = num_clusters;
- + DPRINTK("No last cluster (err : %d)\n", err);
- + return -EIO;
- + }
- + inode->i_size = num_clusters
- + << RFS_SB(sb)->cluster_bits;
- + /* update last cluster */
- + RFS_I(inode)->last_clu = last_clu;
- + }
- +
- + inode->i_nlink = count_subdir(sb, RFS_I(inode)->start_clu);
- + inode->i_blocks = (inode->i_size + SECTOR_SIZE - 1) >> SECTOR_BITS;
- +
- + set_mmu_private(inode, inode->i_size);
- +
- + spin_lock_init(&RFS_I(inode)->write_lock);
- +#ifdef RFS_FOR_2_4
- + init_MUTEX(&RFS_I(inode)->data_mutex);
- + init_timer(&RFS_I(inode)->timer);
- + RFS_I(inode)->timer.function = rfs_data_wakeup;
- + RFS_I(inode)->timer.data = (unsigned long) RFS_I(inode);
- +#endif
- +
- + return 0;
- +}
- +
- +/**
- + * read the super block (disk such as MBR) of RFS and initialize super block (incore)
- + * @param sb super block
- + * @param data private data
- + * @param silent verbose flag
- + * @return return super block pointer on success, null on failure
- + */
- +struct super_block *rfs_common_read_super(struct super_block *sb,
- + void *data, int silent)
- +{
- + struct inode *root_inode = NULL;
- + unsigned long new_blksize, old_blksize;
- + int err;
- +
- +#ifdef RFS_FOR_2_6
- + struct rfs_sb_info *sbi;
- +
- + sbi = kmalloc(sizeof(struct rfs_sb_info), GFP_KERNEL);
- + if (!sbi) /* memory error */
- + goto failed_mount;
- +
- + /* initialize sbi with 0x00 */
- + /* log_info and pool_info must be initialized with 0 */
- + memset(sbi, 0x00, sizeof(struct rfs_sb_info));
- + sb->s_fs_info = sbi;
- +
- + old_blksize = block_size(sb->s_bdev);
- +#else
- + old_blksize = block_size(sb->s_dev);
- +#endif
- + sb_min_blocksize(sb, 512);
- +
- + /* parsing mount options */
- + if (parse_option(sb, data) < 0)
- + goto failed_mount;
- +
- + /* fill the RFS-specific info of sb */
- + if (rfs_build_sb(sb, old_blksize, &new_blksize) < 0)
- + goto failed_mount;
- +
- +
- +
- + /* setup the rest superblock info */
- + if (!sb_set_blocksize(sb, new_blksize)) {
- + DPRINTK("unable to set blocksize\n");
- + goto failed_mount;
- + }
- +
- + sb->s_maxbytes = 0xFFFFFFFF; /* maximum file size */
- + sb->s_op = &rfs_sops;
- + sb->s_magic = RFS_MAGIC;
- + sb->s_dirt = 0;
- +
- + RFS_SB(sb)->fcache_array = NULL;
- + if (rfs_fcache_init(sb)) { /* memory error */
- + DPRINTK("unable to init fat cache\n");
- + goto release_fcache;
- + }
- +
- + /* allocate root inode & fill it */
- + if (!(root_inode = new_inode(sb)))
- + goto release_fcache;
- +
- + err = fill_root_inode(root_inode);
- + if (err) {
- + iput(root_inode);
- + goto release_fcache;
- + }
- +
- + if (!(sb->s_root = d_alloc_root(root_inode))) {
- + iput(root_inode);
- + goto release_fcache;
- + }
- +
- + return sb;
- +
- +release_fcache:
- + /* release fcache */
- + if (RFS_SB(sb)->fcache_array)
- + kfree(RFS_SB(sb)->fcache_array);
- +failed_mount:
- + if (RFS_SB(sb)->fat_mutex)
- + kfree(RFS_SB(sb)->fat_mutex);
- +#ifdef RFS_FOR_2_6
- + if (sbi)
- + kfree(sbi);
- +#endif
- +
- + return NULL;
- +}
- +
- +/**
- + * flush all dirty buffers of the file system include fat cache
- + * @param sb super block
- + * @return return 0
- + */
- +int rfs_sync_vol(struct super_block *sb)
- +{
- + /* fat cache sync */
- + fat_lock(sb);
- + rfs_fcache_sync(sb, 0);
- + fat_unlock(sb);
- +
- + /* fat cache is dirty without waiting flush. So sync device */
- +#ifdef RFS_FOR_2_6
- + sync_blockdev(sb->s_bdev);
- +#else
- + fsync_no_super(sb->s_dev);
- +#endif
- + return 0;
- +}
- +
- +#ifdef _RFS_INTERNAL_UNUSED_LOG
- +/**
- + * flush all dirty buffers of a transaction
- + * @param sb super block
- + * @return return 0 on success
- + */
- +int rfs_sync_transaction(struct super_block *sb)
- +{
- + int ret = 0;
- +
- + if (RFS_LOG_I(sb)->type != RFS_LOG_UNLINK &&
- + RFS_LOG_I(sb)->type != RFS_LOG_DEL_INODE)
- + ret = rfs_sync_inode(RFS_LOG_I(sb)->inode, 1, 0);
- +
- + ret |= rfs_meta_commit(sb);
- +
- + return ret;
- +}
- +#endif
- diff -NurbB linux-2.6.17.14.orig/include/linux/rfs_fs.h linux-2.6.17.14.rfs/include/linux/rfs_fs.h
- --- linux-2.6.17.14.orig/include/linux/rfs_fs.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/include/linux/rfs_fs.h 2008-06-19 04:03:51.000000000 +0200
- @@ -0,0 +1,508 @@
- +/**
- + * @file include/linux/rfs_fs.h
- + * @brief common header file for RFS
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#ifndef _LINUX_RFS_FS_H
- +#define _LINUX_RFS_FS_H
- +
- +#include <linux/version.h>
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +#include <linux/buffer_head.h>
- +#include <linux/rfs_fs_i.h>
- +#include <linux/rfs_fs_sb.h>
- +#else
- +#include <linux/locks.h>
- +#endif
- +#include <linux/byteorder/generic.h>
- +#include <linux/xsr_if.h>
- +
- +/*
- + * Constand and Mecro definition
- + */
- +#define ROOT_INO 1
- +
- +#define RFS_MAGIC (0x2003BAB1L)
- +
- +#define DOS_NAME_LENGTH 11
- +#define EXT_UNAME_LENGTH 13
- +#define SHORT_NAME_LENGTH 8
- +#define UNICODE_NAME_LENGTH 256
- +#define MAX_NAME_LENGTH UNICODE_NAME_LENGTH
- +#define MAX_TOTAL_LENGTH 260
- +
- +#define DELETE_MARK ((u8) 0xE5)
- +#define EXT_END_MARK ((u8) 0x40)
- +
- +#define KANJI_LEAD ((u8)(0xE5))
- +#define REPLACE_KANJI ((u8)(0x05))
- +
- +#define SPACE ((u8)(0x20)) /* ' ' */
- +#define PERIOD ((u8)(0x2E)) /* '.' */
- +#define UNDERSCORE ((u8)(0x5F)) /* '_' */
- +#define TILDE ((u8)(0x7E)) /* '~' */
- +
- +#define PRIMARY_LOWER ((u8)(0x08))
- +#define EXTENSION_LOWER ((u8)(0x10))
- +#define PRIMARY_UPPER ((u8)(0x07))
- +#define EXTENSION_UPPER ((u8)(0x0E0))
- +#define UPPER_N_LOWER ((u8)(0x0FF))
- +
- +#define primary_masked(mixed) (mixed & 0x00F)
- +#define extension_masked(mixed) (mixed & 0x0F0)
- +
- +#define SIGNATURE 0xAA55
- +
- +#define DENTRY_SIZE 32 /* MS-DOS FAT Compatible */
- +#define DENTRY_SIZE_BITS 5
- +#define MAX_ROOT_DENTRY 511 /* 0 ~ 511 */
- +#define MAX_DIR_DENTRY 65536
- +
- +#define SECTOR_SIZE 512
- +#define SECTOR_BITS 9
- +#define SECTOR_MASK (SECTOR_SIZE - 1)
- +
- +/* attribute(FAT type) */
- +#define ATTR_NORMAL ((u8) 0x00)
- +#define ATTR_READONLY ((u8) 0x01)
- +#define ATTR_HIDDEN ((u8) 0x02)
- +#define ATTR_SYSTEM ((u8) 0x04)
- +#define ATTR_VOLUME ((u8) 0x08)
- +#define ATTR_EXTEND ((u8) 0x0F)
- +#define ATTR_SUBDIR ((u8) 0x10)
- +#define ATTR_ARCHIVE ((u8) 0x20)
- +
- +/* type of directory entry(internal type) */
- +#define TYPE_UNUSED ((u32) 0x00)
- +#define TYPE_DELETED ((u32) 0x01)
- +#define TYPE_FILE ((u32) 0x02)
- +#define TYPE_DIR ((u32) 0x03)
- +#define TYPE_EXTEND ((u32) 0x04)
- +#define TYPE_ALL ((u32) 0x05)
- +#define TYPE_UNKNOWN ((u32) 0x06)
- +#define TYPE_SYMLINK ((u32) 0x07)
- +#define TYPE_VOLUME ((u32) 0x08)
- +
- +#define SYMLINK_MARK ((u8) 0xE2) /* symlink */
- +
- +/* FAT type */
- +#define FAT16 16
- +#define FAT32 32
- +
- +#define IS_FAT16(sbi) ((sbi)->fat_bits == FAT16)
- +#define IS_FAT32(sbi) ((sbi)->fat_bits == FAT32)
- +#define IS_VFAT(sbi) ((sbi)->options.isvfat == TRUE)
- +
- +/* threshold value(# of clusters) to determin the FAT type */
- +#define FAT12_THRESHOLD 4087 /* 4085 + clu 0 + clu 1 */
- +#define FAT16_THRESHOLD 65527 /* 65525 + clu 0 + clu 1 */
- +#define FAT32_THRESHOLD 268435447 /* 268435445 + clu 0 + clu 1*/
- +
- +/* related with cluster */
- +#define CLU_TAIL ((unsigned int) (~0))
- +#define CLU_FREE ((unsigned int) (0))
- +
- +#define VALID_CLU 2
- +
- +/* fast unlink */
- +#define RFS_POOL_FILE_NAME "RFS_POOL.SY$"
- +#define RFS_POOL_FILE_LEN 12
- +#define POOL_RESERVED_CLUSTER 1
- +
- +#define SHRINK_POOL_SIZE 0
- +#define EXPAND_POOL_SIZE 1
- +#define SET_POOL_SIZE 2
- +
- +/* Internal error code */
- +#define INVALID_ENTRY 131
- +#define INTERNAL_EOF 132
- +
- +/* Miscellaneous definition */
- +#define TRUE 1
- +#define FALSE 0
- +#define DOT ". "
- +#define DOTDOT ".. "
- +
- +/* REVISIT: It's not fixed since it is changed from int to unsigned int */
- +#define NOT_ASSIGNED (~0)
- +
- +/* macro */
- +/* REVISIT : We need endian handling */
- +#define GET16(x) le16_to_cpu(x)
- +#define GET32(x) le32_to_cpu(x)
- +#define GET64(x) le64_to_cpu(x)
- +
- +#define SET64(dst, src) \
- +do { \
- + (dst) = cpu_to_le64(src); \
- +} while (0)
- +
- +#define SET32(dst, src) \
- +do { \
- + (dst) = cpu_to_le32(src); \
- +} while (0)
- +
- +#define SET16(dst, src) \
- +do { \
- + (dst) = cpu_to_le16(src); \
- +} while (0)
- +
- +#define GET_FREE_CLUS(sbi) ((sbi)->num_clusters - (sbi)->num_used_clusters)
- +
- +#define START_CLUSTER(x) \
- + (((GET16((x)->start_clu_hi)) << 16) | GET16((x)->start_clu_lo))
- +#define START_BLOCK(x, sb) \
- + (((x - VALID_CLU) << RFS_SB(sb)->blks_per_clu_bits) + \
- + RFS_SB(sb)->data_start)
- +
- +#define IS_FREE(name) (((name[0] == DELETE_MARK) || (name[0] == 0x0))? 1 : 0 )
- +
- +#define IS_XSR(x) (MAJOR(x) == XSR_BLK_DEVICE_FTL ? 1 : 0)
- +
- +/* function macro */
- +#ifdef CONFIG_RFS_VFAT
- +#define find_entry find_entry_long
- +#define build_entry build_entry_long
- +#else
- +#define find_entry find_entry_short
- +#define build_entry build_entry_short
- +#endif
- +
- +#define rfs_mark_buffer_dirty(x, sb) mark_buffer_tr_dirty(x, sb)
- +#define rfs_mark_inode_dirty(x) mark_inode_tr_dirty(x)
- +
- +/*
- + * structure of partition entry (DISK)
- + */
- +struct part_entry {
- + u8 def_boot;
- + u8 bgn_head;
- + u8 bgn_sector;
- + u8 bgn_cylinder;
- + u8 sys_type;
- + u8 end_head;
- + u8 end_sector;
- + u8 end_cylinder;
- + u32 start_sector;
- + u32 num_sectors;
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of master boot record (DISK)
- + */
- +struct mbr {
- + u8 boot_code[446];
- + u8 partition[64];
- + u16 signature;
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of BIOS Parameter Block (DISK)
- + */
- +struct bpb {
- + u16 sector_size;
- + u8 sectors_per_clu;
- + u16 num_reserved;
- + u8 num_fats;
- + u16 num_root_entries;
- + u16 num_sectors;
- + u8 media_type;
- + u16 num_fat_sectors;
- + u16 sectors_in_track;
- + u16 num_heads;
- + u32 num_hidden_sectors;
- + u32 num_huge_sectors;
- +
- + u32 num_fat32_sectors;
- + u16 ext_flags;
- + u16 version;
- + u32 root_cluster;
- + u16 fsinfo_sector;
- + u16 backup_sector;
- + u8 reserved[12];
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of additional BPB data (DISK)
- + */
- +struct ext_bpb {
- + u8 phy_drv_no;
- + u8 reserved;
- + u8 ext_signature;
- + u8 vol_serial[4];
- + u8 vol_label[11];
- + u8 vol_type[8];
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of Parition Boot Record (DISK)
- + */
- +struct pbr {
- + u8 jmp_boot[3];
- + u8 oem_name[8];
- + u8 bpb[25];
- + union {
- + struct {
- + u8 ext_bpb[26];
- + u8 boot_code[446];
- + } fat16;
- + struct {
- + u8 bpb[28];
- + u8 ext_bpb[26];
- + u8 boot_code[418];
- + } fat32;
- + } u;
- + u8 boot_code[2];
- + u16 signature;
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of dir entry data on the disk (DISK)
- + */
- +struct rfs_dir_entry {
- + u8 name[DOS_NAME_LENGTH]; /* 8.3 name */
- + u8 attr;
- + u8 sysid;
- + u8 cmsec; /* create time in milliseconds */
- + u16 ctime; /* create time */
- + u16 cdate; /* create date */
- + u16 adate; /* access date */
- + u16 start_clu_hi; /* high 16-bits of start cluster */
- + u16 mtime; /* modify time */
- + u16 mdate; /* modify date */
- + u16 start_clu_lo; /* low 16-bits of start cluster */
- + u32 size;
- +} __attribute__ ((packed));
- +
- +/*
- + * structure of extentry entry data on the disk (DISK)
- + * extentry entry is needed for long file name
- + */
- +struct rfs_ext_entry {
- + u8 entry_offset;
- + u16 uni_0_4[5]; /* unicode 0 ~ 4 */
- + u8 attr;
- + u8 sysid;
- + u8 checksum;
- + u16 uni_5_10[6]; /* unicode 5 ~ 10 */
- + u16 start_clu; /* aligned */
- + u16 uni_11_12[2]; /* unicode 11 ~ 12 */
- +} __attribute__ ((packed));
- +
- +/*
- + * hint info for fast unlink (DISK/INCORE)
- + */
- +struct rfs_pool_info {
- + u32 index; /* index of pool file dir entry */
- + u32 blocknr; /* pool file dir entry is saved in this block */
- + u32 start_cluster; /* start cluster number of pool file */
- + u32 last_cluster; /* last cluster number in deleted segment */
- + u32 num_clusters; /* number of clusters except candidate segs */
- + u32 c_start_cluster; /* start clu # in candidate segment list */
- + u32 c_last_cluster; /* last clu # in candidate segment list */
- + struct list_head c_list; /* head for candidate segment list */
- +};
- +
- +enum rfs_state_bits {
- + BH_RFS_LOG_START = BH_PrivateStart,
- + BH_RFS_LOG_COMMIT,
- + BH_RFS_LOG,
- + BH_RFS_FAT,
- + BH_RFS_POOL_BLOCK,
- + BH_RFS_ROOT,
- + BH_RFS_DIR,
- + BH_RFS_MBR,
- + BH_RFS_DATA,
- +};
- +
- +#ifdef CONFIG_RFS_IGET4
- +/*
- + * structure for read_inode2 (INCORE)
- + */
- +struct rfs_iget4_args {
- + struct rfs_dir_entry *ep;
- + u32 p_start_clu;
- + u32 index;
- +};
- +#endif
- +
- +/* status flag: [3] max n_tail [2] min n_tail [1] lossy, [0] mix */
- +#define get_lossy(status) ((status >> 8) & 0xFF)
- +#define get_mix(status) (u8) (status & 0xFF)
- +
- +#define put_lossy(status, lossy) \
- +do { status |= ((lossy & 0xFF) << 8); \
- +} while(0)
- +#define put_mix(status, mix) \
- +do { status |= (mix & 0xFF); \
- +} while(0)
- +
- +/*
- + * vector operations
- + */
- +
- +/* inode.c */
- +int rfs_iunique (struct inode *, unsigned int, unsigned long *);
- +int fill_inode (struct inode *, struct rfs_dir_entry *, unsigned int, unsigned int);
- +struct inode *rfs_new_inode (struct inode *, struct dentry *, unsigned int);
- +void rfs_delete_inode (struct inode *);
- +int rfs_delete_entry (struct inode *, struct inode *);
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +int rfs_write_inode (struct inode *, int);
- +#else
- +void rfs_write_inode (struct inode *, int);
- +#endif
- +#ifdef CONFIG_RFS_IGET4
- +void rfs_read_inode2 (struct inode *, void *);
- +#endif
- +
- +static inline void set_mmu_private(struct inode *inode, loff_t value)
- +{
- + struct super_block *sb = inode->i_sb;
- +
- + RFS_I(inode)->mmu_private = value;
- + if (RFS_I(inode)->mmu_private & (sb->s_blocksize - 1)) {
- + RFS_I(inode)->mmu_private |= (sb->s_blocksize -1);
- + RFS_I(inode)->mmu_private++;
- + }
- +}
- +
- +/* super.c */
- +struct super_block *rfs_common_read_super (struct super_block *, void *, int);
- +int rfs_sync_vol(struct super_block *);
- +
- +/* file.c */
- +int extend_with_zerofill (struct inode *, unsigned int, unsigned int);
- +int rfs_setattr (struct dentry *, struct iattr *);
- +int rfs_bmap (struct inode *, long, unsigned long *);
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +int rfs_get_block (struct inode *, sector_t, struct buffer_head *, int);
- +int rfs_permission (struct inode *, int, struct nameidata *);
- +#else
- +int rfs_get_block (struct inode *, long, struct buffer_head *, int);
- +int rfs_permission (struct inode *, int);
- +#endif
- +void rfs_invalidate_hint(struct inode *);
- +
- +/* namei.c */
- +int build_entry_short (struct inode *, struct inode *, unsigned int, unsigned int, const char *);
- +int build_entry_long (struct inode *, struct inode *, unsigned int, unsigned int, const char *);
- +int check_reserved_files (struct inode *, const char *);
- +
- +/* dir.c */
- +int is_dir_empty (struct inode *);
- +int init_new_dir (struct inode *);
- +int count_subdir (struct super_block *, unsigned int);
- +
- +/* dos.c */
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +void set_entry_time (struct rfs_dir_entry *, struct timespec);
- +#else
- +void set_entry_time (struct rfs_dir_entry *, time_t);
- +#endif
- +unsigned int entry_type (struct rfs_dir_entry *);
- +unsigned int entry_time (unsigned short, unsigned short);
- +void init_dir_entry (struct inode *, struct rfs_dir_entry *, unsigned int, unsigned int, const u8 *, unsigned char *);
- +int init_ext_entry (struct rfs_ext_entry *, unsigned int, unsigned int, const u16 *, unsigned char);
- +unsigned char calc_checksum (const u8 *);
- +int mk_dosname (struct inode *dir, const char *name, u8 *dosname, unsigned char *mixed, u16 *uname);
- +int get_uname_from_entry (struct inode *, unsigned int , unsigned short *);
- +struct rfs_dir_entry *get_entry (struct inode *, unsigned int, struct buffer_head **);
- +struct rfs_dir_entry *get_entry_with_cluster (struct super_block *, unsigned int, unsigned int, struct buffer_head **);
- +int find_entry_short (struct inode *, const char *, struct buffer_head **, unsigned int);
- +int find_entry_long (struct inode *, const char *, struct buffer_head **, unsigned int);
- +int remove_entry (struct inode *, unsigned int, struct buffer_head **);
- +
- +/* cluster.c */
- +int fat_read (struct super_block *, unsigned int, unsigned int *);
- +int fat_write (struct super_block *, unsigned int, unsigned int);
- +int alloc_cluster (struct inode *, unsigned int *);
- +int rfs_map_destroy (struct super_block *);
- +int free_chain (struct inode *, unsigned int, unsigned int, unsigned int *);
- +int count_num_clusters (struct inode *);
- +int dealloc_clusters (struct inode *, unsigned int);
- +int count_used_clusters (struct super_block *, unsigned int *);
- +int append_new_cluster(struct inode *, unsigned int, unsigned int);
- +int find_free_cluster(struct inode *, unsigned int *);
- +int find_last_cluster(struct inode *, unsigned int *);
- +int find_cluster(struct super_block *, unsigned int, unsigned int, unsigned int *, unsigned int *);
- +
- +int rfs_fcache_init (struct super_block *);
- +void rfs_fcache_release (struct super_block *);
- +void rfs_fcache_sync (struct super_block *, int);
- +
- +int rfs_init_pool (struct super_block *);
- +void rfs_release_pool (struct super_block *);
- +int rfs_shrink_pool_chain (struct super_block *, unsigned int *,
- + unsigned int, unsigned int *, unsigned int *);
- +int rfs_get_pool (struct super_block *, unsigned int, unsigned int);
- +int rfs_attach_candidate (struct inode *);
- +int rfs_detach_candidate (struct inode *);
- +int rfs_remove_candidates (struct super_block *);
- +int rfs_update_pool_block (struct super_block *);
- +int rfs_update_pool_entry (struct super_block *, unsigned int, int);
- +
- +static inline struct rfs_pool_info *RFS_POOL_I(struct super_block *sb)
- +{
- + return (struct rfs_pool_info *)(RFS_SB(sb)->pool_info);
- +}
- +
- +/* code_convert.c */
- +void convert_dosname_to_cstring (char *, const u8 *, unsigned char);
- +int convert_uname_to_cstring (char *, const u16 *, struct nls_table *);
- +int convert_cstring_to_dosname(u8 *, const char *, unsigned int *, unsigned int);
- +int create_fatname(const char *, u8 *, u16 *, unsigned int *, struct nls_table *, unsigned int);
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +/* rfs_26.c : Linux 2.6 dependent operations */
- +struct inode *rfs_alloc_inode (struct super_block *);
- +void rfs_destroy_inode (struct inode *);
- +#else
- +/* rfs_24.c : Linux 2.4 dependent operations */
- +void rfs_log_wakeup (unsigned long __data);
- +void rfs_data_wakeup (unsigned long __data);
- +int rfs_block_prepare_write(struct inode *inode, struct page *page, unsigned from, unsigned to, get_block_t *get_block);
- +int rfs_block_commit_write(struct inode *inode, struct page *page, unsigned from, unsigned to);
- +#endif
- +
- +/* dir.c */
- +extern struct file_operations rfs_dir_operations;
- +
- +/* file.c */
- +extern struct inode_operations rfs_file_inode_operations;
- +extern struct file_operations rfs_file_operations;
- +
- +/* inode.c */
- +extern struct address_space_operations rfs_aops;
- +
- +/* namei.c */
- +extern struct inode_operations rfs_dir_inode_operations;
- +extern struct dentry_operations rfs_dentry_operations;
- +
- +/* symlink.c */
- +extern struct inode_operations rfs_symlink_inode_operations;
- +
- +/* for transaction sync */
- +extern void mark_buffer_tr_dirty(struct buffer_head *bh, struct super_block *sb);
- +extern void mark_inode_tr_dirty(struct inode *inode);
- +#endif /* _LINUX_RFS_FS_H */
- diff -NurbB linux-2.6.17.14.orig/include/linux/rfs_fs_i.h linux-2.6.17.14.rfs/include/linux/rfs_fs_i.h
- --- linux-2.6.17.14.orig/include/linux/rfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/include/linux/rfs_fs_i.h 2008-06-19 04:03:44.000000000 +0200
- @@ -0,0 +1,79 @@
- +/**
- + * @file include/linux/rfs_fs_i.h
- + * @brief header file for RFS inode
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#ifndef _LINUX_RFS_FS_I
- +#define _LINUX_RFS_FS_I
- +
- +#include <linux/timer.h>
- +
- +/*
- + * RFS file system inode data in memory (in-core)
- + */
- +
- +struct rfs_inode_info {
- + __u32 start_clu; /* start cluster of inode */
- + __u32 p_start_clu; /* parent directory start cluster */
- + __u32 index; /* dir entry index(position) in directory */
- + __u32 last_clu; /* last cluster number */
- + __u8 i_state; /* rfs-specific inode state */
- +
- + /* hint for quick search */
- + __u32 hint_last_clu;
- + __u32 hint_last_offset;
- +
- + /* truncate point */
- + unsigned long trunc_start;
- +
- + spinlock_t write_lock;
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- + /* total block size that inode allocated */
- + loff_t mmu_private;
- + struct inode vfs_inode;
- +#else
- + /* total block size that inode allocated */
- + unsigned long mmu_private;
- +
- + /* ordered transaction */
- + struct semaphore data_mutex;
- + struct timer_list timer;
- + struct task_struct *sleep_proc;
- +#endif
- +};
- +
- +/* get inode info */
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +static inline struct rfs_inode_info *RFS_I(struct inode *inode)
- +{
- + return container_of(inode, struct rfs_inode_info, vfs_inode);
- +}
- +#else
- +#define RFS_I(i) (&((i)->u.rfs_i))
- +#endif
- +
- +/* rfs-specific inode state */
- +#define RFS_I_ALLOC 0x00
- +#define RFS_I_FREE 0x01
- +
- +#endif
- diff -NurbB linux-2.6.17.14.orig/include/linux/rfs_fs_sb.h linux-2.6.17.14.rfs/include/linux/rfs_fs_sb.h
- --- linux-2.6.17.14.orig/include/linux/rfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/include/linux/rfs_fs_sb.h 2008-06-19 04:04:09.000000000 +0200
- @@ -0,0 +1,98 @@
- +/**
- + * @file include/linux/rfs_fs_sb.h
- + * @brief header file for RFS superblock
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +
- +#ifndef _LINUX_RFS_FS_SB
- +#define _LINUX_RFS_FS_SB
- +
- +#include <asm/semaphore.h>
- +/*
- + * RFS file system superblock data in memory(in-core)
- + */
- +
- +/* rfs mount options */
- +struct rfs_mount_info {
- + char *codepage;
- + __u32 isvfat;
- +};
- +
- +/* rfs private data structure of sb */
- +struct rfs_sb_info {
- + __u32 fat_bits; /* FAT bits (12, 16 or 32) */
- + __u32 blks_per_clu;
- + __u32 blks_per_clu_bits;
- + __u32 cluster_size;
- + __u32 cluster_bits;
- + __u32 num_clusters;
- +
- + __u32 fat_start_addr; /* start address of first FAT table */
- +
- + __u32 root_start_addr; /* start address of root directory */
- + __u32 root_end_addr; /* end address of root directory */
- +
- + __u32 data_start; /* start block of data area */
- +
- + __u32 root_clu; /* root dir cluster, FAT16 = 0 */
- + __u32 search_ptr; /* cluster search pointer */
- + __u32 num_used_clusters; /* the number of used clusters */
- +
- + /* for FAT table */
- + void *fat_mutex;
- +
- + struct rfs_mount_info options;
- +
- + /* RFS internal FAT cache */
- + struct list_head fcache_lru_list;
- + struct rfs_fcache *fcache_array;
- +
- + struct nls_table *nls_disk;
- +
- + /* fields for log */
- + void *log_info; /* private for log structure */
- +
- + /* hint info for fast unlink */
- + void *pool_info;
- +
- + /* chunk list for map destroy */
- + struct list_head free_chunks;
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
- + struct task_struct *sleep_proc;
- + struct timer_list timer;
- +#endif
- +
- + unsigned long highest_d_ino;
- +};
- +
- +/* get super block info */
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- +static inline struct rfs_sb_info *RFS_SB(struct super_block *sb)
- +{
- + return sb->s_fs_info;
- +}
- +#else
- +#define RFS_SB(s) (&((s)->u.rfs_sb))
- +#endif
- +
- +#endif
- diff -NurbB linux-2.6.17.14.orig/include/linux/xsr_if.h linux-2.6.17.14.rfs/include/linux/xsr_if.h
- --- linux-2.6.17.14.orig/include/linux/xsr_if.h 1970-01-01 01:00:00.000000000 +0100
- +++ linux-2.6.17.14.rfs/include/linux/xsr_if.h 2008-06-19 04:03:36.000000000 +0200
- @@ -0,0 +1,151 @@
- +/**
- + * @file include/linux/xsr_if.h
- + * @brief XSR interface to export commands and macros to utils, fat
- + *
- + *---------------------------------------------------------------------------*
- + * *
- + * COPYRIGHT 2003-2007 SAMSUNG ELECTRONICS CO., LTD. *
- + * ALL RIGHTS RESERVED *
- + * *
- + * Permission is hereby granted to licensees of Samsung Electronics *
- + * Co., Ltd. products to use or abstract this computer program only in *
- + * accordance with the terms of the NAND FLASH MEMORY SOFTWARE LICENSE *
- + * AGREEMENT for the sole purpose of implementing a product based on *
- + * Samsung Electronics Co., Ltd. products. No other rights to reproduce, *
- + * use, or disseminate this computer program, whether in part or in *
- + * whole, are granted. *
- + * *
- + * Samsung Electronics Co., Ltd. makes no representation or warranties *
- + * with respect to the performance of this computer program, and *
- + * specifically disclaims any responsibility for any damages, *
- + * special or consequential, connected with the use of this program. *
- + * *
- + *---------------------------------------------------------------------------*
- + */
- +#ifndef _XSR_IF_H_
- +#define _XSR_IF_H_
- +
- +#ifndef __KERNEL__
- +/*Warning*/
- +/*If you modify BML, you must check this definition*/
- +/*****************************************************************************/
- +/* Partition Entry ID of BML_LoadPIEntry() */
- +/* Partition Entry ID from 0 to 0x0FFFFFFF is reserved in BML */
- +/* Following ID is the pre-defined value and User can use Partition Entry ID */
- +/* from PARTITION_USER_DEF_BASE */
- +/*****************************************************************************/
- +#define PARTITION_ID_NBL1 0 /* NAND bootloader stage 1 */
- +#define PARTITION_ID_NBL2 1 /* NAND bootloader stage 2 */
- +#define PARTITION_ID_NBL3 2 /* NAND bootloader stage 3 */
- +#define PARTITION_ID_COPIEDOS 3 /* OS image copied from NAND
- + flash memory to RAM */
- +#define PARTITION_ID_DEMANDONOS 4 /* OS image loaded on demand */
- +
- +#define PARTITION_ID_FILESYSTEM 8 /* file system 0 */
- +#define PARTITION_ID_FILESYSTEM1 9 /* file system 1 */
- +#define PARTITION_ID_FILESYSTEM2 10 /* file system 2 */
- +#define PARTITION_ID_FILESYSTEM3 11 /* file system 3 */
- +#define PARTITION_ID_FILESYSTEM4 12 /* file system 4 */
- +#define PARTITION_ID_FILESYSTEM5 13 /* file system 5 */
- +#define PARTITION_ID_FILESYSTEM6 14 /* file system 6 */
- +#define PARTITION_ID_FILESYSTEM7 15 /* file system 7 */
- +#define PARTITION_ID_FILESYSTEM8 16 /* file system 8 */
- +#define PARTITION_ID_FILESYSTEM9 17 /* file system 9 */
- +#define PARTITION_ID_FILESYSTEM10 18 /* file system 10 */
- +#define PARTITION_ID_FILESYSTEM11 19 /* file system 11 */
- +#define PARTITION_ID_FILESYSTEM12 20 /* file system 12 */
- +#define PARTITION_ID_FILESYSTEM13 21 /* file system 13 */
- +#define PARTITION_ID_FILESYSTEM14 22 /* file system 14 */
- +
- +#define PARTITION_USER_DEF_BASE 0x10000000 /* partition id base for
- + user definition */
- +
- +/*****************************************************************************/
- +/* value of nAttr of XSRPartEntry structure */
- +/* nAttr can be 'BML_PI_ATTR_FROZEN + BML_PI_ATTR_RO' or */
- +/* 'BML_PI_ATTR_RO' or */
- +/* 'BML_PI_ATTR_RW'. */
- +/* other value is invalid attribute. */
- +/*****************************************************************************/
- +#define BML_PI_ATTR_FROZEN 0x00000020
- +#define BML_PI_ATTR_RO 0x00000002
- +#define BML_PI_ATTR_RW 0x00000001
- +
- +#endif
- +
- +
- +/**
- + * This file define some macro and it will shared user and kernel
- + */
- +#ifdef CONFIG_XSR_DUAL_DEVICE
- +#define XSR_MAX_DEVICES 2
- +#else
- +#define XSR_MAX_DEVICES 1
- +#endif
- +
- +/* this is support 15 partition*/
- +#define MASK(x) ((1U << (x)) -1)
- +#define PARTN_BITS 4
- +#define PARTN_MASK MASK(PARTN_BITS)
- +#define MAX_FLASH_PARTITIONS ((0x1<< PARTN_BITS) - 1)
- +#define MAX_PAGE_SIZE 2048
- +#define MAX_OOB_SIZE 64
- +
- +/* Device major number*/
- +#define XSR_BLK_DEVICE_RAW 137
- +#define XSR_BLK_DEVICE_FTL 138
- +/* distinguish chip and partition during dump and restore */
- +#define XSR_CHIP 0xaabb
- +#define XSR_PART 0xaacc
- +#define MAGIC_STR_SIZE 8
- +/* BML level ioctl commands */
- +#define BML_GET_DEV_INFO 0x8A21
- +#define BML_GET_PARTITION 0x8A22
- +#define BML_SET_PARTITION 0x8A23
- +#define BML_FORMAT 0x8A24
- +#define BML_ERASE_ALL 0x8A25
- +#define BML_ERASE_PARTITION 0x8A26
- +#define BML_DUMP 0x8A27
- +#define BML_RESTORE 0x8A28
- +#define BML_UNLOCK_ALL 0x8A29
- +#define BML_SET_RW_AREA 0x8A2A
- +
- +typedef struct {
- + unsigned int offset;
- + unsigned char mbuf[MAX_PAGE_SIZE];
- + unsigned char sbuf[MAX_OOB_SIZE];
- +} BML_PAGEINFO_T;
- +
- +typedef struct {
- + int phy_blk_size; /* in bytes expect spare*/
- + int num_blocks;
- + int page_msize; /* main size in page */
- + int page_ssize; /* spare size in page */
- +} BML_DEVINFO_T;
- +
- +typedef struct {
- + int num_parts;
- + int part_size[MAX_FLASH_PARTITIONS]; /* in number of blocks */
- + int part_id[MAX_FLASH_PARTITIONS]; /* device class */
- + int part_attr[MAX_FLASH_PARTITIONS]; /* device class */
- +} BML_PARTTAB_T;
- +
- +/* STL level ioctl commands */
- +#define STL_FORMAT 0x8A01 /* FTL format */
- +#define STL_GET_DEV_INFO 0x8A02 /* FTL stat */
- +#define STL_CLEAN 0x8A03 /* FTL clean */
- +#define STL_SYNC 0x8A13 /* FTL sync */
- +#define STL_MAPDESTROY 0x8A14 /* FTL mapdestroy */
- +
- +typedef struct {
- + unsigned int total_sectors;
- + unsigned int page_size;
- +}stl_info_t;
- +
- +typedef struct {
- + unsigned int fill_factor;
- + unsigned int nr_reserved_units;
- + unsigned int blocks_per_unit;
- +}stl_config_t;
- +
- +#endif /* _XSR_IF_H_ */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement