Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add support for VMUFAT

  • Loading branch information...
commit 47305b24487ce6d6d9264bddf2ccf75e5123c1b1 1 parent 0eec698
Adrian McMenamin authored
83 Documentation/filesystems/vmufat.txt
... ... @@ -0,0 +1,83 @@
  1 +VMUFAT FILESYSTEM
  2 +
  3 +VMUFAT is the simple file allocation table (FAT) based filesystem used in Sega
  4 +Dreamcast Visual Memory Units (VMUs) and various Dreamcast emulators and
  5 +Android apps etc.
  6 +
  7 +It is not recommended for general use, but does not require a Dreamcast.
  8 +
  9 +All the physical VMU devices that were made were of the same size 256 blocks
  10 +of 512 octets - 128KB in total. But the specification supports a wider range
  11 +of sizes and the filesystem in the Linux kernel is capable of handling volumes
  12 +of a size between 4 blocks and 65536 blocks.
  13 +
  14 +The standard 256 block VMU is described below:
  15 +
  16 +BLOCK NO CONTENT
  17 +0 Space used by Dreamcast
  18 + to save files
  19 +199
  20 +200 Space which can be used but
  21 + which the Dreamcast ignores
  22 +240
  23 +241 Directory (can hold 208
  24 + file records)
  25 +253
  26 +254 File Allocation Table (FAT)
  27 +255 Root Block
  28 +
  29 +The standard VMU filesystem has 241 blocks which can be used for file storage
  30 +but a Dreamcast will only use 200 of these. The Linux kernel driver prefers to
  31 +use blocks 0 - 199 when allocating blocks to files, but will use blocks 200 -
  32 +240 if lower numbered blocks are not available.
  33 +
  34 +An executible file (generally a game written in the native machine code of
  35 +the VMU's microcontroller) must begin at block 0 and be stored linearly in
  36 +the volume.
  37 +
  38 +DIRECTORY
  39 +The directory contains records 32 octets long (format detail below from Marcus
  40 +Comstedt's website - http://mc.pp.se/dc/vms/flashmem.html)
  41 +0x00 : file type (0x00 = no file, 0x33 = data, 0xcc = game)
  42 +0x01 : copy protect (0x00 = copy ok, 0xff = copy protected)
  43 +0x02-0x03 : 16 bits (little endian) : location of first block
  44 +0x04-0x0f : ASCII string : filename (12 characters)
  45 +0x10-0x17 : Binary Coded Decimal timestamp: file creation time
  46 +0x18-0x19 : 16 bits (little endian) : file size (in blocks)
  47 +0x1a-0x1b : 16 bits (little endian) : offset of header (in blocks)
  48 + from file start
  49 +
  50 +Header positioning is a matter for executible files written in native
  51 +code for a physical VMU (an 8 bit Sanyo microcontroller).
  52 +
  53 +BCD dates are encoded as follows:
  54 +Century prefix (eg., 20)
  55 +Year (eg., 12)
  56 +Month (eg., 02)
  57 +Day in month (eg., 29)
  58 +Hour (eg., 20)
  59 +Minute (eg., 49)
  60 +Second (eg., 22)
  61 +Day of week (Monday is 00, Sunday is 06)
  62 +
  63 +FILE ALLOCATION TABLE
  64 +Every block in the volume is mapped to the FAT eg., block 0 of the VMU maps to
  65 +the first 16 bits of the FAT and so on. Blocks marked 0xFFFC are empty, blocks
  66 +allocated to a file are either numbered with with next block or, if the final
  67 +block are marked 0xFFFA.
  68 +
  69 +ROOT BLOCK
  70 +The first 16 octets are marked as 0x55 - we use this to provide the "magic
  71 +number" for this filesystem. The octets at 0x10 - 0x14 are used to determine
  72 +the colour the representation of VMU is displayed in by a Dreamcast and our
  73 +filesystem does not touch these.
  74 +Octets 0x30 - 0x37 contain the BCD timestamp of the VMU.
  75 +Octets 0x46 - 0x47 contain the location (little endian format) of the FAT
  76 +Octets 0x48 - 0x49 contain the size (little endian) of the FAT
  77 +Octets 0x4A - 0x4B contain the location (little endian) of the Directory
  78 +Octets 0x4C - 0x4D contain the size (little endian) of the Directory
  79 +Octets 0x4E - 0x4F is Dreamcast specific (icon shape)
  80 +Octets 0x50 - 0x51 contain the number (little endian) of user blocks - NB this
  81 +is marked as 200 in a physical VMU although there are in fact 241 usable
  82 +blocks - the reason appears to be that the Directory is not big enough to
  83 +support 1 block files in all user blocks.
1  fs/Kconfig
@@ -215,6 +215,7 @@ source "fs/pstore/Kconfig"
215 215 source "fs/sysv/Kconfig"
216 216 source "fs/ufs/Kconfig"
217 217 source "fs/exofs/Kconfig"
  218 +source "fs/vmufat/Kconfig"
218 219
219 220 endif # MISC_FILESYSTEMS
220 221
1  fs/Makefile
@@ -124,3 +124,4 @@ obj-$(CONFIG_GFS2_FS) += gfs2/
124 124 obj-y += exofs/ # Multiple modules
125 125 obj-$(CONFIG_CEPH_FS) += ceph/
126 126 obj-$(CONFIG_PSTORE) += pstore/
  127 +obj-$(CONFIG_VMUFAT_FS) += vmufat/
14 fs/vmufat/Kconfig
... ... @@ -0,0 +1,14 @@
  1 +config VMUFAT_FS
  2 + tristate "Dreamcast VMU FAT filesystem"
  3 + depends on BLOCK
  4 + help
  5 + This implements the simple FAT type filesystem found on SEGA
  6 + Dreamcast visual memory units.
  7 +
  8 + Dreamcast users who want to mount their VMUs to view the native
  9 + filesystem will say 'Y' here. The filesystem is hardware independent
  10 + but is not recommended for any serious use in other circumstances, so
  11 + just about everyone else should say 'N'.
  12 +
  13 + To compile this as a module say 'M' here. The module will be called
  14 + vmufat
7 fs/vmufat/Makefile
... ... @@ -0,0 +1,7 @@
  1 +#
  2 +# Makefile for VMUFAT filesystem
  3 +#
  4 +
  5 +obj-$(CONFIG_VMUFAT_FS) += vmufat.o
  6 +
  7 +vmufat-y := inode.o super.o
903 fs/vmufat/inode.c
... ... @@ -0,0 +1,903 @@
  1 +/*
  2 + * VMUFAT file system
  3 + *
  4 + * Copyright (C) 2002 - 2012 Adrian McMenamin
  5 + * Copyright (C) 2002 Paul Mundt
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; either version 2 of the License, or
  10 + * (at your option) any later version.
  11 + *
  12 + * This program is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License
  18 + * along with this program; if not, write to the Free Software
  19 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20 + */
  21 +
  22 +#include <linux/fs.h>
  23 +#include <linux/bcd.h>
  24 +#include <linux/rtc.h>
  25 +#include <linux/slab.h>
  26 +#include <linux/sched.h>
  27 +#include <linux/magic.h>
  28 +#include <linux/device.h>
  29 +#include <linux/module.h>
  30 +#include <linux/statfs.h>
  31 +#include <linux/buffer_head.h>
  32 +#include "vmufat.h"
  33 +
  34 +const struct inode_operations vmufat_inode_operations;
  35 +const struct file_operations vmufat_file_operations;
  36 +const struct address_space_operations vmufat_address_space_operations;
  37 +const struct file_operations vmufat_file_dir_operations;
  38 +struct kmem_cache *vmufat_blist_cachep;
  39 +/* Linear day numbers of the respective 1sts in non-leap years. */
  40 +int day_n[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  41 +
  42 +static struct dentry *vmufat_inode_lookup(struct inode *in, struct dentry *dent,
  43 + struct nameidata *ignored)
  44 +{
  45 + struct super_block *sb;
  46 + struct memcard *vmudetails;
  47 + struct buffer_head *bh = NULL;
  48 + struct inode *ino;
  49 + int i, j, error = 0;
  50 +
  51 + if (dent->d_name.len > VMUFAT_NAMELEN) {
  52 + error = -ENAMETOOLONG;
  53 + goto out;
  54 + }
  55 + sb = in->i_sb;
  56 + vmudetails = sb->s_fs_info;
  57 +
  58 + for (i = vmudetails->dir_bnum;
  59 + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
  60 + brelse(bh);
  61 + bh = vmufat_sb_bread(sb, i);
  62 + if (!bh) {
  63 + error = -EIO;
  64 + goto out;
  65 + }
  66 + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
  67 + int record_offset = j * VMU_DIR_RECORD_LEN;
  68 + if (bh->b_data[record_offset] == 0)
  69 + goto fail;
  70 + if (memcmp(dent->d_name.name,
  71 + bh->b_data + record_offset + VMUFAT_NAME_OFFSET,
  72 + dent->d_name.len) == 0) {
  73 + ino = vmufat_get_inode(sb,
  74 + le16_to_cpu(((u16 *) bh->b_data)
  75 + [record_offset
  76 + + VMUFAT_FIRSTBLOCK_OFFSET16]));
  77 + if (IS_ERR(ino)) {
  78 + error = PTR_ERR(ino);
  79 + goto out;
  80 + } else if (!ino) {
  81 + error = -EACCES;
  82 + goto out;
  83 + }
  84 + d_add(dent, ino);
  85 + goto out;
  86 + }
  87 + }
  88 + }
  89 +fail:
  90 + d_add(dent, NULL); /* Did not find the file */
  91 +out:
  92 + brelse(bh);
  93 + return ERR_PTR(error);
  94 +}
  95 +
  96 +static int vmufat_get_freeblock(int start, int end, struct buffer_head *bh)
  97 +{
  98 + int i, ret = -1;
  99 + __le16 fatdata;
  100 +
  101 + for (i = start; i >= end; i--) {
  102 + fatdata = le16_to_cpu(((u16 *)bh->b_data)[i]);
  103 + if (fatdata == VMUFAT_UNALLOCATED) {
  104 + ret = i;
  105 + break;
  106 + }
  107 + }
  108 + return ret;
  109 +}
  110 +
  111 +/*
  112 + * Find a block marked free in the FAT
  113 + */
  114 +static int vmufat_find_free(struct super_block *sb)
  115 +{
  116 + struct memcard *vmudetails;
  117 + int testblk, fatblk, ret;
  118 + struct buffer_head *bh_fat;
  119 +
  120 + vmudetails = sb->s_fs_info;
  121 +
  122 + for (fatblk = vmudetails->fat_bnum;
  123 + fatblk > vmudetails->fat_bnum - vmudetails->fat_len;
  124 + fatblk--) {
  125 + bh_fat = vmufat_sb_bread(sb, fatblk);
  126 + if (!bh_fat) {
  127 + ret = -EIO;
  128 + goto fail;
  129 + }
  130 +
  131 + /* Handle 256 block VMUs like physical devices
  132 + * and other VMUs more simply
  133 + */
  134 + if (vmudetails->sb_bnum != VMU_BLK_SZ16) {
  135 + /* Cannot be physical VMU */
  136 + testblk = vmufat_get_freeblock(VMU_BLK_SZ16, 0, bh_fat);
  137 + put_bh(bh_fat);
  138 + if (testblk >= 0)
  139 + goto out_of_loop;
  140 + } else { /* Physical VMU or logical VMU with same size */
  141 + testblk = vmufat_get_freeblock(VMUFAT_START_ALLOC, 0,
  142 + bh_fat);
  143 + if (testblk >= 0) {
  144 + put_bh(bh_fat);
  145 + goto out_of_loop;
  146 + }
  147 + /* Only allocate to higher blocks if no space left */
  148 + testblk = vmufat_get_freeblock(VMU_BLK_SZ16,
  149 + VMUFAT_START_ALLOC + 1, bh_fat);
  150 + put_bh(bh_fat);
  151 + if (testblk > VMUFAT_START_ALLOC)
  152 + goto out_of_loop;
  153 + }
  154 + }
  155 + printk(KERN_INFO "VMUFAT: volume is full\n");
  156 + ret = -ENOSPC;
  157 + goto fail;
  158 +
  159 +out_of_loop:
  160 + ret = (fatblk - 1 - vmudetails->fat_bnum + vmudetails->fat_len)
  161 + * VMU_BLK_SZ16 + testblk;
  162 +fail:
  163 + return ret;
  164 +}
  165 +
  166 +/* read the FAT for a given block */
  167 +u16 vmufat_get_fat(struct super_block *sb, long block)
  168 +{
  169 + struct buffer_head *bufhead;
  170 + int offset;
  171 + u16 block_content = VMUFAT_ERROR;
  172 + struct memcard *vmudetails = sb->s_fs_info;
  173 +
  174 + /* which block in the FAT */
  175 + offset = block / VMU_BLK_SZ16;
  176 + if (offset >= vmudetails->fat_len)
  177 + goto out;
  178 +
  179 + /* fat_bnum points to highest block in FAT */
  180 + bufhead = vmufat_sb_bread(sb, offset + 1 +
  181 + vmudetails->fat_bnum - vmudetails->fat_len);
  182 + if (!bufhead)
  183 + goto out;
  184 + /* look inside the block */
  185 + block_content = le16_to_cpu(((u16 *)bufhead->b_data)
  186 + [block % VMU_BLK_SZ16]);
  187 + put_bh(bufhead);
  188 +out:
  189 + return block_content;
  190 +}
  191 +
  192 +/* set the FAT for a given block */
  193 +static int vmufat_set_fat(struct super_block *sb, long block,
  194 + u16 data_to_set)
  195 +{
  196 + struct buffer_head *bh;
  197 + int offset, error = 0;
  198 + struct memcard *vmudetails = sb->s_fs_info;
  199 +
  200 + offset = block / VMU_BLK_SZ16;
  201 + if (offset >= vmudetails->fat_len) {
  202 + error = -EINVAL;
  203 + goto out;
  204 + }
  205 + bh = vmufat_sb_bread(sb, offset + 1 +
  206 + vmudetails->fat_bnum - vmudetails->fat_len);
  207 + if (!bh) {
  208 + error = -EIO;
  209 + goto out;
  210 + }
  211 + ((u16 *) bh->b_data)[block % VMU_BLK_SZ16] = cpu_to_le16(data_to_set);
  212 + mark_buffer_dirty(bh);
  213 + put_bh(bh);
  214 +out:
  215 + return error;
  216 +}
  217 +
  218 +
  219 +static void vmufat_save_bcd_nortc(struct inode *in, char *bh, int index_to_dir)
  220 +{
  221 + long years, days;
  222 + unsigned char bcd_century, nl_day, bcd_month;
  223 + unsigned char u8year;
  224 + __kernel_time_t unix_date;
  225 +
  226 + unix_date = in->i_mtime.tv_sec;
  227 + days = unix_date / SECONDS_PER_DAY;
  228 + years = days / DAYS_PER_YEAR;
  229 + /* 1 Jan gets 1 day later after every leap year */
  230 + if ((years + 3) / 4 + DAYS_PER_YEAR * years >= days)
  231 + years--;
  232 + /* rebase days to account for leap years */
  233 + days -= (years + 3) / 4 + DAYS_PER_YEAR * years;
  234 + /* 1 Jan is Day 1 */
  235 + days++;
  236 + if (days == (FEB28 + 1) && !(years % 4)) {
  237 + nl_day = days;
  238 + bcd_month = 2;
  239 + } else {
  240 + nl_day = (years % 4) || days <= FEB28 ? days : days - 1;
  241 + for (bcd_month = 0; bcd_month < 12; bcd_month++)
  242 + if (day_n[bcd_month] > nl_day)
  243 + break;
  244 + }
  245 +
  246 + bcd_century = 19;
  247 + /* TODO:accounts for 21st century but will fail in 2100
  248 + because of leap days */
  249 + if (years > 29)
  250 + bcd_century += 1 + (years - 30)/100;
  251 +
  252 + bh[index_to_dir + VMUFAT_DIR_CENT] = bin2bcd(bcd_century);
  253 + u8year = years + 70; /* account for epoch */
  254 + if (u8year > 99)
  255 + u8year = u8year - 100;
  256 +
  257 + bh[index_to_dir + VMUFAT_DIR_YEAR] = bin2bcd(u8year);
  258 + bh[index_to_dir + VMUFAT_DIR_MONTH] = bin2bcd(bcd_month);
  259 + bh[index_to_dir + VMUFAT_DIR_DAY] =
  260 + bin2bcd(days - day_n[bcd_month - 1]);
  261 + bh[index_to_dir + VMUFAT_DIR_HOUR] =
  262 + bin2bcd((unix_date / SECONDS_PER_HOUR) % HOURS_PER_DAY);
  263 + bh[index_to_dir + VMUFAT_DIR_MIN] =
  264 + bin2bcd((unix_date / SIXTY_MINS_OR_SECS)
  265 + % SIXTY_MINS_OR_SECS);
  266 + bh[index_to_dir + VMUFAT_DIR_SEC] =
  267 + bin2bcd(unix_date % SIXTY_MINS_OR_SECS);
  268 +}
  269 +
  270 +static void vmufat_save_bcd_rtc(struct rtc_device *rtc, struct inode *in,
  271 + char *bh, int index_to_dir)
  272 +{
  273 + struct rtc_time now;
  274 +
  275 + if (rtc_read_time(rtc, &now) < 0)
  276 + vmufat_save_bcd_nortc(in, bh, index_to_dir);
  277 + bh[index_to_dir + VMUFAT_DIR_CENT] = bin2bcd((char)(now.tm_year/100));
  278 + bh[index_to_dir + VMUFAT_DIR_YEAR] = bin2bcd((char)(now.tm_year % 100));
  279 + bh[index_to_dir + VMUFAT_DIR_MONTH] = bin2bcd((char)(now.tm_mon));
  280 + bh[index_to_dir + VMUFAT_DIR_DAY] = bin2bcd((char)(now.tm_mday));
  281 + bh[index_to_dir + VMUFAT_DIR_HOUR] = bin2bcd((char)(now.tm_hour));
  282 + bh[index_to_dir + VMUFAT_DIR_MIN] = bin2bcd((char)(now.tm_min));
  283 + bh[index_to_dir + VMUFAT_DIR_SEC] = bin2bcd((char)(now.tm_sec));
  284 + bh[index_to_dir + VMUFAT_DIR_DOW] = bin2bcd((char)(now.tm_wday));
  285 +}
  286 +
  287 +/*
  288 + * write out the date in bcd format
  289 + * in the appropriate part of the
  290 + * directory entry
  291 + */
  292 +void vmufat_save_bcd(struct inode *in, char *bh, int index_to_dir)
  293 +{
  294 + struct rtc_device *rtc;
  295 + rtc = rtc_class_open("rtc0");
  296 + if (!rtc)
  297 + vmufat_save_bcd_nortc(in, bh, index_to_dir);
  298 + else {
  299 + vmufat_save_bcd_rtc(rtc, in, bh, index_to_dir);
  300 + rtc_class_close(rtc);
  301 + }
  302 +}
  303 +
  304 +static int vmufat_allocate_inode(umode_t imode,
  305 + struct super_block *sb, struct inode *in)
  306 +{
  307 + int error = 0;
  308 + /* Executable files must be at the start of the volume */
  309 + if (imode & EXEC) {
  310 + in->i_ino = VMUFAT_ZEROBLOCK;
  311 + if (vmufat_get_fat(sb, 0) != VMUFAT_UNALLOCATED) {
  312 + printk(KERN_INFO "VMUFAT: cannot write excutable "
  313 + "file. Volume block 0 already allocated.\n");
  314 + error = -ENOSPC;
  315 + goto out;
  316 + }
  317 + } else {
  318 + error = vmufat_find_free(sb);
  319 + if (error >= 0)
  320 + in->i_ino = error;
  321 + }
  322 +out:
  323 + return error;
  324 +}
  325 +
  326 +static void vmufat_setup_inode(struct inode *in, umode_t imode,
  327 + struct super_block *sb)
  328 +{
  329 + in->i_uid = current_fsuid();
  330 + in->i_gid = current_fsgid();
  331 + in->i_mtime = in->i_atime = in->i_ctime = CURRENT_TIME;
  332 + in->i_mode = imode;
  333 + in->i_blocks = 1;
  334 + in->i_sb = sb;
  335 + insert_inode_hash(in);
  336 + in->i_op = &vmufat_inode_operations;
  337 + in->i_fop = &vmufat_file_operations;
  338 + in->i_mapping->a_ops = &vmufat_address_space_operations;
  339 +}
  340 +
  341 +static void vmu_handle_zeroblock(int recno, struct buffer_head *bh, int ino)
  342 +{
  343 + /* offset and header offset settings */
  344 + if (ino != VMUFAT_ZEROBLOCK) {
  345 + ((u16 *) bh->b_data)[recno + VMUFAT_START_OFFSET16] =
  346 + cpu_to_le16(ino);
  347 + ((u16 *) bh->b_data)[recno + VMUFAT_HEADER_OFFSET16] = 0;
  348 + } else {
  349 + ((u16 *) bh->b_data)[recno + VMUFAT_START_OFFSET16] = 0;
  350 + ((u16 *) bh->b_data)[recno + VMUFAT_HEADER_OFFSET16] =
  351 + cpu_to_le16(1);
  352 + }
  353 +}
  354 +
  355 +static void vmu_write_name(int recno, struct buffer_head *bh, char *name,
  356 + int len)
  357 +{
  358 + memset((char *) (bh->b_data + recno + VMUFAT_NAME_OFFSET), '\0',
  359 + VMUFAT_NAMELEN);
  360 + memcpy((char *) (bh->b_data + recno + VMUFAT_NAME_OFFSET),
  361 + name, len);
  362 +}
  363 +
  364 +static int vmufat_inode_create(struct inode *dir, struct dentry *de,
  365 + umode_t imode, struct nameidata *nd)
  366 +{
  367 + int i, j, entry, found = 0, error = 0, freeblock;
  368 + struct inode *inode;
  369 + struct super_block *sb;
  370 + struct memcard *vmudetails;
  371 + struct buffer_head *bh = NULL;
  372 +
  373 + sb = dir->i_sb;
  374 + vmudetails = sb->s_fs_info;
  375 + inode = new_inode(sb);
  376 + if (!inode) {
  377 + error = -ENOSPC;
  378 + goto out;
  379 + }
  380 +
  381 + mutex_lock(&vmudetails->mutex);
  382 + freeblock = vmufat_allocate_inode(imode, sb, inode);
  383 + if (freeblock < 0) {
  384 + mutex_unlock(&vmudetails->mutex);
  385 + error = freeblock;
  386 + goto clean_inode;
  387 + }
  388 + /* mark as single block file - may grow later */
  389 + error = vmufat_set_fat(sb, freeblock, VMUFAT_FILE_END);
  390 + mutex_unlock(&vmudetails->mutex);
  391 + if (error)
  392 + goto clean_inode;
  393 +
  394 + vmufat_setup_inode(inode, imode, sb);
  395 +
  396 + /* Write to the directory
  397 + * Now search for space for the directory entry */
  398 + mutex_lock(&vmudetails->mutex);
  399 + for (i = vmudetails->dir_bnum;
  400 + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
  401 + brelse(bh);
  402 + bh = vmufat_sb_bread(sb, i);
  403 + if (!bh) {
  404 + mutex_unlock(&vmudetails->mutex);
  405 + error = -EIO;
  406 + goto clean_fat;
  407 + }
  408 + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
  409 + entry = j * VMU_DIR_RECORD_LEN;
  410 + if (((bh->b_data)[entry]) == 0) {
  411 + found = 1;
  412 + goto dir_space_found;
  413 + }
  414 + }
  415 + }
  416 + if (found == 0)
  417 + goto clean_fat;
  418 +dir_space_found:
  419 + /* Have the directory entry
  420 + * so now update it */
  421 + if (imode & EXEC)
  422 + bh->b_data[entry] = VMU_GAME;
  423 + else
  424 + bh->b_data[entry] = VMU_DATA;
  425 +
  426 + /* copy protection settings */
  427 + if (bh->b_data[entry + 1] != (char) NOCOPY)
  428 + bh->b_data[entry + 1] = (char) CANCOPY;
  429 +
  430 + vmu_handle_zeroblock(entry / 2, bh, inode->i_ino);
  431 + vmu_write_name(entry, bh, (char *) de->d_name.name, de->d_name.len);
  432 +
  433 + /* BCD timestamp it */
  434 + vmufat_save_bcd(inode, bh->b_data, entry);
  435 +
  436 + ((u16 *) bh->b_data)[entry / 2 + VMUFAT_SIZE_OFFSET16] =
  437 + cpu_to_le16(inode->i_blocks);
  438 + mark_buffer_dirty(bh);
  439 + brelse(bh);
  440 + error = vmufat_list_blocks(inode);
  441 + if (error)
  442 + goto clean_fat;
  443 + mutex_unlock(&vmudetails->mutex);
  444 + d_instantiate(de, inode);
  445 + return error;
  446 +
  447 +clean_fat:
  448 + vmufat_set_fat(sb, freeblock, VMUFAT_UNALLOCATED);
  449 + mutex_unlock(&vmudetails->mutex);
  450 +clean_inode:
  451 + iput(inode);
  452 +out:
  453 + if (error < 0)
  454 + printk(KERN_INFO "VMUFAT: inode creation fails with error"
  455 + " %i\n", error);
  456 + return error;
  457 +}
  458 +
  459 +static int vmufat_readdir(struct file *filp, void *dirent, filldir_t filldir)
  460 +{
  461 + int filenamelen, index, j, k, error = 0;
  462 + struct vmufat_file_info *saved_file = NULL;
  463 + struct dentry *dentry;
  464 + struct inode *inode;
  465 + struct super_block *sb;
  466 + struct memcard *vmudetails;
  467 + struct buffer_head *bh = NULL;
  468 +
  469 + dentry = filp->f_dentry;
  470 + inode = dentry->d_inode;
  471 + sb = inode->i_sb;
  472 + vmudetails = sb->s_fs_info;
  473 + index = filp->f_pos;
  474 + /* handle . for this directory and .. for parent */
  475 + switch ((unsigned int) filp->f_pos) {
  476 + case 0:
  477 + error = filldir(dirent, ".", 1, index++, inode->i_ino, DT_DIR);
  478 + if (error < 0)
  479 + goto out;
  480 + case 1:
  481 + error = filldir(dirent, "..", 2, index++,
  482 + dentry->d_parent->d_inode->i_ino, DT_DIR);
  483 + if (error < 0)
  484 + goto out;
  485 + default:
  486 + break;
  487 + }
  488 +
  489 + saved_file =
  490 + kmalloc(sizeof(struct vmufat_file_info), GFP_KERNEL);
  491 + if (!saved_file) {
  492 + error = -ENOMEM;
  493 + goto out;
  494 + }
  495 +
  496 + for (j = vmudetails->dir_bnum -
  497 + (index - 2) / VMU_DIR_ENTRIES_PER_BLOCK;
  498 + j > vmudetails->dir_bnum - vmudetails->dir_len; j--) {
  499 + brelse(bh);
  500 + bh = vmufat_sb_bread(sb, j);
  501 + if (!bh) {
  502 + error = -EIO;
  503 + goto finish;
  504 + }
  505 + for (k = (index - 2) % VMU_DIR_ENTRIES_PER_BLOCK;
  506 + k < VMU_DIR_ENTRIES_PER_BLOCK; k++) {
  507 + int pos, pos16;
  508 + pos = k * VMU_DIR_RECORD_LEN;
  509 + pos16 = k * VMU_DIR_RECORD_LEN16;
  510 + saved_file->ftype = bh->b_data[pos];
  511 + if (saved_file->ftype == 0)
  512 + goto finish;
  513 + saved_file->fblk =
  514 + le16_to_cpu(((u16 *) bh->b_data)[pos16 + 1]);
  515 + if (saved_file->fblk == 0)
  516 + saved_file->fblk = VMUFAT_ZEROBLOCK;
  517 + memcpy(saved_file->fname,
  518 + bh->b_data + pos + VMUFAT_NAME_OFFSET,
  519 + VMUFAT_NAMELEN);
  520 + filenamelen = strlen(saved_file->fname);
  521 + if (filenamelen > VMUFAT_NAMELEN)
  522 + filenamelen = VMUFAT_NAMELEN;
  523 + error = filldir(dirent, saved_file->fname, filenamelen,
  524 + index++, saved_file->fblk, DT_REG);
  525 + if (error < 0)
  526 + goto finish;
  527 + }
  528 + }
  529 +
  530 +finish:
  531 + filp->f_pos = index;
  532 + kfree(saved_file);
  533 + brelse(bh);
  534 +out:
  535 + return error;
  536 +}
  537 +
  538 +
  539 +int vmufat_list_blocks(struct inode *in)
  540 +{
  541 + struct vmufat_inode *vi;
  542 + struct super_block *sb;
  543 + long nextblock;
  544 + long ino;
  545 + struct memcard *vmudetails;
  546 + int error = -EINVAL;
  547 + struct list_head *iter, *iter2;
  548 + struct vmufat_block_list *vbl, *nvbl;
  549 + u16 fatdata;
  550 +
  551 + vi = VMUFAT_I(in);
  552 + if (!vi)
  553 + goto out;
  554 + sb = in->i_sb;
  555 + ino = in->i_ino;
  556 + vmudetails = sb->s_fs_info;
  557 + error = 0;
  558 + nextblock = ino;
  559 + if (nextblock == VMUFAT_ZEROBLOCK)
  560 + nextblock = 0;
  561 +
  562 + /* Delete any previous list of blocks */
  563 + list_for_each_safe(iter, iter2, &vi->blocks.b_list) {
  564 + vbl = list_entry(iter, struct vmufat_block_list, b_list);
  565 + list_del(iter);
  566 + kmem_cache_free(vmufat_blist_cachep, vbl);
  567 + }
  568 + vi->nblcks = 0;
  569 + do {
  570 + vbl = kmem_cache_alloc(vmufat_blist_cachep,
  571 + GFP_KERNEL);
  572 + if (!vbl) {
  573 + error = -ENOMEM;
  574 + goto unwind_out;
  575 + }
  576 + INIT_LIST_HEAD(&vbl->b_list);
  577 + vbl->bno = nextblock;
  578 + list_add_tail(&vbl->b_list, &vi->blocks.b_list);
  579 + vi->nblcks++;
  580 +
  581 + /* Find next block in the FAT - if there is one */
  582 + fatdata = vmufat_get_fat(sb, nextblock);
  583 + if (fatdata == VMUFAT_UNALLOCATED) {
  584 + printk(KERN_ERR "VMUFAT: FAT table appears to have"
  585 + " been corrupted.\n");
  586 + error = -EIO;
  587 + goto unwind_out;
  588 + }
  589 + if (fatdata == VMUFAT_FILE_END)
  590 + break;
  591 + nextblock = fatdata;
  592 + } while (1);
  593 +out:
  594 + return error;
  595 +
  596 +unwind_out:
  597 + list_for_each_entry_safe(vbl, nvbl, &vi->blocks.b_list, b_list) {
  598 + list_del_init(&vbl->b_list);
  599 + kmem_cache_free(vmufat_blist_cachep, vbl);
  600 + }
  601 + return error;
  602 +}
  603 +
  604 +static int vmufat_clean_fat(struct super_block *sb, int inum)
  605 +{
  606 + int error = 0;
  607 + u16 fatword, nextword;
  608 +
  609 + nextword = inum;
  610 + do {
  611 + fatword = vmufat_get_fat(sb, nextword);
  612 + if (fatword == VMUFAT_ERROR) {
  613 + error = -EIO;
  614 + break;
  615 + }
  616 + error = vmufat_set_fat(sb, nextword, VMUFAT_UNALLOCATED);
  617 + if (error)
  618 + break;
  619 + if (fatword == VMUFAT_FILE_END)
  620 + break;
  621 + nextword = fatword;
  622 + } while (1);
  623 +
  624 + return error;
  625 +}
  626 +
  627 +/*
  628 + * Delete inode by marking space as free in FAT
  629 + * no need to waste time and effort by actually
  630 + * wiping underlying data on media
  631 + */
  632 +static void vmufat_remove_inode(struct inode *in)
  633 +{
  634 + struct buffer_head *bh = NULL, *bh_old = NULL;
  635 + struct super_block *sb;
  636 + struct memcard *vmudetails;
  637 + int i, j, k, l, startpt, found = 0;
  638 +
  639 + if (in->i_ino == VMUFAT_ZEROBLOCK)
  640 + in->i_ino = 0;
  641 + sb = in->i_sb;
  642 + vmudetails = sb->s_fs_info;
  643 + if (in->i_ino > vmudetails->fat_len * sb->s_blocksize / 2) {
  644 + printk(KERN_WARNING "VMUFAT: attempting to delete"
  645 + "inode beyond device size");
  646 + goto ret;
  647 + }
  648 +
  649 + mutex_lock(&vmudetails->mutex);
  650 + if (vmufat_clean_fat(sb, in->i_ino)) {
  651 + mutex_unlock(&vmudetails->mutex);
  652 + goto failure;
  653 + }
  654 +
  655 + /* Now clean the directory entry
  656 + * Have to wander through this
  657 + * to find the appropriate entry */
  658 + for (i = vmudetails->dir_bnum;
  659 + i > vmudetails->dir_bnum - vmudetails->dir_len; i--) {
  660 + brelse(bh);
  661 + bh = vmufat_sb_bread(sb, i);
  662 + if (!bh) {
  663 + mutex_unlock(&vmudetails->mutex);
  664 + goto failure;
  665 + }
  666 + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++) {
  667 + if (bh->b_data[j * VMU_DIR_RECORD_LEN] == 0) {
  668 + mutex_unlock(&vmudetails->mutex);
  669 + goto failure;
  670 + }
  671 + if (le16_to_cpu(((u16 *) bh->b_data)
  672 + [j * VMU_DIR_RECORD_LEN16 +
  673 + VMUFAT_FIRSTBLOCK_OFFSET16]) == in->i_ino) {
  674 + found = 1;
  675 + goto found;
  676 + }
  677 + }
  678 + }
  679 +found:
  680 + if (found == 0) {
  681 + mutex_unlock(&vmudetails->mutex);
  682 + goto failure;
  683 + }
  684 +
  685 + /* Found directory entry - so NULL it now */
  686 + for (k = 0; k < VMU_DIR_RECORD_LEN; k++)
  687 + bh->b_data[j * VMU_DIR_RECORD_LEN + k] = 0;
  688 + mark_buffer_dirty(bh);
  689 + /* Patch up directory, by moving up last file */
  690 + found = 0;
  691 + startpt = j + 1;
  692 + for (l = i; l > vmudetails->dir_bnum - vmudetails->dir_len; l--) {
  693 + bh_old = vmufat_sb_bread(sb, l);
  694 + if (!bh_old) {
  695 + mutex_unlock(&vmudetails->mutex);
  696 + goto failure;
  697 + }
  698 + for (k = startpt; k < VMU_DIR_ENTRIES_PER_BLOCK; k++) {
  699 + if (bh_old->b_data[k * VMU_DIR_RECORD_LEN] == 0) {
  700 + found = 1;
  701 + brelse(bh_old);
  702 + goto lastdirfound;
  703 + }
  704 + }
  705 + startpt = 0;
  706 + brelse(bh_old);
  707 + }
  708 +lastdirfound:
  709 + if (found == 0) { /* full directory */
  710 + l = vmudetails->dir_bnum - vmudetails->dir_len + 1;
  711 + k = VMU_DIR_ENTRIES_PER_BLOCK;
  712 + } else if (l == i && k == j + 1) /* deleted entry was last in dir */
  713 + goto finish;
  714 + else if (k == 0) {
  715 + l = l + 1;
  716 + k = VMU_DIR_ENTRIES_PER_BLOCK;
  717 + if (l == i && k == j + 1)
  718 + goto finish;
  719 + }
  720 + /* fill gap first then wipe out old entry */
  721 + bh_old = vmufat_sb_bread(sb, l);
  722 + if (!bh_old) {
  723 + mutex_unlock(&vmudetails->mutex);
  724 + brelse(bh);
  725 + goto failure;
  726 + }
  727 + for (i = 0; i < VMU_DIR_RECORD_LEN; i++) {
  728 + bh->b_data[j * VMU_DIR_RECORD_LEN + i] =
  729 + bh_old->b_data[(k - 1) * VMU_DIR_RECORD_LEN + i];
  730 + bh_old->b_data[(k - 1) * VMU_DIR_RECORD_LEN + i] = 0;
  731 + }
  732 + mark_buffer_dirty(bh_old);
  733 + mark_buffer_dirty(bh);
  734 + brelse(bh_old);
  735 +
  736 +finish:
  737 + mutex_unlock(&vmudetails->mutex);
  738 + brelse(bh);
  739 + return;
  740 +
  741 +failure:
  742 + printk(KERN_WARNING "VMUFAT: Failure to read volume,"
  743 + " could not delete inode - filesystem may be damaged\n");
  744 +ret:
  745 + return;
  746 +}
  747 +
  748 +/*
  749 + * vmufat_unlink - delete a file pointed to
  750 + * by the dentry (only one directory in a
  751 + * vmufat fs so safe to ignore the inode
  752 + * supplied here
  753 + */
  754 +static int vmufat_unlink(struct inode *dir, struct dentry *dentry)
  755 +{
  756 + struct inode *in;
  757 + in = dentry->d_inode;
  758 + vmufat_remove_inode(in);
  759 + return 0;
  760 +}
  761 +
  762 +static int vmufat_get_block(struct inode *inode, sector_t iblock,
  763 + struct buffer_head *bh_result, int create)
  764 +{
  765 + struct vmufat_inode *vin;
  766 + struct vmufat_block_list *vlist, *vblk;
  767 + struct super_block *sb;
  768 + struct memcard *vmudetails;
  769 + int cural;
  770 + int finblk, nxtblk, exeblk;
  771 + struct list_head *iter;
  772 + sector_t cntdwn = iblock;
  773 + sector_t phys;
  774 + int error = -EINVAL;
  775 +
  776 + vin = VMUFAT_I(inode);
  777 + if (!vin || !(&vin->blocks) || vin->nblcks <= 0)
  778 + goto out;
  779 + vlist = &vin->blocks;
  780 + sb = inode->i_sb;
  781 + vmudetails = sb->s_fs_info;
  782 +
  783 + if (iblock < vin->nblcks) {
  784 + /* block is already here so read it into the buffer head */
  785 + list_for_each(iter, &vlist->b_list) {
  786 + if (cntdwn-- == 0)
  787 + break;
  788 + }
  789 + vblk = list_entry(iter, struct vmufat_block_list, b_list);
  790 + clear_buffer_new(bh_result);
  791 + error = 0;
  792 + phys = vblk->bno;
  793 + goto got_it;
  794 + }
  795 + if (!create)
  796 + goto out;
  797 + /*
  798 + * check not looking for a block too far
  799 + * beyond the end of the existing file
  800 + */
  801 + if (iblock > vin->nblcks)
  802 + goto out;
  803 +
  804 + /* if looking for a block that is not current - allocate it */
  805 + cural = vin->nblcks;
  806 + list_for_each(iter, &vlist->b_list) {
  807 + if (cural-- == 1)
  808 + break;
  809 + }
  810 + vblk = list_entry(iter, struct vmufat_block_list, b_list);
  811 + finblk = vblk->bno;
  812 +
  813 + mutex_lock(&vmudetails->mutex);
  814 + /* Exec files have to be linear */
  815 + if (inode->i_ino == 0) {
  816 + exeblk = vmufat_get_fat(sb, finblk + 1);
  817 + if (exeblk != VMUFAT_UNALLOCATED) {
  818 + mutex_unlock(&vmudetails->mutex);
  819 + printk(KERN_WARNING "VMUFAT: Cannot allocate linear "
  820 + "space needed for executible\n");
  821 + error = -ENOSPC;
  822 + goto out;
  823 + }
  824 + nxtblk = finblk + 1;
  825 + } else {
  826 + nxtblk = vmufat_find_free(sb);
  827 + if (nxtblk < 0) {
  828 + mutex_unlock(&vmudetails->mutex);
  829 + error = nxtblk;
  830 + goto out;
  831 + }
  832 + }
  833 + error = vmufat_set_fat(sb, finblk, nxtblk);
  834 + if (error) {
  835 + mutex_unlock(&vmudetails->mutex);
  836 + goto out;
  837 + }
  838 + error = vmufat_set_fat(sb, nxtblk, VMUFAT_FILE_END);
  839 + mutex_unlock(&vmudetails->mutex);
  840 + if (error)
  841 + goto out;
  842 + error = vmufat_list_blocks(inode);
  843 + mark_inode_dirty(inode);
  844 + if (error)
  845 + goto out;
  846 + set_buffer_new(bh_result);
  847 + phys = nxtblk;
  848 + error = 0;
  849 +got_it:
  850 + map_bh(bh_result, sb, phys);
  851 +out:
  852 + return error;
  853 +}
  854 +
  855 +static int vmufat_writepage(struct page *page, struct writeback_control *wbc)
  856 +{
  857 + return block_write_full_page(page, vmufat_get_block, wbc);
  858 +}
  859 +
  860 +static int vmufat_write_begin(struct file *filp, struct address_space *mapping,
  861 + loff_t pos, unsigned len, unsigned flags,
  862 + struct page **pagep, void **fsdata)
  863 +{
  864 + *pagep = NULL;
  865 + return block_write_begin(mapping, pos, len, flags, pagep,
  866 + vmufat_get_block);
  867 +}
  868 +
  869 +static int vmufat_readpage(struct file *file, struct page *page)
  870 +{
  871 + return block_read_full_page(page, vmufat_get_block);
  872 +}
  873 +
  874 +
  875 +const struct address_space_operations
  876 + vmufat_address_space_operations = {
  877 + .readpage = vmufat_readpage,
  878 + .writepage = vmufat_writepage,
  879 + .write_begin = vmufat_write_begin,
  880 + .write_end = generic_write_end,
  881 +};
  882 +
  883 +const struct inode_operations vmufat_inode_operations = {
  884 + .lookup = vmufat_inode_lookup,
  885 + .create = vmufat_inode_create,
  886 + .unlink = vmufat_unlink,
  887 +};
  888 +
  889 +const struct file_operations vmufat_file_dir_operations = {
  890 + .owner = THIS_MODULE,
  891 + .read = generic_read_dir,
  892 + .readdir = vmufat_readdir,
  893 + .fsync = generic_file_fsync,
  894 +};
  895 +
  896 +const struct file_operations vmufat_file_operations = {
  897 + .llseek = generic_file_llseek,
  898 + .read = do_sync_read,
  899 + .write = do_sync_write,
  900 + .aio_read = generic_file_aio_read,
  901 + .aio_write = generic_file_aio_write,
  902 + .fsync = generic_file_fsync,
  903 +};
522 fs/vmufat/super.c
... ... @@ -0,0 +1,522 @@
  1 +/*
  2 + * VMUFAT file system
  3 + *
  4 + * Copyright (C) 2002 - 2012 Adrian McMenamin
  5 + * Copyright (C) 2002 Paul Mundt
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; either version 2 of the License, or
  10 + * (at your option) any later version.
  11 + *
  12 + * This program is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License
  18 + * along with this program; if not, write to the Free Software
  19 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20 + */
  21 +
  22 +#include <linux/fs.h>
  23 +#include <linux/bcd.h>
  24 +#include <linux/rtc.h>
  25 +#include <linux/slab.h>
  26 +#include <linux/sched.h>
  27 +#include <linux/magic.h>
  28 +#include <linux/device.h>
  29 +#include <linux/module.h>
  30 +#include <linux/statfs.h>
  31 +#include <linux/buffer_head.h>
  32 +#include "vmufat.h"
  33 +
  34 +static struct kmem_cache *vmufat_inode_cachep;
  35 +static const struct super_operations vmufat_super_operations;
  36 +extern int *day_n;
  37 +extern struct kmem_cache *vmufat_blist_cachep;
  38 +extern const struct inode_operations vmufat_inode_operations;
  39 +extern const struct file_operations vmufat_file_operations;
  40 +extern const struct address_space_operations vmufat_address_space_operations;
  41 +extern const struct file_operations vmufat_file_dir_operations;
  42 +
  43 +static long vmufat_get_date(struct buffer_head *bh, int offset)
  44 +{
  45 + int century, year, month, day, hour, minute, second;
  46 + century = bcd2bin(bh->b_data[offset++]);
  47 + year = bcd2bin(bh->b_data[offset++]);
  48 + month = bcd2bin(bh->b_data[offset++]);
  49 + day = bcd2bin(bh->b_data[offset++]);
  50 + hour = bcd2bin(bh->b_data[offset++]);
  51 + minute = bcd2bin(bh->b_data[offset++]);
  52 + second = bcd2bin(bh->b_data[offset]);
  53 +
  54 + return mktime(century * 100 + year, month, day, hour, minute,
  55 + second);
  56 +}
  57 +
  58 +static struct inode *vmufat_alloc_inode(struct super_block *sb)
  59 +{
  60 + struct vmufat_inode *vi = kmem_cache_alloc(vmufat_inode_cachep,
  61 + GFP_KERNEL);
  62 + if (!vi)
  63 + return NULL;
  64 +
  65 + INIT_LIST_HEAD(&vi->blocks.b_list);
  66 + return &vi->vfs_inode;
  67 +}
  68 +
  69 +static void vmufat_destroy_inode(struct inode *in)
  70 +{
  71 + struct vmufat_inode *vi;
  72 + struct vmufat_block_list *vb;
  73 + struct list_head *iter, *iter2;
  74 + vi = VMUFAT_I(in);
  75 + if (!vi)
  76 + return;
  77 +
  78 + list_for_each_safe(iter, iter2, &vi->blocks.b_list) {
  79 + vb = list_entry(iter, struct vmufat_block_list, b_list);
  80 + list_del(iter);
  81 + kmem_cache_free(vmufat_blist_cachep, vb);
  82 + }
  83 + kmem_cache_free(vmufat_inode_cachep, vi);
  84 +}
  85 +
  86 +struct inode *vmufat_get_inode(struct super_block *sb, long ino)
  87 +{
  88 + struct buffer_head *bh = NULL;
  89 + int error = 0, i, j, found = 0;
  90 + int offsetindir;
  91 + struct inode *inode;
  92 + struct memcard *vmudetails;
  93 + long superblock_bno;
  94 +
  95 + vmudetails = sb->s_fs_info;
  96 + inode = iget_locked(sb, ino);
  97 + if (!inode) {
  98 + error = -EIO;
  99 + goto reterror;
  100 + }
  101 + superblock_bno = vmudetails->sb_bnum;
  102 +
  103 + if (inode->i_state & I_NEW) {
  104 + inode->i_uid = current_fsuid();
  105 + inode->i_gid = current_fsgid();
  106 + inode->i_mode &= ~S_IFMT;
  107 + if (inode->i_ino == superblock_bno) {
  108 + bh = vmufat_sb_bread(sb, inode->i_ino);
  109 + if (!bh) {
  110 + error = -EIO;
  111 + goto failed;
  112 + }
  113 + inode->i_ctime.tv_sec = inode->i_mtime.tv_sec =
  114 + vmufat_get_date(bh, VMUFAT_SB_DATEOFFSET);
  115 +
  116 + /* Mark as a directory */
  117 + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
  118 + inode->i_op = &vmufat_inode_operations;
  119 + inode->i_fop = &vmufat_file_dir_operations;
  120 + } else {
  121 + /* Mark file as regular type */
  122 + inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
  123 +
  124 + /* Scan through the directory to find matching file */
  125 + for (i = vmudetails->dir_bnum;
  126 + i > vmudetails->dir_bnum - vmudetails->dir_len;
  127 + i--) {
  128 + brelse(bh);
  129 + bh = vmufat_sb_bread(sb, i);
  130 + if (!bh) {
  131 + error = -EIO;
  132 + goto failed;
  133 + }
  134 + for (j = 0; j < VMU_DIR_ENTRIES_PER_BLOCK; j++)
  135 + {
  136 + if (bh->b_data[j * VMU_DIR_RECORD_LEN]
  137 + == 0)
  138 + goto notfound;
  139 + if (le16_to_cpu(((u16 *) bh->b_data)
  140 + [j * VMU_DIR_RECORD_LEN16 +
  141 + VMUFAT_FIRSTBLOCK_OFFSET16]) == ino) {
  142 + found = 1;
  143 + goto found;
  144 + }
  145 + }
  146 + }
  147 +notfound:
  148 + error = -ENOENT;
  149 + goto failed;
  150 +found:
  151 + /* identified the correct directory entry */
  152 + offsetindir = j * VMU_DIR_RECORD_LEN;
  153 + inode->i_ctime.tv_sec = inode->i_mtime.tv_sec =
  154 + vmufat_get_date(bh,
  155 + offsetindir + VMUFAT_FILE_DATEOFFSET);
  156 +
  157 + /* Execute if a game, write if not copy protected */
  158 + inode->i_mode &= ~(S_IWUGO | S_IXUGO);
  159 + inode->i_mode |= S_IRUGO;
  160 +
  161 + /* Mode - is it write protected? */
  162 + if ((((u8 *) bh->b_data)[0x01 + offsetindir] ==
  163 + 0x00) & ~(sb->s_flags & MS_RDONLY))
  164 + inode->i_mode |= S_IWUSR;
  165 + /* Is file executible - ie a game */
  166 + if ((((u8 *) bh->b_data)[offsetindir] ==
  167 + 0xcc) & ~(sb->s_flags & MS_NOEXEC))
  168 + inode->i_mode |= S_IXUSR;
  169 +
  170 + inode->i_fop = &vmufat_file_operations;
  171 +
  172 + inode->i_blocks =
  173 + le16_to_cpu(((u16 *) bh->b_data)
  174 + [offsetindir / 2 + 0x0C]);
  175 + inode->i_size = inode->i_blocks * sb->s_blocksize;
  176 +
  177 + inode->i_mapping->a_ops =
  178 + &vmufat_address_space_operations;
  179 + inode->i_op = &vmufat_inode_operations;
  180 + inode->i_fop = &vmufat_file_operations;
  181 + error = vmufat_list_blocks(inode);
  182 + if (error)
  183 + goto failed;
  184 + }
  185 + inode->i_atime = CURRENT_TIME;
  186 + unlock_new_inode(inode);
  187 + }
  188 + brelse(bh);
  189 + return inode;
  190 +
  191 +failed:
  192 + iget_failed(inode);
  193 +reterror:
  194 + brelse(bh);
  195 + return ERR_PTR(error);
  196 +}
  197 +
  198