lkml.org 
[lkml]   [2012]   [Mar]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    Subject[PATCH] VMUFAT filesystem [2/4]
    From
    diff --git a/fs/vmufat/inode.c b/fs/vmufat/inode.c
    new file mode 100644
    index 0000000..584fb56
    --- /dev/null
    +++ b/fs/vmufat/inode.c
    @@ -0,0 +1,951 @@
    +/*
    + * VMUFAT file system
    + *
    + * Copyright (C) 2002 - 2012 Adrian McMenamin
    + * Copyright (C) 2002 Paul Mundt
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    + */
    +
    +#include <linux/fs.h>
    +#include <linux/bcd.h>
    +#include <linux/rtc.h>
    +#include <linux/slab.h>
    +#include <linux/sched.h>
    +#include <linux/magic.h>
    +#include <linux/device.h>
    +#include <linux/module.h>
    +#include <linux/statfs.h>
    +#include <linux/buffer_head.h>
    +#include "vmufat.h"
    +
    +const struct inode_operations vmufat_inode_operations;
    +const struct file_operations vmufat_file_operations;
    +const struct address_space_operations vmufat_address_space_operations;
    +const struct file_operations vmufat_file_dir_operations;
    +struct kmem_cache *vmufat_blist_cachep;
    +/* Linear day numbers of the respective 1sts in non-leap years. */
    +int day_n[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
    +
    +static struct dentry *vmufat_inode_lookup(struct inode *in, struct
    dentry *dent,
    + struct nameidata *ignored)
    +{
    + struct super_block *sb;
    + struct memcard *vmudetails;
    + struct buffer_head *bufhead = NULL;
    + struct inode *ino;
    + char name[VMUFAT_NAMELEN];
    + int i, j, error = -EINVAL;
    + if (!dent || !in || !in->i_sb)
    + goto out;
    + if (dent->d_name.len > VMUFAT_NAMELEN) {
    + error = -ENAMETOOLONG;
    + goto out;
    + }
    + sb = in->i_sb;
    + if (!sb->s_fs_info)
    + goto out;
    + vmudetails = sb->s_fs_info;
    + error = 0;
    +
    + for (i = vmudetails->dir_bnum;
    + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
    + brelse(bufhead);
    + bufhead = vmufat_sb_bread(sb, i);
    + if (!bufhead) {
    + error = -EIO;
    + goto out;
    + }
    + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
    + if (bufhead->b_data[j * VMU_DIR_RECORD_LEN] == 0)
    + goto fail;
    + /* get name and check for match */
    + memcpy(name,
    + bufhead->b_data + j * VMU_DIR_RECORD_LEN
    + + VMUFAT_NAME_OFFSET, VMUFAT_NAMELEN);
    + if (memcmp(dent->d_name.name, name,
    + dent->d_name.len) == 0) {
    + ino = vmufat_get_inode(sb,
    + le16_to_cpu(((u16 *) bufhead->b_data)
    + [j * VMU_DIR_RECORD_LEN16
    + + VMUFAT_FIRSTBLOCK_OFFSET16]));
    + if (IS_ERR_OR_NULL(ino)) {
    + if (IS_ERR(ino))
    + error = PTR_ERR(ino);
    + else
    + error = -EACCES;
    + goto out;
    + }
    + d_add(dent, ino);
    + goto out;
    + }
    + }
    + }
    +fail:
    + d_add(dent, NULL); /* Did not find the file */
    +out:
    + brelse(bufhead);
    + return ERR_PTR(error);
    +}
    +
    +/*
    + * Find a block marked free in the FAT
    + */
    +static int vmufat_find_free(struct super_block *sb)
    +{
    + struct memcard *vmudetails;
    + int found = 0, testblk, fatblk, error, index_to_fat;
    + int diff;
    + __le16 fatdata;
    + struct buffer_head *bh_fat;
    +
    + if (!sb || !sb->s_fs_info) {
    + error = -EINVAL;
    + goto fail;
    + }
    + vmudetails = sb->s_fs_info;
    +
    + for (fatblk = vmudetails->fat_bnum;
    + fatblk > vmudetails->fat_bnum - vmudetails->fat_len;
    + fatblk--) {
    + bh_fat = vmufat_sb_bread(sb, fatblk);
    + if (!bh_fat) {
    + error = -EIO;
    + goto fail;
    + }
    +
    + index_to_fat = 0xFF;
    + /* prefer not to allocate to higher blocks if we can
    + * to ensure full compatibility with Dreamcast devices */
    + diff = index_to_fat - VMUFAT_START_ALLOC;
    + for (testblk = index_to_fat; testblk > 0; testblk--) {
    + if (diff > 0 && testblk - diff < 0)
    + diff = -VMUFAT_START_ALLOC - 1;
    + fatdata =
    + le16_to_cpu(((u16 *) bh_fat->b_data)
    + [testblk - diff]);
    + if (fatdata == VMUFAT_UNALLOCATED) {
    + found = 1;
    + put_bh(bh_fat);
    + goto out_of_loop;
    + }
    + }
    + put_bh(bh_fat);
    + }
    +out_of_loop:
    + return (fatblk - 1 - vmudetails->fat_bnum + vmudetails->fat_len)
    + * VMU_BLK_SZ16 + testblk - diff;
    +
    + printk(KERN_WARNING "VMUFAT: volume is full\n");
    + error = -ENOSPC;
    +fail:
    + return error;
    +}
    +
    +/* read the FAT for a given block */
    +u16 vmufat_get_fat(struct super_block *sb, long block)
    +{
    + struct buffer_head *bufhead;
    + int offset;
    + u16 block_content = VMUFAT_ERROR;
    + struct memcard *vmudetails;
    + if (!sb || !sb->s_fs_info)
    + goto out;
    + vmudetails = sb->s_fs_info;
    +
    + /* which block in the FAT */
    + offset = block / VMU_BLK_SZ16;
    + if (offset >= vmudetails->fat_len)
    + goto out;
    +
    + /* fat_bnum points to highest block in FAT */
    + bufhead = vmufat_sb_bread(sb, offset + 1 +
    + vmudetails->fat_bnum - vmudetails->fat_len);
    + if (!bufhead)
    + goto out;
    + /* look inside the block */
    + block_content = le16_to_cpu(((u16 *)bufhead->b_data)
    + [block % VMU_BLK_SZ16]);
    + put_bh(bufhead);
    +out:
    + return block_content;
    +}
    +
    +/* set the FAT for a given block */
    +static int vmufat_set_fat(struct super_block *sb, long block,
    + u16 data_to_set)
    +{
    + struct buffer_head *bh;
    + int offset, error = 0;
    + struct memcard *vmudetails;
    + if (!sb || !sb->s_fs_info) {
    + error = -EINVAL;
    + goto out;
    + }
    + vmudetails = sb->s_fs_info;
    +
    + offset = block / VMU_BLK_SZ16;
    + if (offset >= vmudetails->fat_len) {
    + error = -EINVAL;
    + goto out;
    + }
    + bh = vmufat_sb_bread(sb, offset + 1 +
    + vmudetails->fat_bnum - vmudetails->fat_len);
    + if (!bh) {
    + error = -EIO;
    + goto out;
    + }
    + ((u16 *) bh->b_data)[block % VMU_BLK_SZ16] = cpu_to_le16(data_to_set);
    + mark_buffer_dirty(bh);
    + put_bh(bh);
    +out:
    + return 0;
    +}
    +
    +
    +static void vmufat_save_bcd_nortc(struct inode *in, char *bh, int index_to_dir)
    +{
    + long years, days;
    + unsigned char bcd_century, nl_day, bcd_month;
    + unsigned char u8year;
    + __kernel_time_t unix_date;
    +
    + unix_date = in->i_mtime.tv_sec;
    + days = unix_date / SECONDS_PER_DAY;
    + years = days / DAYS_PER_YEAR;
    + /* 1 Jan gets 1 day later after every leap year */
    + if ((years + 3) / 4 + DAYS_PER_YEAR * years >= days)
    + years--;
    + /* rebase days to account for leap years */
    + days -= (years + 3) / 4 + DAYS_PER_YEAR * years;
    + /* 1 Jan is Day 1 */
    + days++;
    + if (days == (FEB28 + 1) && !(years % 4)) {
    + nl_day = days;
    + bcd_month = 2;
    + } else {
    + nl_day = (years % 4) || days <= FEB28 ? days : days - 1;
    + for (bcd_month = 0; bcd_month < 12; bcd_month++)
    + if (day_n[bcd_month] > nl_day)
    + break;
    + }
    +
    + bcd_century = 19;
    + /* TODO:accounts for 21st century but will fail in 2100
    + because of leap days */
    + if (years > 29)
    + bcd_century += 1 + (years - 30)/100;
    +
    + bh[index_to_dir + VMUFAT_DIR_CENT] = bin2bcd(bcd_century);
    + u8year = years + 70; /* account for epoch */
    + if (u8year > 99)
    + u8year = u8year - 100;
    +
    + bh[index_to_dir + VMUFAT_DIR_YEAR] = bin2bcd(u8year);
    + bh[index_to_dir + VMUFAT_DIR_MONTH] = bin2bcd(bcd_month);
    + bh[index_to_dir + VMUFAT_DIR_DAY] =
    + bin2bcd(days - day_n[bcd_month - 1]);
    + bh[index_to_dir + VMUFAT_DIR_HOUR] =
    + bin2bcd((unix_date / SECONDS_PER_HOUR) % HOURS_PER_DAY);
    + bh[index_to_dir + VMUFAT_DIR_MIN] =
    + bin2bcd((unix_date / SIXTY_MINS_OR_SECS)
    + % SIXTY_MINS_OR_SECS);
    + bh[index_to_dir + VMUFAT_DIR_SEC] =
    + bin2bcd(unix_date % SIXTY_MINS_OR_SECS);
    +}
    +
    +static void vmufat_save_bcd_rtc(struct rtc_device *rtc, struct inode *in,
    + char *bh, int index_to_dir)
    +{
    + struct rtc_time now;
    +
    + if (rtc_read_time(rtc, &now) < 0)
    + vmufat_save_bcd_nortc(in, bh, index_to_dir);
    + bh[index_to_dir + VMUFAT_DIR_CENT] = bin2bcd((char)(now.tm_year/100));
    + bh[index_to_dir + VMUFAT_DIR_YEAR] = bin2bcd((char)(now.tm_year % 100));
    + bh[index_to_dir + VMUFAT_DIR_MONTH] = bin2bcd((char)(now.tm_mon));
    + bh[index_to_dir + VMUFAT_DIR_DAY] = bin2bcd((char)(now.tm_mday));
    + bh[index_to_dir + VMUFAT_DIR_HOUR] = bin2bcd((char)(now.tm_hour));
    + bh[index_to_dir + VMUFAT_DIR_MIN] = bin2bcd((char)(now.tm_min));
    + bh[index_to_dir + VMUFAT_DIR_SEC] = bin2bcd((char)(now.tm_sec));
    + bh[index_to_dir + VMUFAT_DIR_DOW] = bin2bcd((char)(now.tm_wday));
    +}
    +
    +/*
    + * write out the date in bcd format
    + * in the appropriate part of the
    + * directory entry
    + */
    +void vmufat_save_bcd(struct inode *in, char *bh, int index_to_dir)
    +{
    + struct rtc_device *rtc;
    + rtc = rtc_class_open("rtc0");
    + if (!rtc)
    + vmufat_save_bcd_nortc(in, bh, index_to_dir);
    + else {
    + vmufat_save_bcd_rtc(rtc, in, bh, index_to_dir);
    + rtc_class_close(rtc);
    + }
    +}
    +
    +static int vmufat_allocate_inode(umode_t imode,
    + struct super_block *sb, struct inode *in)
    +{
    + int error;
    + if (!sb || !in)
    + return -EINVAL;
    + /* Executable files must be at the start of the volume */
    + if (imode & EXEC) {
    + in->i_ino = VMUFAT_ZEROBLOCK;
    + if (vmufat_get_fat(sb, 0) != VMUFAT_UNALLOCATED) {
    + printk(KERN_WARNING "VMUFAT: cannot write excutable "
    + "file. Volume block 0 already allocated.\n");
    + error = -ENOSPC;
    + goto out;
    + }
    + error = 0;
    + } else {
    + error = vmufat_find_free(sb);
    + if (error >= 0)
    + in->i_ino = error;
    + }
    +out:
    + return error;
    +}
    +
    +static void vmufat_setup_inode(struct inode *in, umode_t imode,
    + struct super_block *sb)
    +{
    + if (!in)
    + return;
    +
    + in->i_uid = current_fsuid();
    + in->i_gid = current_fsgid();
    + in->i_mtime = in->i_atime = in->i_ctime = CURRENT_TIME;
    + in->i_mode = imode;
    + in->i_blocks = 1;
    + in->i_sb = sb;
    + insert_inode_hash(in);
    + in->i_op = &vmufat_inode_operations;
    + in->i_fop = &vmufat_file_operations;
    + in->i_mapping->a_ops = &vmufat_address_space_operations;
    +}
    +
    +static void vmu_handle_zeroblock(int recno, struct buffer_head *bh, int ino)
    +{
    + /* offset and header offset settings */
    + if (ino != VMUFAT_ZEROBLOCK) {
    + ((u16 *) bh->b_data)[recno + VMUFAT_START_OFFSET16] =
    + cpu_to_le16(ino);
    + ((u16 *) bh->b_data)[recno + VMUFAT_HEADER_OFFSET16] = 0;
    + } else {
    + ((u16 *) bh->b_data)[recno + VMUFAT_START_OFFSET16] = 0;
    + ((u16 *) bh->b_data)[recno + VMUFAT_HEADER_OFFSET16] = 1;
    + }
    +}
    +
    +static void vmu_write_name(int recno, struct buffer_head *bh, char *name,
    + int len)
    +{
    + memset((char *) (bh->b_data + recno + VMUFAT_NAME_OFFSET), '\0',
    + VMUFAT_NAMELEN);
    + memcpy((char *) (bh->b_data + recno + VMUFAT_NAME_OFFSET),
    + name, len);
    +}
    +
    +static int vmufat_inode_create(struct inode *dir, struct dentry *de,
    + umode_t imode, struct nameidata *nd)
    +{
    + /* Create an inode */
    + int i, j, found = 0, error = 0, freeblock;
    + struct inode *inode;
    + struct super_block *sb;
    + struct memcard *vmudetails;
    + struct buffer_head *bh = NULL;
    + if (!dir || !de) {
    + error = -EINVAL;
    + goto out;
    + }
    +
    + if (de->d_name.len > VMUFAT_NAMELEN) {
    + error = -ENAMETOOLONG;
    + goto out;
    + }
    +
    + sb = dir->i_sb;
    + if (!sb || !sb->s_fs_info) {
    + error = -EINVAL;
    + goto out;
    + }
    + vmudetails = sb->s_fs_info;
    +
    + inode = new_inode(sb);
    + if (!inode) {
    + error = -ENOSPC;
    + goto out;
    + }
    +
    + down_interruptible(&vmudetails->vmu_sem);
    + freeblock = vmufat_allocate_inode(imode, sb, inode);
    + if (freeblock < 0) {
    + up(&vmudetails->vmu_sem);
    + error = freeblock;
    + goto clean_inode;
    + }
    + /* mark as single block file - may grow later */
    + error = vmufat_set_fat(sb, freeblock, VMUFAT_FILE_END);
    + up(&vmudetails->vmu_sem);
    + if (error)
    + goto clean_inode;
    +
    + vmufat_setup_inode(inode, imode, sb);
    +
    + /* Write to the directory
    + * Now search for space for the directory entry */
    + down_interruptible(&vmudetails->vmu_sem);
    + for (i = vmudetails->dir_bnum;
    + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
    + brelse(bh);
    + bh = vmufat_sb_bread(sb, i);
    + if (!bh) {
    + up(&vmudetails->vmu_sem);
    + error = -EIO;
    + goto clean_fat;
    + }
    + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
    + if (((bh->b_data)[j * VMU_DIR_RECORD_LEN]) == 0) {
    + up(&vmudetails->vmu_sem);
    + found = 1;
    + goto dir_space_found;
    + }
    + }
    + }
    + if (found == 0)
    + goto clean_fat;
    +dir_space_found:
    + j = j * VMU_DIR_RECORD_LEN;
    + /* Have the directory entry
    + * so now update it */
    + if (imode & EXEC)
    + bh->b_data[j] = VMU_GAME; /* exec file */
    + else
    + bh->b_data[j] = VMU_DATA;
    +
    + /* copy protection settings */
    + if (bh->b_data[j + 1] != (char) NOCOPY)
    + bh->b_data[j + 1] = (char) CANCOPY;
    +
    + vmu_handle_zeroblock(j / 2, bh, inode->i_ino);
    + vmu_write_name(j, bh, (char *) de->d_name.name, de->d_name.len);
    +
    + /* BCD timestamp it */
    + vmufat_save_bcd(inode, bh->b_data, j);
    +
    + ((u16 *) bh->b_data)[j / 2 + VMUFAT_SIZE_OFFSET16] =
    + cpu_to_le16(inode->i_blocks);
    + mark_buffer_dirty(bh);
    + brelse(bh);
    + error = vmufat_list_blocks(inode);
    + if (error)
    + goto clean_fat;
    + up(&vmudetails->vmu_sem);
    + d_instantiate(de, inode);
    + return error;
    +
    +clean_fat:
    + vmufat_set_fat(sb, freeblock, VMUFAT_UNALLOCATED);
    + up(&vmudetails->vmu_sem);
    +clean_inode:
    + iput(inode);
    +out:
    + if (error < 0)
    + printk(KERN_ERR "VMUFAT: inode creation fails with error"
    + " %i\n", error);
    + return error;
    +}
    +
    +static int vmufat_readdir(struct file *filp, void *dirent, filldir_t filldir)
    +{
    + int filenamelen, index, j, k, error = -EINVAL;
    + struct vmufat_file_info *saved_file = NULL;
    + struct dentry *dentry;
    + struct inode *inode;
    + struct super_block *sb;
    + struct memcard *vmudetails;
    + struct buffer_head *bh = NULL;
    + if (!filp || !filp->f_dentry)
    + goto out;
    + dentry = filp->f_dentry;
    + inode = dentry->d_inode;
    + if (!inode)
    + goto out;
    + sb = inode->i_sb;
    + if (!sb)
    + goto out;
    + vmudetails = sb->s_fs_info;
    + if (!vmudetails)
    + goto out;
    + error = 0;
    +
    + index = filp->f_pos;
    + if (index > 200)
    + return -EIO;
    + /* handle . for this directory and .. for parent */
    + switch ((unsigned int) filp->f_pos) {
    + case 0:
    + error = filldir(dirent, ".", 1, index++, inode->i_ino, DT_DIR);
    + if (error < 0)
    + goto out;
    + case 1:
    + error = filldir(dirent, "..", 2, index++,
    + dentry->d_parent->d_inode->i_ino, DT_DIR);
    + if (error < 0)
    + goto out;
    + default:
    + break;
    + }
    +
    + saved_file =
    + kmalloc(sizeof(struct vmufat_file_info), GFP_KERNEL);
    + if (!saved_file) {
    + error = -ENOMEM;
    + goto out;
    + }
    +
    + for (j = vmudetails->dir_bnum -
    + (index - 2) / VMU_DIR_ENTRIES_PER_BLOCK;
    + j > vmudetails->dir_bnum - vmudetails->dir_len; j--) {
    + brelse(bh);
    + bh = vmufat_sb_bread(sb, j);
    + if (!bh) {
    + error = -EIO;
    + goto finish;
    + }
    + for (k = (index - 2) % VMU_DIR_ENTRIES_PER_BLOCK;
    + k < VMU_DIR_ENTRIES_PER_BLOCK; k++) {
    + saved_file->ftype = bh->b_data[k * VMU_DIR_RECORD_LEN];
    + if (saved_file->ftype == 0)
    + goto finish;
    + saved_file->fblk =
    + le16_to_cpu(((u16 *) bh->b_data)
    + [k * VMU_DIR_RECORD_LEN16 + 1]);
    + if (saved_file->fblk == 0)
    + saved_file->fblk = VMUFAT_ZEROBLOCK;
    + memcpy(saved_file->fname,
    + bh->b_data + k * VMU_DIR_RECORD_LEN
    + + VMUFAT_NAME_OFFSET, VMUFAT_NAMELEN);
    + filenamelen = strlen(saved_file->fname);
    + if (filenamelen > VMUFAT_NAMELEN)
    + filenamelen = VMUFAT_NAMELEN;
    + error = filldir(dirent, saved_file->fname, filenamelen,
    + index++, saved_file->fblk, DT_REG);
    + if (error < 0)
    + goto finish;
    + }
    + }
    +
    +finish:
    + filp->f_pos = index;
    + kfree(saved_file);
    + brelse(bh);
    +out:
    + return error;
    +}
    +
    +
    +int vmufat_list_blocks(struct inode *in)
    +{
    + struct vmufat_inode *vi;
    + struct super_block *sb;
    + long nextblock;
    + long ino;
    + struct memcard *vmudetails;
    + int error = -EINVAL;
    + struct list_head *iter, *iter2;
    + struct vmufat_block_list *vbl, *nvbl;
    + u16 fatdata;
    + if (!in || !in->i_sb)
    + goto out;
    +
    + vi = VMUFAT_I(in);
    + if (!vi)
    + goto out;
    + sb = in->i_sb;
    + ino = in->i_ino;
    + vmudetails = sb->s_fs_info;
    + if (!vmudetails)
    + goto out;
    + error = 0;
    + nextblock = ino;
    + if (nextblock == VMUFAT_ZEROBLOCK)
    + nextblock = 0;
    +
    + /* Delete any previous list of blocks */
    + list_for_each_safe(iter, iter2, &vi->blocks.b_list) {
    + vbl = list_entry(iter, struct vmufat_block_list, b_list);
    + list_del(iter);
    + kmem_cache_free(vmufat_blist_cachep, vbl);
    + }
    + vi->nblcks = 0;
    + do {
    + vbl = kmem_cache_alloc(vmufat_blist_cachep,
    + GFP_KERNEL);
    + if (!vbl) {
    + error = -ENOMEM;
    + goto unwind_out;
    + }
    + INIT_LIST_HEAD(&vbl->b_list);
    + vbl->bno = nextblock;
    + list_add_tail(&vbl->b_list, &vi->blocks.b_list);
    + vi->nblcks++;
    +
    + /* Find next block in the FAT - if there is one */
    + fatdata = vmufat_get_fat(sb, nextblock);
    + if (fatdata == VMUFAT_UNALLOCATED) {
    + printk(KERN_ERR "VMUFAT: FAT table appears to have"
    + " been corrupted.\n");
    + error = -EIO;
    + goto unwind_out;
    + }
    + if (fatdata == VMUFAT_FILE_END)
    + break; /*end of file */
    + nextblock = fatdata;
    + } while (1);
    +out:
    + return error;
    +
    +unwind_out:
    + list_for_each_entry_safe(vbl, nvbl, &vi->blocks.b_list, b_list) {
    + list_del_init(&vbl->b_list);
    + kmem_cache_free(vmufat_blist_cachep, vbl);
    + }
    + return error;
    +}
    +
    +static int vmufat_clean_fat(struct super_block *sb, int inum)
    +{
    + int error = 0;
    + u16 fatword, nextword;
    + if (!sb) {
    + error = -EINVAL;
    + goto out;
    + }
    +
    + nextword = inum;
    + do {
    + fatword = vmufat_get_fat(sb, nextword);
    + if (fatword == VMUFAT_ERROR) {
    + error = -EIO;
    + goto out;
    + }
    + error = vmufat_set_fat(sb, nextword, VMUFAT_UNALLOCATED);
    + if (error)
    + goto out;
    + if (fatword == VMUFAT_FILE_END)
    + goto out;
    + nextword = fatword;
    + } while (1);
    +out:
    + return error;
    +}
    +
    +/*
    + * Delete inode by marking space as free in FAT
    + * no need to waste time and effort by actually
    + * wiping underlying data on media
    + */
    +static void vmufat_remove_inode(struct inode *in)
    +{
    + struct buffer_head *bh = NULL, *bh_old = NULL;
    + struct super_block *sb;
    + struct memcard *vmudetails;
    + int i, j, k, l, startpt, found = 0;
    + if (!in || !in->i_sb)
    + goto ret;
    +
    + if (in->i_ino == VMUFAT_ZEROBLOCK)
    + in->i_ino = 0;
    + sb = in->i_sb;
    + vmudetails = sb->s_fs_info;
    + if (!vmudetails)
    + goto ret;
    + if (in->i_ino > vmudetails->fat_len * sb->s_blocksize / 2) {
    + printk(KERN_ERR "VMUFAT: attempting to delete"
    + "inode beyond device size");
    + goto ret;
    + }
    +
    + down_interruptible(&vmudetails->vmu_sem);
    + if (vmufat_clean_fat(sb, in->i_ino)) {
    + up(&vmudetails->vmu_sem);
    + goto failure;
    + }
    +
    + /* Now clean the directory entry
    + * Have to wander through this
    + * to find the appropriate entry */
    + for (i = vmudetails->dir_bnum;
    + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
    + brelse(bh);
    + bh = vmufat_sb_bread(sb, i);
    + if (!bh) {
    + up(&vmudetails->vmu_sem);
    + goto failure;
    + }
    + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
    + if (bh->b_data[j * VMU_DIR_RECORD_LEN] == 0) {
    + up(&vmudetails->vmu_sem);
    + goto failure;
    + }
    + if (le16_to_cpu(((u16 *) bh->b_data)
    + [j * VMU_DIR_RECORD_LEN16 +
    + VMUFAT_FIRSTBLOCK_OFFSET16]) == in->i_ino) {
    + found = 1;
    + goto found;
    + }
    + }
    + }
    +found:
    + if (found == 0) {
    + up(&vmudetails->vmu_sem);
    + goto failure;
    + }
    +
    + /* Found directory entry - so NULL it now */
    + for (k = 0; k < VMU_DIR_RECORD_LEN; k++)
    + bh->b_data[j * VMU_DIR_RECORD_LEN + k] = 0;
    + mark_buffer_dirty(bh);
    + /* Patch up directory, by moving up last file */
    + found = 0;
    + startpt = j + 1;
    + for (l = i; l > vmudetails->dir_bnum - vmudetails->dir_len; l--) {
    + bh_old = vmufat_sb_bread(sb, l);
    + if (!bh_old) {
    + up(&vmudetails->vmu_sem);
    + goto failure;
    + }
    + for (k = startpt; k < VMU_DIR_ENTRIES_PER_BLOCK; k++) {
    + if (bh_old->b_data[k * VMU_DIR_RECORD_LEN] == 0) {
    + found = 1;
    + brelse(bh_old);
    + goto lastdirfound;
    + }
    + }
    + startpt = 0;
    + brelse(bh_old);
    + }
    +lastdirfound:
    + if (found == 0) { /* full directory */
    + l = vmudetails->dir_bnum - vmudetails->dir_len + 1;
    + k = VMU_DIR_ENTRIES_PER_BLOCK;
    + } else if (l == i && k == j + 1) /* deleted entry was last in dir */
    + goto finish;
    + else if (k == 0) {
    + l = l + 1;
    + k = VMU_DIR_ENTRIES_PER_BLOCK;
    + if (l == i && k == j + 1)
    + goto finish;
    + }
    + /* fill gap first then wipe out old entry */
    + bh_old = vmufat_sb_bread(sb, l);
    + if (!bh_old) {
    + up(&vmudetails->vmu_sem);
    + brelse(bh);
    + goto failure;
    + }
    + for (i = 0; i < VMU_DIR_RECORD_LEN; i++) {
    + bh->b_data[j * VMU_DIR_RECORD_LEN + i] =
    + bh_old->b_data[(k - 1) * VMU_DIR_RECORD_LEN + i];
    + bh_old->b_data[(k - 1) * VMU_DIR_RECORD_LEN + i] = 0;
    + }
    + mark_buffer_dirty(bh_old);
    + mark_buffer_dirty(bh);
    + brelse(bh_old);
    +
    +finish:
    + up(&vmudetails->vmu_sem);
    + brelse(bh);
    + return;
    +
    +failure:
    + printk(KERN_ERR "VMUFAT: Failure to read volume,"
    + " could not delete inode - filesystem may be damaged\n");
    +ret:
    + return;
    +}
    +
    +/*
    + * vmufat_unlink - delete a file pointed to
    + * by the dentry (only one directory in a
    + * vmufat fs so safe to ignore the inode
    + * supplied here
    + */
    +static int vmufat_unlink(struct inode *dir, struct dentry *dentry)
    +{
    + struct inode *in;
    + if (!dentry || !dentry->d_inode)
    + return -EINVAL;
    + in = dentry->d_inode;
    + vmufat_remove_inode(in);
    + return 0;
    +}
    +
    +static int vmufat_get_block(struct inode *inode, sector_t iblock,
    + struct buffer_head *bh_result, int create)
    +{
    + struct vmufat_inode *vin;
    + struct vmufat_block_list *vlist, *vblk;
    + struct super_block *sb;
    + struct memcard *vmudetails;
    + int cural;
    + int finblk, nxtblk, exeblk;
    + struct list_head *iter;
    + sector_t cntdwn = iblock;
    + sector_t phys;
    + int error = -EINVAL;
    + if (!inode || !inode->i_sb)
    + goto out;
    + vin = VMUFAT_I(inode);
    + if (!vin || !(&vin->blocks))
    + goto out;
    + vlist = &vin->blocks;
    + sb = inode->i_sb;
    + vmudetails = sb->s_fs_info;
    + if (!vmudetails)
    + goto out;
    +
    + if (vin->nblcks <= 0)
    + goto out;
    + if (iblock < vin->nblcks) {
    + /* block is already here so read it into the buffer head */
    + list_for_each(iter, &vlist->b_list) {
    + if (cntdwn-- == 0)
    + break;
    + }
    + vblk = list_entry(iter, struct vmufat_block_list, b_list);
    + clear_buffer_new(bh_result);
    + error = 0;
    + phys = vblk->bno;
    + goto got_it;
    + }
    + if (!create)
    + goto out;
    + /*
    + * check not looking for a block too far
    + * beyond the end of the existing file
    + */
    + if (iblock > vin->nblcks)
    + goto out;
    +
    + /* if looking for a block that is not current - allocate it*/
    + cural = vin->nblcks;
    + list_for_each(iter, &vlist->b_list) {
    + if (cural-- == 1)
    + break;
    + }
    + vblk = list_entry(iter, struct vmufat_block_list, b_list);
    + finblk = vblk->bno;
    +
    + down_interruptible(&vmudetails->vmu_sem);
    + /* Exec files have to be linear */
    + if (inode->i_ino == 0) {
    + exeblk = vmufat_get_fat(sb, finblk + 1);
    + if (exeblk != VMUFAT_UNALLOCATED) {
    + up(&vmudetails->vmu_sem);
    + printk(KERN_WARNING "VMUFAT: Cannot allocate linear "
    + "space needed for executible\n");
    + error = -ENOSPC;
    + goto out;
    + }
    + nxtblk = finblk + 1;
    + } else {
    + nxtblk = vmufat_find_free(sb);
    + if (nxtblk < 0) {
    + up(&vmudetails->vmu_sem);
    + error = nxtblk;
    + goto out;
    + }
    + }
    + error = vmufat_set_fat(sb, finblk, nxtblk);
    + if (error) {
    + up(&vmudetails->vmu_sem);
    + goto out;
    + }
    + error = vmufat_set_fat(sb, nxtblk, VMUFAT_FILE_END);
    + up(&vmudetails->vmu_sem);
    + if (error)
    + goto out;
    + error = vmufat_list_blocks(inode);
    + mark_inode_dirty(inode);
    + if (error)
    + goto out;
    + set_buffer_new(bh_result);
    + phys = nxtblk;
    + error = 0;
    +got_it:
    + map_bh(bh_result, sb, phys);
    +out:
    + return error;
    +}
    +
    +static int vmufat_writepage(struct page *page, struct writeback_control *wbc)
    +{
    + return block_write_full_page(page, vmufat_get_block, wbc);
    +}
    +
    +static int vmufat_write_begin(struct file *filp, struct address_space *mapping,
    + loff_t pos, unsigned len, unsigned flags,
    + struct page **pagep, void **fsdata)
    +{
    + *pagep = NULL;
    + return block_write_begin(mapping, pos, len, flags, pagep,
    + vmufat_get_block);
    +}
    +
    +static int vmufat_readpage(struct file *file, struct page *page)
    +{
    + return block_read_full_page(page, vmufat_get_block);
    +}
    +
    +
    +const struct address_space_operations
    + vmufat_address_space_operations = {
    + .readpage = vmufat_readpage,
    + .writepage = vmufat_writepage,
    + .write_begin = vmufat_write_begin,
    + .write_end = generic_write_end,
    +};
    +
    +const struct inode_operations vmufat_inode_operations = {
    + .lookup = vmufat_inode_lookup,
    + .create = vmufat_inode_create,
    + .unlink = vmufat_unlink,
    +};
    +
    +const struct file_operations vmufat_file_dir_operations = {
    + .owner = THIS_MODULE,
    + .read = generic_read_dir,
    + .readdir = vmufat_readdir,
    + .fsync = generic_file_fsync,
    +};
    +
    +const struct file_operations vmufat_file_operations = {
    + .llseek = generic_file_llseek,
    + .read = do_sync_read,
    + .write = do_sync_write,
    + .aio_read = generic_file_aio_read,
    + .aio_write = generic_file_aio_write,
    + .fsync = generic_file_fsync,
    +};

    \
     
     \ /
      Last update: 2012-03-21 05:35    [W:0.072 / U:30.812 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site