OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/tags/LINUX_2_4_26_OR32/linux/linux-2.4/fs/fat
    from Rev 1279 to Rev 1765
    Reverse comparison

Rev 1279 → Rev 1765

/cvf.c
0,0 → 1,177
/*
* CVF extensions for fat-based filesystems
*
* written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
*
* please do not remove the next line, dmsdos needs it for verifying patches
* CVF-FAT-VERSION-ID: 1.2.0
*
*/
#include<linux/sched.h>
#include<linux/fs.h>
#include<linux/msdos_fs.h>
#include<linux/msdos_fs_sb.h>
#include<linux/string.h>
#include<linux/fat_cvf.h>
#include<linux/config.h>
#ifdef CONFIG_KMOD
#include<linux/kmod.h>
#endif
 
#define MAX_CVF_FORMATS 3
 
struct buffer_head *default_fat_bread(struct super_block *,int);
struct buffer_head *default_fat_getblk(struct super_block *, int);
void default_fat_brelse(struct super_block *, struct buffer_head *);
void default_fat_mark_buffer_dirty (struct super_block *, struct buffer_head *);
void default_fat_set_uptodate (struct super_block *, struct buffer_head *,int);
int default_fat_is_uptodate(struct super_block *, struct buffer_head *);
int default_fat_access(struct super_block *sb,int nr,int new_value);
void default_fat_ll_rw_block (struct super_block *sb, int opr, int nbreq,
struct buffer_head *bh[32]);
int default_fat_bmap(struct inode *inode,int block);
ssize_t default_fat_file_write(struct file *filp, const char *buf,
size_t count, loff_t *ppos);
 
struct cvf_format default_cvf = {
cvf_version: 0, /* version - who cares? */
cvf_version_text: "plain",
flags: 0, /* flags - who cares? */
cvf_bread: default_fat_bread,
cvf_getblk: default_fat_getblk,
cvf_brelse: default_fat_brelse,
cvf_mark_buffer_dirty: default_fat_mark_buffer_dirty,
cvf_set_uptodate: default_fat_set_uptodate,
cvf_is_uptodate: default_fat_is_uptodate,
cvf_ll_rw_block: default_fat_ll_rw_block,
fat_access: default_fat_access,
cvf_bmap: default_fat_bmap,
cvf_file_read: generic_file_read,
cvf_file_write: default_fat_file_write,
};
 
struct cvf_format *cvf_formats[MAX_CVF_FORMATS];
int cvf_format_use_count[MAX_CVF_FORMATS];
 
int register_cvf_format(struct cvf_format*cvf_format)
{ int i,j;
 
for(i=0;i<MAX_CVF_FORMATS;++i)
{ if(cvf_formats[i]==NULL)
{ /* free slot found, now check version */
for(j=0;j<MAX_CVF_FORMATS;++j)
{ if(cvf_formats[j])
{ if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
{ printk("register_cvf_format: version %d already registered\n",
cvf_format->cvf_version);
return -1;
}
}
}
cvf_formats[i]=cvf_format;
cvf_format_use_count[i]=0;
printk("CVF format %s (version id %d) successfully registered.\n",
cvf_format->cvf_version_text,cvf_format->cvf_version);
return 0;
}
}
printk("register_cvf_format: too many formats\n");
return -1;
}
 
int unregister_cvf_format(struct cvf_format*cvf_format)
{ int i;
 
for(i=0;i<MAX_CVF_FORMATS;++i)
{ if(cvf_formats[i])
{ if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
{ if(cvf_format_use_count[i])
{ printk("unregister_cvf_format: format %d in use, cannot remove!\n",
cvf_formats[i]->cvf_version);
return -1;
}
printk("CVF format %s (version id %d) successfully unregistered.\n",
cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
cvf_formats[i]=NULL;
return 0;
}
}
}
printk("unregister_cvf_format: format %d is not registered\n",
cvf_format->cvf_version);
return -1;
}
 
void dec_cvf_format_use_count_by_version(int version)
{ int i;
 
for(i=0;i<MAX_CVF_FORMATS;++i)
{ if(cvf_formats[i])
{ if(cvf_formats[i]->cvf_version==version)
{ --cvf_format_use_count[i];
if(cvf_format_use_count[i]<0)
{ cvf_format_use_count[i]=0;
printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
}
return;
}
}
}
printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
version);
}
 
int detect_cvf(struct super_block*sb,char*force)
{ int i;
int found=0;
int found_i=-1;
 
if(force)
if(strcmp(force,"autoload")==0)
{
#ifdef CONFIG_KMOD
request_module("cvf_autoload");
force=NULL;
#else
printk("cannot autoload CVF modules: kmod support is not compiled into kernel\n");
return -1;
#endif
}
#ifdef CONFIG_KMOD
if(force)
if(*force)
request_module(force);
#endif
 
if(force)
{ if(*force)
{ for(i=0;i<MAX_CVF_FORMATS;++i)
{ if(cvf_formats[i])
{ if(!strcmp(cvf_formats[i]->cvf_version_text,force))
return i;
}
}
printk("CVF format %s unknown (module not loaded?)\n",force);
return -1;
}
}
 
for(i=0;i<MAX_CVF_FORMATS;++i)
{ if(cvf_formats[i])
{ if(cvf_formats[i]->detect_cvf(sb))
{ ++found;
found_i=i;
}
}
}
if(found==1)return found_i;
if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
return -1;
}
/dir.c
0,0 → 1,798
/*
* linux/fs/fat/dir.c
*
* directory handling functions for fat-based filesystems
*
* Written 1992,1993 by Werner Almesberger
*
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
*
* VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
* Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
* Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
* Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
*/
 
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/ioctl.h>
#include <linux/dirent.h>
#include <linux/mm.h>
#include <linux/ctype.h>
 
#include <asm/uaccess.h>
 
#define PRINTK(X)
 
struct file_operations fat_dir_operations = {
read: generic_read_dir,
readdir: fat_readdir,
ioctl: fat_dir_ioctl,
fsync: file_fsync,
};
 
/*
* Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
* If uni_xlate is enabled and we can't get a 1:1 conversion, use a
* colon as an escape character since it is normally invalid on the vfat
* filesystem. The following four characters are the hexadecimal digits
* of Unicode value. This lets us do a full dump and restore of Unicode
* filenames. We could get into some trouble with long Unicode names,
* but ignore that right now.
* Ahem... Stack smashing in ring 0 isn't fun. Fixed.
*/
static int
uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
struct nls_table *nls)
{
wchar_t *ip, ec;
unsigned char *op, nc;
int charlen;
int k;
 
ip = uni;
op = ascii;
 
while (*ip) {
ec = *ip++;
if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {
op += charlen;
} else {
if (uni_xlate == 1) {
*op = ':';
for (k = 4; k > 0; k--) {
nc = ec & 0xF;
op[k] = nc > 9 ? nc + ('a' - 10)
: nc + '0';
ec >>= 4;
}
op += 5;
} else {
*op++ = '?';
}
}
/* We have some slack there, so it's OK */
if (op>ascii+256) {
op = ascii + 256;
break;
}
}
*op = 0;
return (op - ascii);
}
 
#if 0
static void dump_de(struct msdos_dir_entry *de)
{
int i;
unsigned char *p = (unsigned char *) de;
printk("[");
 
for (i = 0; i < 32; i++, p++) {
printk("%02x ", *p);
}
printk("]\n");
}
#endif
 
static inline unsigned char
fat_tolower(struct nls_table *t, unsigned char c)
{
unsigned char nc = t->charset2lower[c];
 
return nc ? nc : c;
}
 
static inline int
fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
{
int charlen;
 
charlen = t->char2uni(c, clen, uni);
if (charlen < 0) {
*uni = 0x003f; /* a question mark */
charlen = 1;
}
return charlen;
}
 
static inline int
fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
{
int charlen;
wchar_t wc;
 
charlen = t->char2uni(c, clen, &wc);
if (charlen < 0) {
*uni = 0x003f; /* a question mark */
charlen = 1;
} else if (charlen <= 1) {
unsigned char nc = t->charset2lower[*c];
if (!nc)
nc = *c;
if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {
*uni = 0x003f; /* a question mark */
charlen = 1;
}
} else
*uni = wc;
return charlen;
}
 
static int
fat_strnicmp(struct nls_table *t, const unsigned char *s1,
const unsigned char *s2, int len)
{
while(len--)
if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++))
return 1;
 
return 0;
}
 
static inline int
fat_shortname2uni(struct nls_table *nls, char *buf, int buf_size,
wchar_t *uni_buf, unsigned short opt, int lower)
{
int len = 0;
 
if (opt & VFAT_SFN_DISPLAY_LOWER)
len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
else if (opt & VFAT_SFN_DISPLAY_WIN95)
len = fat_short2uni(nls, buf, buf_size, uni_buf);
else if (opt & VFAT_SFN_DISPLAY_WINNT) {
if (lower)
len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
else
len = fat_short2uni(nls, buf, buf_size, uni_buf);
} else
len = fat_short2uni(nls, buf, buf_size, uni_buf);
 
return len;
}
 
/*
* Return values: negative -> error, 0 -> not found, positive -> found,
* value is the total amount of slots, including the shortname entry.
*/
int fat_search_long(struct inode *inode, const char *name, int name_len,
int anycase, loff_t *spos, loff_t *lpos)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
wchar_t bufuname[14];
unsigned char xlate_len, long_slots;
wchar_t *unicode = NULL;
char work[8], bufname[260]; /* 256 + 4 */
int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
int utf8 = MSDOS_SB(sb)->options.utf8;
unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
int chl, i, j, last_u, res = 0;
loff_t i_pos, cpos = 0;
 
while(1) {
if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
goto EODir;
parse_record:
long_slots = 0;
if (de->name[0] == (__s8) DELETED_FLAG)
continue;
if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
continue;
if (de->attr != ATTR_EXT && IS_FREE(de->name))
continue;
if (de->attr == ATTR_EXT) {
struct msdos_dir_slot *ds;
unsigned char id;
unsigned char slot;
unsigned char slots;
unsigned char sum;
unsigned char alias_checksum;
 
if (!unicode) {
unicode = (wchar_t *)
__get_free_page(GFP_KERNEL);
if (!unicode) {
fat_brelse(sb, bh);
return -ENOMEM;
}
}
parse_long:
slots = 0;
ds = (struct msdos_dir_slot *) de;
id = ds->id;
if (!(id & 0x40))
continue;
slots = id & ~0x40;
if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
continue;
long_slots = slots;
alias_checksum = ds->alias_checksum;
 
slot = slots;
while (1) {
int offset;
 
slot--;
offset = slot * 13;
fat16_towchar(unicode + offset, ds->name0_4, 5);
fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
 
if (ds->id & 0x40) {
unicode[offset + 13] = 0;
}
if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos)<0)
goto EODir;
if (slot == 0)
break;
ds = (struct msdos_dir_slot *) de;
if (ds->attr != ATTR_EXT)
goto parse_record;
if ((ds->id & ~0x40) != slot)
goto parse_long;
if (ds->alias_checksum != alias_checksum)
goto parse_long;
}
if (de->name[0] == (__s8) DELETED_FLAG)
continue;
if (de->attr == ATTR_EXT)
goto parse_long;
if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
continue;
for (sum = 0, i = 0; i < 11; i++)
sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
if (sum != alias_checksum)
long_slots = 0;
}
 
for (i = 0; i < 8; i++) {
/* see namei.c, msdos_format_name */
if (de->name[i] == 0x05)
work[i] = 0xE5;
else
work[i] = de->name[i];
}
for (i = 0, j = 0, last_u = 0; i < 8;) {
if (!work[i]) break;
chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_BASE);
if (chl <= 1) {
if (work[i] != ' ')
last_u = j;
} else {
last_u = j;
}
i += chl;
}
j = last_u;
fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
for (i = 0; i < 3;) {
if (!de->ext[i]) break;
chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i,
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_EXT);
if (chl <= 1) {
if (de->ext[i] != ' ')
last_u = j;
} else {
last_u = j;
}
i += chl;
}
if (!last_u)
continue;
 
bufuname[last_u] = 0x0000;
xlate_len = utf8
?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
:uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
if (xlate_len == name_len)
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
(anycase && !fat_strnicmp(nls_io, name, bufname,
xlate_len)))
goto Found;
 
if (long_slots) {
xlate_len = utf8
?utf8_wcstombs(bufname, unicode, sizeof(bufname))
:uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
if (xlate_len != name_len)
continue;
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
(anycase && !fat_strnicmp(nls_io, name, bufname,
xlate_len)))
goto Found;
}
}
 
Found:
res = long_slots + 1;
*spos = cpos - sizeof(struct msdos_dir_entry);
*lpos = cpos - res*sizeof(struct msdos_dir_entry);
EODir:
fat_brelse(sb, bh);
if (unicode) {
free_page((unsigned long) unicode);
}
return res;
}
 
static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
filldir_t filldir, int shortnames, int both)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
wchar_t bufuname[14];
unsigned char long_slots;
wchar_t *unicode = NULL;
char c, work[8], bufname[56], *ptname = bufname;
unsigned long lpos, dummy, *furrfu = &lpos;
int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
int isvfat = MSDOS_SB(sb)->options.isvfat;
int utf8 = MSDOS_SB(sb)->options.utf8;
int nocase = MSDOS_SB(sb)->options.nocase;
unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
unsigned long inum;
int chi, chl, i, i2, j, last, last_u, dotoffset = 0;
loff_t i_pos, cpos;
 
cpos = filp->f_pos;
/* Fake . and .. for the root directory. */
if (inode->i_ino == MSDOS_ROOT_INO) {
while (cpos < 2) {
if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0)
return 0;
cpos++;
filp->f_pos++;
}
if (cpos == 2) {
dummy = 2;
furrfu = &dummy;
cpos = 0;
}
}
if (cpos & (sizeof(struct msdos_dir_entry)-1))
return -ENOENT;
 
bh = NULL;
GetNew:
long_slots = 0;
if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
goto EODir;
/* Check for long filename entry */
if (isvfat) {
if (de->name[0] == (__s8) DELETED_FLAG)
goto RecEnd;
if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
goto RecEnd;
if (de->attr != ATTR_EXT && IS_FREE(de->name))
goto RecEnd;
} else {
if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
goto RecEnd;
}
 
if (isvfat && de->attr == ATTR_EXT) {
struct msdos_dir_slot *ds;
unsigned char id;
unsigned char slot;
unsigned char slots;
unsigned char sum;
unsigned char alias_checksum;
 
if (!unicode) {
unicode = (wchar_t *)
__get_free_page(GFP_KERNEL);
if (!unicode) {
filp->f_pos = cpos;
fat_brelse(sb, bh);
return -ENOMEM;
}
}
ParseLong:
slots = 0;
ds = (struct msdos_dir_slot *) de;
id = ds->id;
if (!(id & 0x40))
goto RecEnd;
slots = id & ~0x40;
if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
goto RecEnd;
long_slots = slots;
alias_checksum = ds->alias_checksum;
 
slot = slots;
while (1) {
int offset;
 
slot--;
offset = slot * 13;
fat16_towchar(unicode + offset, ds->name0_4, 5);
fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
 
if (ds->id & 0x40) {
unicode[offset + 13] = 0;
}
if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
goto EODir;
if (slot == 0)
break;
ds = (struct msdos_dir_slot *) de;
if (ds->attr != ATTR_EXT)
goto RecEnd; /* XXX */
if ((ds->id & ~0x40) != slot)
goto ParseLong;
if (ds->alias_checksum != alias_checksum)
goto ParseLong;
}
if (de->name[0] == (__s8) DELETED_FLAG)
goto RecEnd;
if (de->attr == ATTR_EXT)
goto ParseLong;
if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
goto RecEnd;
for (sum = 0, i = 0; i < 11; i++)
sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
if (sum != alias_checksum)
long_slots = 0;
}
 
if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
*ptname++ = '.';
dotoffset = 1;
}
 
for (i = 0; i < 8; i++) {
/* see namei.c, msdos_format_name */
if (de->name[i] == 0x05)
work[i] = 0xE5;
else
work[i] = de->name[i];
}
for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
if (!(c = work[i])) break;
chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_BASE);
if (chl <= 1) {
ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
if (c != ' ') {
last = i;
last_u = j;
}
} else {
last_u = j;
for (chi = 0; chi < chl && i < 8; chi++) {
ptname[i] = work[i];
i++; last = i;
}
}
}
i = last;
j = last_u;
fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
ptname[i++] = '.';
for (i2 = 0; i2 < 3;) {
if (!(c = de->ext[i2])) break;
chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2,
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_EXT);
if (chl <= 1) {
i2++;
ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
if (c != ' ') {
last = i;
last_u = j;
}
} else {
last_u = j;
for (chi = 0; chi < chl && i2 < 3; chi++) {
ptname[i++] = de->ext[i2++];
last = i;
}
}
}
if (!last)
goto RecEnd;
 
i = last + dotoffset;
j = last_u;
 
lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
if (!memcmp(de->name,MSDOS_DOT,11))
inum = inode->i_ino;
else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
/* inum = fat_parent_ino(inode,0); */
inum = filp->f_dentry->d_parent->d_inode->i_ino;
} else {
struct inode *tmp = fat_iget(sb, i_pos);
if (tmp) {
inum = tmp->i_ino;
iput(tmp);
} else
inum = iunique(sb, MSDOS_ROOT_INO);
}
 
if (isvfat) {
bufuname[j] = 0x0000;
i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
: uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
}
 
if (!long_slots||shortnames) {
if (both)
bufname[i] = '\0';
if (filldir(dirent, bufname, i, *furrfu, inum,
(de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
goto FillFailed;
} else {
char longname[275];
int long_len = utf8
? utf8_wcstombs(longname, unicode, sizeof(longname))
: uni16_to_x8(longname, unicode, uni_xlate,
nls_io);
if (both) {
memcpy(&longname[long_len+1], bufname, i);
long_len += i;
}
if (filldir(dirent, longname, long_len, *furrfu, inum,
(de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
goto FillFailed;
}
 
RecEnd:
furrfu = &lpos;
filp->f_pos = cpos;
goto GetNew;
EODir:
filp->f_pos = cpos;
FillFailed:
if (bh)
fat_brelse(sb, bh);
if (unicode) {
free_page((unsigned long) unicode);
}
return 0;
}
 
int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
}
 
static int vfat_ioctl_fill(
void * buf,
const char * name,
int name_len,
loff_t offset,
ino_t ino,
unsigned int d_type)
{
struct dirent *d1 = (struct dirent *)buf;
struct dirent *d2 = d1 + 1;
int len, slen;
int dotdir;
 
get_user(len, &d1->d_reclen);
if (len != 0) {
return -1;
}
 
if ((name_len == 1 && name[0] == '.') ||
(name_len == 2 && name[0] == '.' && name[1] == '.')) {
dotdir = 1;
len = name_len;
} else {
dotdir = 0;
len = strlen(name);
}
if (len != name_len) {
copy_to_user(d2->d_name, name, len);
put_user(0, d2->d_name + len);
put_user(len, &d2->d_reclen);
put_user(ino, &d2->d_ino);
put_user(offset, &d2->d_off);
slen = name_len - len;
copy_to_user(d1->d_name, name+len+1, slen);
put_user(0, d1->d_name+slen);
put_user(slen, &d1->d_reclen);
} else {
put_user(0, d2->d_name);
put_user(0, &d2->d_reclen);
copy_to_user(d1->d_name, name, len);
put_user(0, d1->d_name+len);
put_user(len, &d1->d_reclen);
}
PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
d1, d2, len, name_len));
 
return 0;
}
 
int fat_dir_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg)
{
int err;
/*
* We want to provide an interface for Samba to be able
* to get the short filename for a given long filename.
* Samba should use this ioctl instead of readdir() to
* get the information it needs.
*/
switch (cmd) {
case VFAT_IOCTL_READDIR_BOTH: {
struct dirent *d1 = (struct dirent *)arg;
err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
if (err)
return err;
put_user(0, &d1->d_reclen);
return fat_readdirx(inode,filp,(void *)arg,
vfat_ioctl_fill, 0, 1);
}
case VFAT_IOCTL_READDIR_SHORT: {
struct dirent *d1 = (struct dirent *)arg;
put_user(0, &d1->d_reclen);
err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
if (err)
return err;
return fat_readdirx(inode,filp,(void *)arg,
vfat_ioctl_fill, 1, 1);
}
default:
/* forward ioctl to CVF extension */
if (MSDOS_SB(inode->i_sb)->cvf_format &&
MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
return MSDOS_SB(inode->i_sb)->cvf_format
->cvf_dir_ioctl(inode,filp,cmd,arg);
return -EINVAL;
}
 
return 0;
}
 
/***** See if directory is empty */
int fat_dir_empty(struct inode *dir)
{
loff_t pos, i_pos;
struct buffer_head *bh;
struct msdos_dir_entry *de;
int result = 0;
 
pos = 0;
bh = NULL;
while (fat_get_entry(dir,&pos,&bh,&de,&i_pos) > -1) {
/* Ignore vfat longname entries */
if (de->attr == ATTR_EXT)
continue;
if (!IS_FREE(de->name) &&
strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
result = -ENOTEMPTY;
break;
}
}
if (bh)
fat_brelse(dir->i_sb, bh);
 
return result;
}
 
/* This assumes that size of cluster is above the 32*slots */
 
int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
struct msdos_dir_entry **de, loff_t *i_pos)
{
struct super_block *sb = dir->i_sb;
loff_t offset, curr;
int row;
struct buffer_head *new_bh;
 
offset = curr = 0;
*bh = NULL;
row = 0;
while (fat_get_entry(dir,&curr,bh,de,i_pos) > -1) {
if (IS_FREE((*de)->name)) {
if (++row == slots)
return offset;
} else {
row = 0;
offset = curr;
}
}
if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
return -ENOSPC;
new_bh = fat_extend_dir(dir);
if (!new_bh)
return -ENOSPC;
fat_brelse(sb, new_bh);
do fat_get_entry(dir,&curr,bh,de,i_pos); while (++row<slots);
return offset;
}
 
int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
{
struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
__u16 date, time;
 
if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC;
/* zeroed out, so... */
fat_date_unix2dos(dir->i_mtime,&time,&date);
de = (struct msdos_dir_entry*)&bh->b_data[0];
memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
de[0].attr = de[1].attr = ATTR_DIR;
de[0].time = de[1].time = CT_LE_W(time);
de[0].date = de[1].date = CT_LE_W(date);
if (is_vfat) { /* extra timestamps */
de[0].ctime = de[1].ctime = CT_LE_W(time);
de[0].adate = de[0].cdate =
de[1].adate = de[1].cdate = CT_LE_W(date);
}
de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
fat_mark_buffer_dirty(sb, bh);
fat_brelse(sb, bh);
dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(dir);
 
return 0;
}
 
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 8
* c-brace-imaginary-offset: 0
* c-brace-offset: -8
* c-argdecl-indent: 8
* c-label-offset: -8
* c-continued-statement-offset: 8
* c-continued-brace-offset: 0
* End:
*/
/fatfs_syms.c
0,0 → 1,48
/*
* linux/fs/fat/fatfs_syms.c
*
* Exported kernel symbols for the low-level FAT-based fs support.
*
*/
 
#include <linux/module.h>
#include <linux/init.h>
 
#include <linux/mm.h>
#include <linux/msdos_fs.h>
#include <linux/fat_cvf.h>
 
EXPORT_SYMBOL(fat_new_dir);
EXPORT_SYMBOL(fat_get_block);
EXPORT_SYMBOL(fat_clear_inode);
EXPORT_SYMBOL(fat_date_unix2dos);
EXPORT_SYMBOL(fat_delete_inode);
EXPORT_SYMBOL(fat__get_entry);
EXPORT_SYMBOL(fat_mark_buffer_dirty);
EXPORT_SYMBOL(fat_notify_change);
EXPORT_SYMBOL(fat_put_super);
EXPORT_SYMBOL(fat_attach);
EXPORT_SYMBOL(fat_detach);
EXPORT_SYMBOL(fat_build_inode);
EXPORT_SYMBOL(fat_read_super);
EXPORT_SYMBOL(fat_search_long);
EXPORT_SYMBOL(fat_readdir);
EXPORT_SYMBOL(fat_scan);
EXPORT_SYMBOL(fat_statfs);
EXPORT_SYMBOL(fat_write_inode);
EXPORT_SYMBOL(register_cvf_format);
EXPORT_SYMBOL(unregister_cvf_format);
EXPORT_SYMBOL(fat_get_cluster);
EXPORT_SYMBOL(fat_dir_ioctl);
EXPORT_SYMBOL(fat_add_entries);
EXPORT_SYMBOL(fat_dir_empty);
EXPORT_SYMBOL(fat_truncate);
EXPORT_SYMBOL(fat_brelse);
 
static int __init init_fat_fs(void)
{
fat_hash_init();
return 0;
}
 
module_init(init_fat_fs)
/inode.c
0,0 → 1,1074
/*
* linux/fs/fat/inode.c
*
* Written 1992,1993 by Werner Almesberger
* VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
* Rewritten for the constant inumbers support by Al Viro
*
* Fixes:
*
* Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
*/
 
#include <linux/module.h>
#include <linux/msdos_fs.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/locks.h>
#include <linux/fat_cvf.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
 
#include <asm/uaccess.h>
#include <asm/unaligned.h>
 
extern struct cvf_format default_cvf;
 
/* #define FAT_PARANOIA 1 */
#define DEBUG_LEVEL 0
#ifdef FAT_DEBUG
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
#if (DEBUG_LEVEL >= 1)
# define PRINTK1(x) printk x
#else
# define PRINTK1(x)
#endif
 
/*
* New FAT inode stuff. We do the following:
* a) i_ino is constant and has nothing with on-disk location.
* b) FAT manages its own cache of directory entries.
* c) *This* cache is indexed by on-disk location.
* d) inode has an associated directory entry, all right, but
* it may be unhashed.
* e) currently entries are stored within struct inode. That should
* change.
* f) we deal with races in the following way:
* 1. readdir() and lookup() do FAT-dir-cache lookup.
* 2. rename() unhashes the F-d-c entry and rehashes it in
* a new place.
* 3. unlink() and rmdir() unhash F-d-c entry.
* 4. fat_write_inode() checks whether the thing is unhashed.
* If it is we silently return. If it isn't we do bread(),
* check if the location is still valid and retry if it
* isn't. Otherwise we do changes.
* 5. Spinlock is used to protect hash/unhash/location check/lookup
* 6. fat_clear_inode() unhashes the F-d-c entry.
* 7. lookup() and readdir() do igrab() if they find a F-d-c entry
* and consider negative result as cache miss.
*/
 
#define FAT_HASH_BITS 8
#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
#define FAT_HASH_MASK (FAT_HASH_SIZE-1)
static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
 
void fat_hash_init(void)
{
int i;
for(i = 0; i < FAT_HASH_SIZE; i++) {
INIT_LIST_HEAD(&fat_inode_hashtable[i]);
}
}
 
static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos)
{
unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
return tmp & FAT_HASH_MASK;
}
 
void fat_attach(struct inode *inode, loff_t i_pos)
{
spin_lock(&fat_inode_lock);
MSDOS_I(inode)->i_pos = i_pos;
list_add(&MSDOS_I(inode)->i_fat_hash,
fat_inode_hashtable + fat_hash(inode->i_sb, i_pos));
spin_unlock(&fat_inode_lock);
}
 
void fat_detach(struct inode *inode)
{
spin_lock(&fat_inode_lock);
MSDOS_I(inode)->i_pos = 0;
list_del(&MSDOS_I(inode)->i_fat_hash);
INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
spin_unlock(&fat_inode_lock);
}
 
struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
{
struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos);
struct list_head *walk;
struct msdos_inode_info *i;
struct inode *inode = NULL;
 
spin_lock(&fat_inode_lock);
list_for_each(walk, p) {
i = list_entry(walk, struct msdos_inode_info, i_fat_hash);
if (i->i_fat_inode->i_sb != sb)
continue;
if (i->i_pos != i_pos)
continue;
inode = igrab(i->i_fat_inode);
if (inode)
break;
}
spin_unlock(&fat_inode_lock);
return inode;
}
 
static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
 
struct inode *fat_build_inode(struct super_block *sb,
struct msdos_dir_entry *de, loff_t i_pos, int *res)
{
struct inode *inode;
*res = 0;
inode = fat_iget(sb, i_pos);
if (inode)
goto out;
inode = new_inode(sb);
*res = -ENOMEM;
if (!inode)
goto out;
*res = 0;
inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
fat_fill_inode(inode, de);
fat_attach(inode, i_pos);
insert_inode_hash(inode);
out:
return inode;
}
 
void fat_delete_inode(struct inode *inode)
{
if (!is_bad_inode(inode)) {
lock_kernel();
inode->i_size = 0;
fat_truncate(inode);
unlock_kernel();
}
clear_inode(inode);
}
 
void fat_clear_inode(struct inode *inode)
{
if (is_bad_inode(inode))
return;
lock_kernel();
spin_lock(&fat_inode_lock);
fat_cache_inval_inode(inode);
list_del(&MSDOS_I(inode)->i_fat_hash);
spin_unlock(&fat_inode_lock);
unlock_kernel();
}
 
void fat_put_super(struct super_block *sb)
{
if (MSDOS_SB(sb)->cvf_format->cvf_version) {
dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
}
if (MSDOS_SB(sb)->fat_bits == 32) {
fat_clusters_flush(sb);
}
fat_cache_inval_dev(sb->s_dev);
set_blocksize (sb->s_dev,BLOCK_SIZE);
if (MSDOS_SB(sb)->nls_disk) {
unload_nls(MSDOS_SB(sb)->nls_disk);
MSDOS_SB(sb)->nls_disk = NULL;
MSDOS_SB(sb)->options.codepage = 0;
}
if (MSDOS_SB(sb)->nls_io) {
unload_nls(MSDOS_SB(sb)->nls_io);
MSDOS_SB(sb)->nls_io = NULL;
}
/*
* Note: the iocharset option might have been specified
* without enabling nls_io, so check for it here.
*/
if (MSDOS_SB(sb)->options.iocharset) {
kfree(MSDOS_SB(sb)->options.iocharset);
MSDOS_SB(sb)->options.iocharset = NULL;
}
}
 
 
static int parse_options(char *options,int *fat, int *debug,
struct fat_mount_options *opts,
char *cvf_format, char *cvf_options)
{
char *this_char,*value,save,*savep;
char *p;
int ret = 1, len;
 
opts->name_check = 'n';
opts->conversion = 'b';
opts->fs_uid = current->uid;
opts->fs_gid = current->gid;
opts->fs_umask = current->fs->umask;
opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
opts->codepage = 0;
opts->nocase = 0;
opts->shortname = 0;
opts->utf8 = 0;
opts->iocharset = NULL;
*debug = *fat = 0;
 
if (!options)
goto out;
save = 0;
savep = NULL;
for (this_char = strtok(options,","); this_char;
this_char = strtok(NULL,",")) {
if ((value = strchr(this_char,'=')) != NULL) {
save = *value;
savep = value;
*value++ = 0;
}
if (!strcmp(this_char,"check") && value) {
if (value[0] && !value[1] && strchr("rns",*value))
opts->name_check = *value;
else if (!strcmp(value,"relaxed"))
opts->name_check = 'r';
else if (!strcmp(value,"normal"))
opts->name_check = 'n';
else if (!strcmp(value,"strict"))
opts->name_check = 's';
else ret = 0;
}
else if (!strcmp(this_char,"conv") && value) {
if (value[0] && !value[1] && strchr("bta",*value))
opts->conversion = *value;
else if (!strcmp(value,"binary"))
opts->conversion = 'b';
else if (!strcmp(value,"text"))
opts->conversion = 't';
else if (!strcmp(value,"auto"))
opts->conversion = 'a';
else ret = 0;
}
else if (!strcmp(this_char,"dots")) {
opts->dotsOK = 1;
}
else if (!strcmp(this_char,"nocase")) {
opts->nocase = 1;
}
else if (!strcmp(this_char,"nodots")) {
opts->dotsOK = 0;
}
else if (!strcmp(this_char,"showexec")) {
opts->showexec = 1;
}
else if (!strcmp(this_char,"dotsOK") && value) {
if (!strcmp(value,"yes")) opts->dotsOK = 1;
else if (!strcmp(value,"no")) opts->dotsOK = 0;
else ret = 0;
}
else if (!strcmp(this_char,"uid")) {
if (!value || !*value) ret = 0;
else {
opts->fs_uid = simple_strtoul(value,&value,0);
if (*value) ret = 0;
}
}
else if (!strcmp(this_char,"gid")) {
if (!value || !*value) ret= 0;
else {
opts->fs_gid = simple_strtoul(value,&value,0);
if (*value) ret = 0;
}
}
else if (!strcmp(this_char,"umask")) {
if (!value || !*value) ret = 0;
else {
opts->fs_umask = simple_strtoul(value,&value,8);
if (*value) ret = 0;
}
}
else if (!strcmp(this_char,"debug")) {
if (value) ret = 0;
else *debug = 1;
}
else if (!strcmp(this_char,"fat")) {
if (!value || !*value) ret = 0;
else {
*fat = simple_strtoul(value,&value,0);
if (*value || (*fat != 12 && *fat != 16 &&
*fat != 32))
ret = 0;
}
}
else if (!strcmp(this_char,"quiet")) {
if (value) ret = 0;
else opts->quiet = 1;
}
else if (!strcmp(this_char,"blocksize")) {
printk("FAT: blocksize option is obsolete, "
"not supported now\n");
}
else if (!strcmp(this_char,"sys_immutable")) {
if (value) ret = 0;
else opts->sys_immutable = 1;
}
else if (!strcmp(this_char,"codepage") && value) {
opts->codepage = simple_strtoul(value,&value,0);
if (*value) ret = 0;
else printk ("MSDOS FS: Using codepage %d\n",
opts->codepage);
}
else if (!strcmp(this_char,"iocharset") && value) {
p = value;
while (*value && *value != ',')
value++;
len = value - p;
if (len) {
char *buffer;
 
if (opts->iocharset != NULL) {
kfree(opts->iocharset);
opts->iocharset = NULL;
}
buffer = kmalloc(len + 1, GFP_KERNEL);
if (buffer != NULL) {
opts->iocharset = buffer;
memcpy(buffer, p, len);
buffer[len] = 0;
printk("MSDOS FS: IO charset %s\n", buffer);
} else
ret = 0;
}
}
else if (!strcmp(this_char,"cvf_format")) {
if (!value)
return 0;
strncpy(cvf_format,value,20);
}
else if (!strcmp(this_char,"cvf_options")) {
if (!value)
return 0;
strncpy(cvf_options,value,100);
}
 
if (this_char != options) *(this_char-1) = ',';
if (value) *savep = save;
if (ret == 0)
break;
}
out:
return ret;
}
 
static void fat_read_root(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int nr;
 
INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
MSDOS_I(inode)->i_pos = 0;
MSDOS_I(inode)->i_fat_inode = inode;
inode->i_uid = sbi->options.fs_uid;
inode->i_gid = sbi->options.fs_gid;
inode->i_version = ++event;
inode->i_generation = 0;
inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR;
inode->i_op = sbi->dir_ops;
inode->i_fop = &fat_dir_operations;
if (sbi->fat_bits == 32) {
MSDOS_I(inode)->i_start = sbi->root_cluster;
if ((nr = MSDOS_I(inode)->i_start) != 0) {
while (nr != -1) {
inode->i_size += 1 << sbi->cluster_bits;
if (!(nr = fat_access(sb, nr, -1))) {
printk("Directory %ld: bad FAT\n",
inode->i_ino);
break;
}
}
}
} else {
MSDOS_I(inode)->i_start = 0;
inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
}
inode->i_blksize = 1 << sbi->cluster_bits;
inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
& ~(inode->i_blksize - 1)) >> 9;
MSDOS_I(inode)->i_logstart = 0;
MSDOS_I(inode)->mmu_private = inode->i_size;
 
MSDOS_I(inode)->i_attrs = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
MSDOS_I(inode)->i_ctime_ms = 0;
inode->i_nlink = fat_subdirs(inode)+2;
}
 
/*
* a FAT file handle with fhtype 3 is
* 0/ i_ino - for fast, reliable lookup if still in the cache
* 1/ i_generation - to see if i_ino is still valid
* bit 0 == 0 iff directory
* 2/ i_pos(8-39) - if ino has changed, but still in cache
* 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
* 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
*
* Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
* i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
* of i_logstart is used to store the directory entry offset.
*/
struct dentry *fat_fh_to_dentry(struct super_block *sb, __u32 *fh,
int len, int fhtype, int parent)
{
struct inode *inode = NULL;
struct list_head *lp;
struct dentry *result;
 
if (fhtype != 3)
return ERR_PTR(-ESTALE);
if (len < 5)
return ERR_PTR(-ESTALE);
/* We cannot find the parent,
It better just *be* there */
if (parent)
return ERR_PTR(-ESTALE);
 
inode = iget(sb, fh[0]);
if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) {
if (inode)
iput(inode);
inode = NULL;
}
if (!inode) {
loff_t i_pos;
int i_logstart = fh[3] & 0x0fffffff;
 
i_pos = (loff_t)fh[2] << 8;
i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
 
/* try 2 - see if i_pos is in F-d-c
* require i_logstart to be the same
* Will fail if you truncate and then re-write
*/
 
inode = fat_iget(sb, i_pos);
if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
iput(inode);
inode = NULL;
}
}
if (!inode) {
/* For now, do nothing
* What we could do is:
* follow the file starting at fh[4], and record
* the ".." entry, and the name of the fh[2] entry.
* The follow the ".." file finding the next step up.
* This way we build a path to the root of
* the tree. If this works, we lookup the path and so
* get this inode into the cache.
* Finally try the fat_iget lookup again
* If that fails, then weare totally out of luck
* But all that is for another day
*/
}
if (!inode)
return ERR_PTR(-ESTALE);
 
/* now to find a dentry.
* If possible, get a well-connected one
*
* Given the way that we found the inode, it *MUST* be
* well-connected, but it is easiest to just copy the
* code.
*/
spin_lock(&dcache_lock);
for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
result = list_entry(lp,struct dentry, d_alias);
if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
dget_locked(result);
result->d_vfs_flags |= DCACHE_REFERENCED;
spin_unlock(&dcache_lock);
iput(inode);
return result;
}
}
spin_unlock(&dcache_lock);
result = d_alloc_root(inode);
if (result == NULL) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
result->d_op = sb->s_root->d_op;
result->d_flags |= DCACHE_NFSD_DISCONNECTED;
return result;
}
 
int fat_dentry_to_fh(struct dentry *de, __u32 *fh, int *lenp, int needparent)
{
int len = *lenp;
struct inode *inode = de->d_inode;
u32 ipos_h, ipos_m, ipos_l;
if (len < 5)
return 255; /* no room */
 
ipos_h = MSDOS_I(inode)->i_pos >> 8;
ipos_m = (MSDOS_I(inode)->i_pos & 0xf0) << 24;
ipos_l = (MSDOS_I(inode)->i_pos & 0x0f) << 28;
*lenp = 5;
fh[0] = inode->i_ino;
fh[1] = inode->i_generation;
fh[2] = ipos_h;
fh[3] = ipos_m | MSDOS_I(inode)->i_logstart;
fh[4] = ipos_l | MSDOS_I(de->d_parent->d_inode)->i_logstart;
return 3;
}
 
static struct super_operations fat_sops = {
write_inode: fat_write_inode,
delete_inode: fat_delete_inode,
put_super: fat_put_super,
statfs: fat_statfs,
clear_inode: fat_clear_inode,
 
read_inode: make_bad_inode,
fh_to_dentry: fat_fh_to_dentry,
dentry_to_fh: fat_dentry_to_fh,
};
 
/*
* Read the super block of an MS-DOS FS.
*
* Note that this may be called from vfat_read_super
* with some fields already initialized.
*/
struct super_block *
fat_read_super(struct super_block *sb, void *data, int silent,
struct inode_operations *fs_dir_inode_ops)
{
struct inode *root_inode;
struct buffer_head *bh;
struct fat_boot_sector *b;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
char *p;
int logical_sector_size, hard_blksize, fat_clusters = 0;
unsigned int total_sectors, rootdir_sectors;
int fat32, debug, error, fat, cp;
struct fat_mount_options opts;
char buf[50];
int i;
char cvf_format[21];
char cvf_options[101];
 
cvf_format[0] = '\0';
cvf_options[0] = '\0';
sbi->cvf_format = NULL;
sbi->private_data = NULL;
 
sbi->dir_ops = fs_dir_inode_ops;
 
sb->s_maxbytes = MAX_NON_LFS;
sb->s_op = &fat_sops;
 
hard_blksize = get_hardsect_size(sb->s_dev);
if (!hard_blksize)
hard_blksize = 512;
 
opts.isvfat = sbi->options.isvfat;
if (!parse_options((char *) data, &fat, &debug, &opts,
cvf_format, cvf_options))
goto out_fail;
/* N.B. we should parse directly into the sb structure */
memcpy(&(sbi->options), &opts, sizeof(struct fat_mount_options));
 
fat_cache_init();
 
sb->s_blocksize = hard_blksize;
set_blocksize(sb->s_dev, hard_blksize);
bh = sb_bread(sb, 0);
if (bh == NULL) {
printk("FAT: unable to read boot sector\n");
goto out_fail;
}
 
/*
* The DOS3 partition size limit is *not* 32M as many people think.
* Instead, it is 64K sectors (with the usual sector size being
* 512 bytes, leading to a 32M limit).
*
* DOS 3 partition managers got around this problem by faking a
* larger sector size, ie treating multiple physical sectors as
* a single logical sector.
*
* We can accommodate this scheme by adjusting our cluster size,
* fat_start, and data_start by an appropriate value.
*
* (by Drew Eckhardt)
*/
 
 
b = (struct fat_boot_sector *) bh->b_data;
logical_sector_size =
CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
if (!logical_sector_size
|| (logical_sector_size & (logical_sector_size - 1))) {
printk("FAT: bogus logical sector size %d\n",
logical_sector_size);
brelse(bh);
goto out_invalid;
}
 
sbi->cluster_size = b->cluster_size;
if (!sbi->cluster_size
|| (sbi->cluster_size & (sbi->cluster_size - 1))) {
printk("FAT: bogus cluster size %d\n", sbi->cluster_size);
brelse(bh);
goto out_invalid;
}
 
if (logical_sector_size < hard_blksize) {
printk("FAT: logical sector size too small for device"
" (logical sector size = %d)\n", logical_sector_size);
brelse(bh);
goto out_invalid;
}
 
sbi->cluster_bits = ffs(logical_sector_size * sbi->cluster_size) - 1;
sbi->fats = b->fats;
sbi->fat_start = CF_LE_W(b->reserved);
sbi->prev_free = 0;
if (!b->fat_length && b->fat32_length) {
struct fat_boot_fsinfo *fsinfo;
struct buffer_head *fsinfo_bh;
int fsinfo_block, fsinfo_offset;
 
/* Must be FAT32 */
fat32 = 1;
sbi->fat_length = CF_LE_L(b->fat32_length);
sbi->root_cluster = CF_LE_L(b->root_cluster);
 
sbi->fsinfo_sector = CF_LE_W(b->info_sector);
/* MC - if info_sector is 0, don't multiply by 0 */
if (sbi->fsinfo_sector == 0)
sbi->fsinfo_sector = 1;
 
fsinfo_block =
(sbi->fsinfo_sector * logical_sector_size) / hard_blksize;
fsinfo_offset =
(sbi->fsinfo_sector * logical_sector_size) % hard_blksize;
fsinfo_bh = bh;
if (fsinfo_block != 0) {
fsinfo_bh = sb_bread(sb, fsinfo_block);
if (fsinfo_bh == NULL) {
printk("FAT: bread failed, FSINFO block"
" (blocknr = %d)\n", fsinfo_block);
brelse(bh);
goto out_invalid;
}
}
fsinfo = (struct fat_boot_fsinfo *)&fsinfo_bh->b_data[fsinfo_offset];
if (!IS_FSINFO(fsinfo)) {
printk("FAT: Did not find valid FSINFO signature.\n"
"Found signature1 0x%x signature2 0x%x sector=%ld.\n",
CF_LE_L(fsinfo->signature1),
CF_LE_L(fsinfo->signature2),
sbi->fsinfo_sector);
} else {
sbi->free_clusters = CF_LE_L(fsinfo->free_clusters);
sbi->prev_free = CF_LE_L(fsinfo->next_cluster);
}
 
if (fsinfo_block != 0)
brelse(fsinfo_bh);
} else {
fat32 = 0;
sbi->fat_length = CF_LE_W(b->fat_length);
sbi->root_cluster = 0;
sbi->free_clusters = -1; /* Don't know yet */
}
 
sbi->dir_per_block = logical_sector_size / sizeof(struct msdos_dir_entry);
sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
 
sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length;
sbi->dir_entries =
CF_LE_W(get_unaligned((unsigned short *)&b->dir_entries));
rootdir_sectors = sbi->dir_entries
* sizeof(struct msdos_dir_entry) / logical_sector_size;
sbi->data_start = sbi->dir_start + rootdir_sectors;
total_sectors = CF_LE_W(get_unaligned((unsigned short *)&b->sectors));
if (total_sectors == 0)
total_sectors = CF_LE_L(b->total_sect);
sbi->clusters = (total_sectors - sbi->data_start) / sbi->cluster_size;
 
error = 0;
if (!error) {
sbi->fat_bits = fat32 ? 32 :
(fat ? fat :
(sbi->clusters > MSDOS_FAT12 ? 16 : 12));
fat_clusters =
sbi->fat_length * logical_sector_size * 8 / sbi->fat_bits;
error = !sbi->fats || (sbi->dir_entries & (sbi->dir_per_block - 1))
|| sbi->clusters + 2 > fat_clusters + MSDOS_MAX_EXTRA
|| logical_sector_size < 512
|| PAGE_CACHE_SIZE < logical_sector_size
|| !b->secs_track || !b->heads;
}
brelse(bh);
 
if (error)
goto out_invalid;
 
sb->s_blocksize = logical_sector_size;
sb->s_blocksize_bits = ffs(logical_sector_size) - 1;
set_blocksize(sb->s_dev, sb->s_blocksize);
sbi->cvf_format = &default_cvf;
if (!strcmp(cvf_format, "none"))
i = -1;
else
i = detect_cvf(sb,cvf_format);
if (i >= 0)
error = cvf_formats[i]->mount_cvf(sb, cvf_options);
if (error || debug) {
/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
"uid=%d,gid=%d,umask=%03o%s]\n",
sbi->fat_bits,opts.name_check,
opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
MSDOS_CAN_BMAP(sbi) ? ",bmap" : "");
printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%ld,ds=%ld,de=%d,data=%ld,"
"se=%u,ts=%u,ls=%d,rc=%ld,fc=%u]\n",
b->media, sbi->cluster_size, sbi->fats,
sbi->fat_start, sbi->fat_length, sbi->dir_start,
sbi->dir_entries, sbi->data_start,
CF_LE_W(get_unaligned((unsigned short *)&b->sectors)),
CF_LE_L(b->total_sect), logical_sector_size,
sbi->root_cluster, sbi->free_clusters);
printk ("hard sector size = %d\n", hard_blksize);
}
if (i < 0)
if (sbi->clusters + 2 > fat_clusters)
sbi->clusters = fat_clusters - 2;
if (error)
goto out_invalid;
 
sb->s_magic = MSDOS_SUPER_MAGIC;
/* set up enough so that it can read an inode */
init_MUTEX(&sbi->fat_lock);
 
cp = opts.codepage ? opts.codepage : 437;
sprintf(buf, "cp%d", cp);
sbi->nls_disk = load_nls(buf);
if (! sbi->nls_disk) {
/* Fail only if explicit charset specified */
if (opts.codepage != 0)
goto out_fail;
sbi->options.codepage = 0; /* already 0?? */
sbi->nls_disk = load_nls_default();
}
 
sbi->nls_io = NULL;
if (sbi->options.isvfat && !opts.utf8) {
p = opts.iocharset ? opts.iocharset : CONFIG_NLS_DEFAULT;
sbi->nls_io = load_nls(p);
if (! sbi->nls_io)
/* Fail only if explicit charset specified */
if (opts.iocharset)
goto out_unload_nls;
}
if (! sbi->nls_io)
sbi->nls_io = load_nls_default();
 
root_inode = new_inode(sb);
if (!root_inode)
goto out_unload_nls;
root_inode->i_ino = MSDOS_ROOT_INO;
fat_read_root(root_inode);
insert_inode_hash(root_inode);
sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
if(i >= 0) {
sbi->cvf_format = cvf_formats[i];
++cvf_format_use_count[i];
}
return sb;
 
out_no_root:
printk("FAT: get root inode failed\n");
iput(root_inode);
unload_nls(sbi->nls_io);
out_unload_nls:
unload_nls(sbi->nls_disk);
goto out_fail;
out_invalid:
if (!silent) {
printk("VFS: Can't find a valid FAT filesystem on dev %s.\n",
kdevname(sb->s_dev));
}
out_fail:
if (opts.iocharset) {
printk("FAT: freeing iocharset=%s\n", opts.iocharset);
kfree(opts.iocharset);
}
if(sbi->private_data)
kfree(sbi->private_data);
sbi->private_data = NULL;
return NULL;
}
 
int fat_statfs(struct super_block *sb,struct statfs *buf)
{
int free,nr;
if (MSDOS_SB(sb)->cvf_format &&
MSDOS_SB(sb)->cvf_format->cvf_statfs)
return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,
sizeof(struct statfs));
lock_fat(sb);
if (MSDOS_SB(sb)->free_clusters != -1)
free = MSDOS_SB(sb)->free_clusters;
else {
free = 0;
for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
if (!fat_access(sb,nr,-1)) free++;
MSDOS_SB(sb)->free_clusters = free;
}
unlock_fat(sb);
buf->f_type = sb->s_magic;
buf->f_bsize = 1 << MSDOS_SB(sb)->cluster_bits;
buf->f_blocks = MSDOS_SB(sb)->clusters;
buf->f_bfree = free;
buf->f_bavail = free;
buf->f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
return 0;
}
 
static int is_exec(char *extension)
{
char *exe_extensions = "EXECOMBAT", *walk;
 
for (walk = exe_extensions; *walk; walk += 3)
if (!strncmp(extension, walk, 3))
return 1;
return 0;
}
 
static int fat_writepage(struct page *page)
{
return block_write_full_page(page,fat_get_block);
}
static int fat_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page,fat_get_block);
}
static int fat_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
{
return cont_prepare_write(page,from,to,fat_get_block,
&MSDOS_I(page->mapping->host)->mmu_private);
}
static int _fat_bmap(struct address_space *mapping, long block)
{
return generic_block_bmap(mapping,block,fat_get_block);
}
static struct address_space_operations fat_aops = {
readpage: fat_readpage,
writepage: fat_writepage,
sync_page: block_sync_page,
prepare_write: fat_prepare_write,
commit_write: generic_commit_write,
bmap: _fat_bmap
};
 
/* doesn't deal with root inode */
static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int nr;
 
INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
MSDOS_I(inode)->i_pos = 0;
MSDOS_I(inode)->i_fat_inode = inode;
inode->i_uid = sbi->options.fs_uid;
inode->i_gid = sbi->options.fs_gid;
inode->i_version = ++event;
inode->i_generation = CURRENT_TIME;
if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
inode->i_generation &= ~1;
inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
~sbi->options.fs_umask) | S_IFDIR;
inode->i_op = sbi->dir_ops;
inode->i_fop = &fat_dir_operations;
 
MSDOS_I(inode)->i_start = CF_LE_W(de->start);
if (sbi->fat_bits == 32)
MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
 
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_nlink = fat_subdirs(inode);
/* includes .., compensating for "self" */
#ifdef DEBUG
if (!inode->i_nlink) {
printk("directory %d: i_nlink == 0\n",inode->i_ino);
inode->i_nlink = 1;
}
#endif
if ((nr = MSDOS_I(inode)->i_start) != 0)
while (nr != -1) {
inode->i_size += 1 << sbi->cluster_bits;
if (!(nr = fat_access(sb, nr, -1))) {
printk("Directory %ld: bad FAT\n",
inode->i_ino);
break;
}
}
MSDOS_I(inode)->mmu_private = inode->i_size;
} else { /* not a directory */
inode->i_generation |= 1;
inode->i_mode = MSDOS_MKMODE(de->attr,
((sbi->options.showexec &&
!is_exec(de->ext))
? S_IRUGO|S_IWUGO : S_IRWXUGO)
& ~sbi->options.fs_umask) | S_IFREG;
MSDOS_I(inode)->i_start = CF_LE_W(de->start);
if (sbi->fat_bits == 32)
MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
 
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_size = CF_LE_L(de->size);
inode->i_op = &fat_file_inode_operations;
inode->i_fop = &fat_file_operations;
inode->i_mapping->a_ops = &fat_aops;
MSDOS_I(inode)->mmu_private = inode->i_size;
}
if(de->attr & ATTR_SYS)
if (sbi->options.sys_immutable)
inode->i_flags |= S_IMMUTABLE;
MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
/* this is as close to the truth as we can get ... */
inode->i_blksize = 1 << sbi->cluster_bits;
inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
& ~(inode->i_blksize - 1)) >> 9;
inode->i_mtime = inode->i_atime =
date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
inode->i_ctime =
MSDOS_SB(sb)->options.isvfat
? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
: inode->i_mtime;
MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
}
 
void fat_write_inode(struct inode *inode, int wait)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *raw_entry;
loff_t i_pos;
 
retry:
i_pos = MSDOS_I(inode)->i_pos;
if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) {
return;
}
lock_kernel();
if (!(bh = fat_bread(sb, i_pos >> MSDOS_SB(sb)->dir_per_block_bits))) {
printk("dev = %s, i_pos = %llu\n", kdevname(inode->i_dev), i_pos);
fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
unlock_kernel();
return;
}
spin_lock(&fat_inode_lock);
if (i_pos != MSDOS_I(inode)->i_pos) {
spin_unlock(&fat_inode_lock);
fat_brelse(sb, bh);
unlock_kernel();
goto retry;
}
 
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
[i_pos & (MSDOS_SB(sb)->dir_per_block - 1)];
if (S_ISDIR(inode->i_mode)) {
raw_entry->attr = ATTR_DIR;
raw_entry->size = 0;
}
else {
raw_entry->attr = ATTR_NONE;
raw_entry->size = CT_LE_L(inode->i_size);
}
raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
MSDOS_I(inode)->i_attrs;
raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
raw_entry->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
fat_date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
raw_entry->time = CT_LE_W(raw_entry->time);
raw_entry->date = CT_LE_W(raw_entry->date);
if (MSDOS_SB(sb)->options.isvfat) {
fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms;
raw_entry->ctime = CT_LE_W(raw_entry->ctime);
raw_entry->cdate = CT_LE_W(raw_entry->cdate);
}
spin_unlock(&fat_inode_lock);
fat_mark_buffer_dirty(sb, bh);
fat_brelse(sb, bh);
unlock_kernel();
}
 
 
int fat_notify_change(struct dentry * dentry, struct iattr * attr)
{
struct super_block *sb = dentry->d_sb;
struct inode *inode = dentry->d_inode;
int error;
 
/* FAT cannot truncate to a longer file */
if (attr->ia_valid & ATTR_SIZE) {
if (attr->ia_size > inode->i_size)
return -EPERM;
}
 
error = inode_change_ok(inode, attr);
if (error)
return MSDOS_SB(sb)->options.quiet ? 0 : error;
 
if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != MSDOS_SB(sb)->options.fs_uid)) ||
((attr->ia_valid & ATTR_GID) &&
(attr->ia_gid != MSDOS_SB(sb)->options.fs_gid)) ||
((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~MSDOS_VALID_MODE)))
error = -EPERM;
 
if (error)
return MSDOS_SB(sb)->options.quiet ? 0 : error;
 
error = inode_setattr(inode, attr);
if (error)
return error;
 
if (S_ISDIR(inode->i_mode))
inode->i_mode |= S_IXUGO;
 
inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
& ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
~MSDOS_SB(sb)->options.fs_umask;
return 0;
}
MODULE_LICENSE("GPL");
/file.c
0,0 → 1,136
/*
* linux/fs/fat/file.c
*
* Written 1992,1993 by Werner Almesberger
*
* regular file handling primitives for fat-based filesystems
*/
 
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/fat_cvf.h>
 
#include <asm/uaccess.h>
#include <asm/system.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
struct file_operations fat_file_operations = {
llseek: generic_file_llseek,
read: fat_file_read,
write: fat_file_write,
mmap: generic_file_mmap,
fsync: file_fsync,
};
 
struct inode_operations fat_file_inode_operations = {
truncate: fat_truncate,
setattr: fat_notify_change,
};
 
ssize_t fat_file_read(
struct file *filp,
char *buf,
size_t count,
loff_t *ppos)
{
struct inode *inode = filp->f_dentry->d_inode;
return MSDOS_SB(inode->i_sb)->cvf_format
->cvf_file_read(filp,buf,count,ppos);
}
 
 
int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
{
struct super_block *sb = inode->i_sb;
unsigned long phys;
 
phys = fat_bmap(inode, iblock);
if (phys) {
bh_result->b_dev = inode->i_dev;
bh_result->b_blocknr = phys;
bh_result->b_state |= (1UL << BH_Mapped);
return 0;
}
if (!create)
return 0;
if (iblock << sb->s_blocksize_bits != MSDOS_I(inode)->mmu_private) {
BUG();
return -EIO;
}
if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) {
if (fat_add_cluster(inode) < 0)
return -ENOSPC;
}
MSDOS_I(inode)->mmu_private += sb->s_blocksize;
phys = fat_bmap(inode, iblock);
if (!phys)
BUG();
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);
return 0;
}
 
ssize_t fat_file_write(
struct file *filp,
const char *buf,
size_t count,
loff_t *ppos)
{
struct inode *inode = filp->f_dentry->d_inode;
struct super_block *sb = inode->i_sb;
return MSDOS_SB(sb)->cvf_format
->cvf_file_write(filp,buf,count,ppos);
}
 
ssize_t default_fat_file_write(
struct file *filp,
const char *buf,
size_t count,
loff_t *ppos)
{
struct inode *inode = filp->f_dentry->d_inode;
int retval;
 
retval = generic_file_write(filp, buf, count, ppos);
if (retval > 0) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
mark_inode_dirty(inode);
}
return retval;
}
 
void fat_truncate(struct inode *inode)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
int cluster;
 
/* Why no return value? Surely the disk could fail... */
if (IS_RDONLY (inode))
return /* -EPERM */;
if (IS_IMMUTABLE(inode))
return /* -EPERM */;
cluster = 1 << sbi->cluster_bits;
/*
* This protects against truncating a file bigger than it was then
* trying to write into the hole.
*/
if (MSDOS_I(inode)->mmu_private > inode->i_size)
MSDOS_I(inode)->mmu_private = inode->i_size;
 
fat_free(inode, (inode->i_size + (cluster - 1)) >> sbi->cluster_bits);
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode);
}
/cache.c
0,0 → 1,362
/*
* linux/fs/fat/cache.c
*
* Written 1992,1993 by Werner Almesberger
*
* Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
* of inode number.
* May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
*/
 
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fat_cvf.h>
 
#if 0
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
 
static struct fat_cache *fat_cache,cache[FAT_CACHE];
static spinlock_t fat_cache_lock = SPIN_LOCK_UNLOCKED;
 
/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
new_value is != -1, that FAT entry is replaced by it. */
 
int fat_access(struct super_block *sb,int nr,int new_value)
{
return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
}
 
int fat_bmap(struct inode *inode,int sector)
{
return MSDOS_SB(inode->i_sb)->cvf_format->cvf_bmap(inode,sector);
}
 
int default_fat_access(struct super_block *sb,int nr,int new_value)
{
struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
unsigned char *p_first, *p_last;
int copy, first, last, next, b;
 
if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
return 0;
if (MSDOS_SB(sb)->fat_bits == 32) {
first = last = nr*4;
} else if (MSDOS_SB(sb)->fat_bits == 16) {
first = last = nr*2;
} else {
first = nr*3/2;
last = first+1;
}
b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits);
if (!(bh = fat_bread(sb, b))) {
printk("bread in fat_access failed\n");
return 0;
}
if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
bh2 = bh;
} else {
if (!(bh2 = fat_bread(sb, b+1))) {
fat_brelse(sb, bh);
printk("2nd bread in fat_access failed\n");
return 0;
}
}
if (MSDOS_SB(sb)->fat_bits == 32) {
p_first = p_last = NULL; /* GCC needs that stuff */
next = CF_LE_L(((__u32 *) bh->b_data)[(first &
(sb->s_blocksize - 1)) >> 2]);
/* Fscking Microsoft marketing department. Their "32" is 28. */
next &= 0xfffffff;
if (next >= 0xffffff7) next = -1;
PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%x\n", b, nr, first, next));
 
} else if (MSDOS_SB(sb)->fat_bits == 16) {
p_first = p_last = NULL; /* GCC needs that stuff */
next = CF_LE_W(((__u16 *) bh->b_data)[(first &
(sb->s_blocksize - 1)) >> 1]);
if (next >= 0xfff7) next = -1;
} else {
p_first = &((__u8 *)bh->b_data)[first & (sb->s_blocksize - 1)];
p_last = &((__u8 *)bh2->b_data)[(first + 1) & (sb->s_blocksize - 1)];
if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
else next = (*p_first+(*p_last << 8)) & 0xfff;
if (next >= 0xff7) next = -1;
}
if (new_value != -1) {
if (MSDOS_SB(sb)->fat_bits == 32) {
((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
= CT_LE_L(new_value);
} else if (MSDOS_SB(sb)->fat_bits == 16) {
((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
= CT_LE_W(new_value);
} else {
if (nr & 1) {
*p_first = (*p_first & 0xf) | (new_value << 4);
*p_last = new_value >> 4;
}
else {
*p_first = new_value & 0xff;
*p_last = (*p_last & 0xf0) | (new_value >> 8);
}
fat_mark_buffer_dirty(sb, bh2);
}
fat_mark_buffer_dirty(sb, bh);
for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits)
+ MSDOS_SB(sb)->fat_length * copy;
if (!(c_bh = fat_bread(sb, b)))
break;
if (bh != bh2) {
if (!(c_bh2 = fat_bread(sb, b+1))) {
fat_brelse(sb, c_bh);
break;
}
memcpy(c_bh2->b_data, bh2->b_data, sb->s_blocksize);
fat_mark_buffer_dirty(sb, c_bh2);
fat_brelse(sb, c_bh2);
}
memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
fat_mark_buffer_dirty(sb, c_bh);
fat_brelse(sb, c_bh);
}
}
fat_brelse(sb, bh);
if (bh != bh2)
fat_brelse(sb, bh2);
return next;
}
 
void fat_cache_init(void)
{
static int initialized = 0;
int count;
 
spin_lock(&fat_cache_lock);
if (initialized) {
spin_unlock(&fat_cache_lock);
return;
}
fat_cache = &cache[0];
for (count = 0; count < FAT_CACHE; count++) {
cache[count].device = 0;
cache[count].next = count == FAT_CACHE-1 ? NULL :
&cache[count+1];
}
initialized = 1;
spin_unlock(&fat_cache_lock);
}
 
 
void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
{
struct fat_cache *walk;
int first = MSDOS_I(inode)->i_start;
 
if (!first)
return;
spin_lock(&fat_cache_lock);
for (walk = fat_cache; walk; walk = walk->next)
if (inode->i_dev == walk->device
&& walk->start_cluster == first
&& walk->file_cluster <= cluster
&& walk->file_cluster > *f_clu) {
*d_clu = walk->disk_cluster;
#ifdef DEBUG
printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
#endif
if ((*f_clu = walk->file_cluster) == cluster) {
spin_unlock(&fat_cache_lock);
return;
}
}
spin_unlock(&fat_cache_lock);
#ifdef DEBUG
printk("cache miss\n");
#endif
}
 
 
#ifdef DEBUG
static void list_cache(void)
{
struct fat_cache *walk;
 
for (walk = fat_cache; walk; walk = walk->next) {
if (walk->device)
printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
walk->start_cluster, walk->file_cluster,
walk->disk_cluster);
else printk("-- ");
}
printk("\n");
}
#endif
 
 
void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
{
struct fat_cache *walk,*last;
int first = MSDOS_I(inode)->i_start;
 
last = NULL;
spin_lock(&fat_cache_lock);
for (walk = fat_cache; walk->next; walk = (last = walk)->next)
if (inode->i_dev == walk->device
&& walk->start_cluster == first
&& walk->file_cluster == f_clu) {
if (walk->disk_cluster != d_clu) {
printk("FAT cache corruption inode=%ld\n",
inode->i_ino);
spin_unlock(&fat_cache_lock);
fat_cache_inval_inode(inode);
return;
}
/* update LRU */
if (last == NULL) {
spin_unlock(&fat_cache_lock);
return;
}
last->next = walk->next;
walk->next = fat_cache;
fat_cache = walk;
#ifdef DEBUG
list_cache();
#endif
spin_unlock(&fat_cache_lock);
return;
}
walk->device = inode->i_dev;
walk->start_cluster = first;
walk->file_cluster = f_clu;
walk->disk_cluster = d_clu;
last->next = NULL;
walk->next = fat_cache;
fat_cache = walk;
spin_unlock(&fat_cache_lock);
#ifdef DEBUG
list_cache();
#endif
}
 
 
/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
fixes itself after a while. */
 
void fat_cache_inval_inode(struct inode *inode)
{
struct fat_cache *walk;
int first = MSDOS_I(inode)->i_start;
 
spin_lock(&fat_cache_lock);
for (walk = fat_cache; walk; walk = walk->next)
if (walk->device == inode->i_dev
&& walk->start_cluster == first)
walk->device = 0;
spin_unlock(&fat_cache_lock);
}
 
 
void fat_cache_inval_dev(kdev_t device)
{
struct fat_cache *walk;
 
spin_lock(&fat_cache_lock);
for (walk = fat_cache; walk; walk = walk->next)
if (walk->device == device)
walk->device = 0;
spin_unlock(&fat_cache_lock);
}
 
 
int fat_get_cluster(struct inode *inode,int cluster)
{
int nr,count;
 
if (!(nr = MSDOS_I(inode)->i_start)) return 0;
if (!cluster) return nr;
count = 0;
for (fat_cache_lookup(inode,cluster,&count,&nr); count < cluster;
count++) {
if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
if (!nr) return 0;
}
fat_cache_add(inode,cluster,nr);
return nr;
}
 
int default_fat_bmap(struct inode *inode,int sector)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int cluster, offset, last_block;
 
if ((sbi->fat_bits != 32) &&
(inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
!MSDOS_I(inode)->i_start))) {
if (sector >= sbi->dir_entries >> sbi->dir_per_block_bits)
return 0;
return sector + sbi->dir_start;
}
last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
>> sb->s_blocksize_bits;
if (sector >= last_block)
return 0;
 
cluster = sector / sbi->cluster_size;
offset = sector % sbi->cluster_size;
if (!(cluster = fat_get_cluster(inode, cluster)))
return 0;
 
return (cluster - 2) * sbi->cluster_size + sbi->data_start + offset;
}
 
 
/* Free all clusters after the skip'th cluster. Doesn't use the cache,
because this way we get an additional sanity check. */
 
int fat_free(struct inode *inode,int skip)
{
int nr,last;
 
if (!(nr = MSDOS_I(inode)->i_start)) return 0;
last = 0;
while (skip--) {
last = nr;
if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
if (!nr) {
printk("fat_free: skipped EOF\n");
return -EIO;
}
}
if (last) {
fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
fat_cache_inval_inode(inode);
} else {
fat_cache_inval_inode(inode);
MSDOS_I(inode)->i_start = 0;
MSDOS_I(inode)->i_logstart = 0;
mark_inode_dirty(inode);
}
lock_fat(inode->i_sb);
while (nr != -1) {
if (!(nr = fat_access(inode->i_sb,nr,0))) {
fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
break;
}
if (MSDOS_SB(inode->i_sb)->free_clusters != -1) {
MSDOS_SB(inode->i_sb)->free_clusters++;
if (MSDOS_SB(inode->i_sb)->fat_bits == 32) {
fat_clusters_flush(inode->i_sb);
}
}
inode->i_blocks -= (1 << MSDOS_SB(inode->i_sb)->cluster_bits) / 512;
}
unlock_fat(inode->i_sb);
return 0;
}
/buffer.c
0,0 → 1,102
/*
* linux/fs/fat/buffer.c
*
*
*/
 
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/msdos_fs.h>
#include <linux/fat_cvf.h>
 
#if 0
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
 
struct buffer_head *fat_bread(struct super_block *sb, int block)
{
return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
}
struct buffer_head *fat_getblk(struct super_block *sb, int block)
{
return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
}
void fat_brelse (struct super_block *sb, struct buffer_head *bh)
{
if (bh)
MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
}
void fat_mark_buffer_dirty (
struct super_block *sb,
struct buffer_head *bh)
{
MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh);
}
void fat_set_uptodate (
struct super_block *sb,
struct buffer_head *bh,
int val)
{
MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
}
int fat_is_uptodate(struct super_block *sb, struct buffer_head *bh)
{
return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
}
void fat_ll_rw_block (
struct super_block *sb,
int opr,
int nbreq,
struct buffer_head *bh[32])
{
MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
}
 
struct buffer_head *default_fat_bread(struct super_block *sb, int block)
{
return sb_bread(sb, block);
}
 
struct buffer_head *default_fat_getblk(struct super_block *sb, int block)
{
return sb_getblk(sb, block);
}
 
void default_fat_brelse(struct super_block *sb, struct buffer_head *bh)
{
brelse (bh);
}
 
void default_fat_mark_buffer_dirty (
struct super_block *sb,
struct buffer_head *bh)
{
mark_buffer_dirty (bh);
}
 
void default_fat_set_uptodate (
struct super_block *sb,
struct buffer_head *bh,
int val)
{
mark_buffer_uptodate(bh, val);
}
 
int default_fat_is_uptodate (struct super_block *sb, struct buffer_head *bh)
{
return buffer_uptodate(bh);
}
 
void default_fat_ll_rw_block (
struct super_block *sb,
int opr,
int nbreq,
struct buffer_head *bh[32])
{
ll_rw_block(opr,nbreq,bh);
}
/misc.c
0,0 → 1,566
/*
* linux/fs/fat/misc.c
*
* Written 1992,1993 by Werner Almesberger
* 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
* and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
*/
 
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
 
#if 0
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
#define Printk(x) printk x
 
/* Well-known binary file extensions - of course there are many more */
 
static char ascii_extensions[] =
"TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */
"C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */
"F " "MAK" "INC" "BAS" /* programming languages */
"BAT" "SH " /* program code :) */
"INI" /* config files */
"PBM" "PGM" "DXF" /* graphics */
"TEX"; /* TeX */
 
 
/*
* fat_fs_panic reports a severe file system problem and sets the file system
* read-only. The file system can be made writable again by remounting it.
*/
 
void fat_fs_panic(struct super_block *s,const char *msg)
{
int not_ro;
 
not_ro = !(s->s_flags & MS_RDONLY);
if (not_ro) s->s_flags |= MS_RDONLY;
printk("Filesystem panic (dev %s).\n %s\n", kdevname(s->s_dev), msg);
if (not_ro)
printk(" File system has been set read-only\n");
}
 
 
/*
* fat_is_binary selects optional text conversion based on the conversion mode
* and the extension part of the file name.
*/
 
int fat_is_binary(char conversion,char *extension)
{
char *walk;
 
switch (conversion) {
case 'b':
return 1;
case 't':
return 0;
case 'a':
for (walk = ascii_extensions; *walk; walk += 3)
if (!strncmp(extension,walk,3)) return 0;
return 1; /* default binary conversion */
default:
printk("Invalid conversion mode - defaulting to "
"binary.\n");
return 1;
}
}
 
void lock_fat(struct super_block *sb)
{
down(&(MSDOS_SB(sb)->fat_lock));
}
 
void unlock_fat(struct super_block *sb)
{
up(&(MSDOS_SB(sb)->fat_lock));
}
 
/* Flushes the number of free clusters on FAT32 */
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
void fat_clusters_flush(struct super_block *sb)
{
struct buffer_head *bh;
struct fat_boot_fsinfo *fsinfo;
 
bh = fat_bread(sb, MSDOS_SB(sb)->fsinfo_sector);
if (bh == NULL) {
printk("FAT bread failed in fat_clusters_flush\n");
return;
}
 
fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
/* Sanity check */
if (!IS_FSINFO(fsinfo)) {
printk("FAT: Did not find valid FSINFO signature.\n"
"Found signature1 0x%x signature2 0x%x sector=%ld.\n",
CF_LE_L(fsinfo->signature1), CF_LE_L(fsinfo->signature2),
MSDOS_SB(sb)->fsinfo_sector);
return;
}
fsinfo->free_clusters = CF_LE_L(MSDOS_SB(sb)->free_clusters);
fsinfo->next_cluster = CF_LE_L(MSDOS_SB(sb)->prev_free);
fat_mark_buffer_dirty(sb, bh);
fat_brelse(sb, bh);
}
 
/*
* fat_add_cluster tries to allocate a new cluster and adds it to the
* file represented by inode.
*/
int fat_add_cluster(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
int count, nr, limit, last, curr, file_cluster;
int cluster_size = MSDOS_SB(sb)->cluster_size;
int res = -ENOSPC;
lock_fat(sb);
if (MSDOS_SB(sb)->free_clusters == 0) {
unlock_fat(sb);
return res;
}
limit = MSDOS_SB(sb)->clusters;
nr = limit; /* to keep GCC happy */
for (count = 0; count < limit; count++) {
nr = ((count + MSDOS_SB(sb)->prev_free) % limit) + 2;
if (fat_access(sb, nr, -1) == 0)
break;
}
if (count >= limit) {
MSDOS_SB(sb)->free_clusters = 0;
unlock_fat(sb);
return res;
}
MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit;
fat_access(sb, nr, EOF_FAT(sb));
if (MSDOS_SB(sb)->free_clusters != -1)
MSDOS_SB(sb)->free_clusters--;
if (MSDOS_SB(sb)->fat_bits == 32)
fat_clusters_flush(sb);
unlock_fat(sb);
/* We must locate the last cluster of the file to add this
new one (nr) to the end of the link list (the FAT).
Here file_cluster will be the number of the last cluster of the
file (before we add nr).
last is the corresponding cluster number on the disk. We will
use last to plug the nr cluster. We will use file_cluster to
update the cache.
*/
last = file_cluster = 0;
if ((curr = MSDOS_I(inode)->i_start) != 0) {
fat_cache_lookup(inode, INT_MAX, &last, &curr);
file_cluster = last;
while (curr && curr != -1){
file_cluster++;
if (!(curr = fat_access(sb, last = curr,-1))) {
fat_fs_panic(sb, "File without EOF");
return res;
}
}
}
if (last) {
fat_access(sb, last, nr);
fat_cache_add(inode, file_cluster, nr);
} else {
MSDOS_I(inode)->i_start = nr;
MSDOS_I(inode)->i_logstart = nr;
mark_inode_dirty(inode);
}
if (file_cluster
!= inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) {
printk ("file_cluster badly computed!!! %d <> %ld\n",
file_cluster,
inode->i_blocks / cluster_size / (sb->s_blocksize / 512));
fat_cache_inval_inode(inode);
}
inode->i_blocks += (1 << MSDOS_SB(sb)->cluster_bits) / 512;
 
return nr;
}
 
struct buffer_head *fat_extend_dir(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
int nr, sector, last_sector;
struct buffer_head *bh, *res = NULL;
int cluster_size = MSDOS_SB(sb)->cluster_size;
 
if (MSDOS_SB(sb)->fat_bits != 32) {
if (inode->i_ino == MSDOS_ROOT_INO)
return res;
}
 
nr = fat_add_cluster(inode);
if (nr < 0)
return res;
sector = MSDOS_SB(sb)->data_start + (nr - 2) * cluster_size;
last_sector = sector + cluster_size;
if (MSDOS_SB(sb)->cvf_format && MSDOS_SB(sb)->cvf_format->zero_out_cluster)
MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode, nr);
else {
for ( ; sector < last_sector; sector++) {
#ifdef DEBUG
printk("zeroing sector %d\n", sector);
#endif
if (!(bh = fat_getblk(sb, sector)))
printk("getblk failed\n");
else {
memset(bh->b_data, 0, sb->s_blocksize);
fat_set_uptodate(sb, bh, 1);
fat_mark_buffer_dirty(sb, bh);
if (!res)
res = bh;
else
fat_brelse(sb, bh);
}
}
}
if (inode->i_size & (sb->s_blocksize - 1)) {
fat_fs_panic(sb, "Odd directory size");
inode->i_size = (inode->i_size + sb->s_blocksize)
& ~(sb->s_blocksize - 1);
}
inode->i_size += 1 << MSDOS_SB(sb)->cluster_bits;
MSDOS_I(inode)->mmu_private += 1 << MSDOS_SB(sb)->cluster_bits;
mark_inode_dirty(inode);
 
return res;
}
 
/* Linear day numbers of the respective 1sts in non-leap years. */
 
static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
 
 
extern struct timezone sys_tz;
 
 
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 
int date_dos2unix(unsigned short time,unsigned short date)
{
int month,year,secs;
 
/* first subtract and mask after that... Otherwise, if
date == 0, bad things happen */
month = ((date >> 5) - 1) & 15;
year = date >> 9;
secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
month < 2 ? 1 : 0)+3653);
/* days since 1.1.70 plus 80's leap day */
secs += sys_tz.tz_minuteswest*60;
return secs;
}
 
 
/* Convert linear UNIX date to a MS-DOS time/date pair. */
 
void fat_date_unix2dos(int unix_date,unsigned short *time,
unsigned short *date)
{
int day,year,nl_day,month;
 
unix_date -= sys_tz.tz_minuteswest*60;
 
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
if (unix_date < 315532800)
unix_date = 315532800;
 
*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
(((unix_date/3600) % 24) << 11);
day = unix_date/86400-3652;
year = day/365;
if ((year+3)/4+365*year > day) year--;
day -= (year+3)/4+365*year;
if (day == 59 && !(year & 3)) {
nl_day = day;
month = 2;
}
else {
nl_day = (year & 3) || day <= 59 ? day : day-1;
for (month = 0; month < 12; month++)
if (day_n[month] > nl_day) break;
}
*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
}
 
 
/* Returns the inode number of the directory entry at offset pos. If bh is
non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
returned in bh.
AV. Most often we do it item-by-item. Makes sense to optimize.
AV. OK, there we go: if both bh and de are non-NULL we assume that we just
AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
AV. It's done in fat_get_entry() (inlined), here the slow case lives.
AV. Additionally, when we return -1 (i.e. reached the end of directory)
AV. we make bh NULL.
*/
 
int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
struct msdos_dir_entry **de, loff_t *i_pos)
{
struct super_block *sb = dir->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int sector;
loff_t offset;
 
while (1) {
offset = *pos;
PRINTK (("get_entry offset %d\n",offset));
if (*bh)
fat_brelse(sb, *bh);
*bh = NULL;
if ((sector = fat_bmap(dir,offset >> sb->s_blocksize_bits)) == -1)
return -1;
PRINTK (("get_entry sector %d %p\n",sector,*bh));
PRINTK (("get_entry sector apres brelse\n"));
if (!sector)
return -1; /* beyond EOF */
*pos += sizeof(struct msdos_dir_entry);
if (!(*bh = fat_bread(sb, sector))) {
printk("Directory sread (sector 0x%x) failed\n",sector);
continue;
}
PRINTK (("get_entry apres sread\n"));
 
offset &= sb->s_blocksize - 1;
*de = (struct msdos_dir_entry *) ((*bh)->b_data + offset);
*i_pos = ((loff_t)sector << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS);
 
return 0;
}
}
 
 
/*
* Now an ugly part: this set of directory scan routines works on clusters
* rather than on inodes and sectors. They are necessary to locate the '..'
* directory "inode". raw_scan_sector operates in four modes:
*
* name number ino action
* -------- -------- -------- -------------------------------------------------
* non-NULL - X Find an entry with that name
* NULL non-NULL non-NULL Find an entry whose data starts at *number
* NULL non-NULL NULL Count subdirectories in *number. (*)
* NULL NULL non-NULL Find an empty entry
*
* (*) The return code should be ignored. It DOES NOT indicate success or
* failure. *number has to be initialized to zero.
*
* - = not used, X = a value is returned unless NULL
*
* If res_bh is non-NULL, the buffer is not deallocated but returned to the
* caller on success. res_de is set accordingly.
*
* If cont is non-zero, raw_found continues with the entry after the one
* res_bh/res_de point to.
*/
 
 
#define RSS_NAME /* search for name */ \
done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
!(data[entry].attr & ATTR_VOLUME);
 
#define RSS_START /* search for start cluster */ \
done = !IS_FREE(data[entry].name) \
&& ( \
( \
(sbi->fat_bits != 32) ? 0 : (CF_LE_W(data[entry].starthi) << 16) \
) \
| CF_LE_W(data[entry].start) \
) == *number;
 
#define RSS_FREE /* search for free entry */ \
{ \
done = IS_FREE(data[entry].name); \
}
 
#define RSS_COUNT /* count subdirectories */ \
{ \
done = 0; \
if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
(*number)++; \
}
 
static int raw_scan_sector(struct super_block *sb, int sector,
const char *name, int *number, loff_t *i_pos,
struct buffer_head **res_bh,
struct msdos_dir_entry **res_de)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
struct buffer_head *bh;
struct msdos_dir_entry *data;
int entry,start,done;
 
if (!(bh = fat_bread(sb, sector)))
return -EIO;
data = (struct msdos_dir_entry *) bh->b_data;
for (entry = 0; entry < sbi->dir_per_block; entry++) {
/* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
if (name) {
RSS_NAME
} else {
if (!i_pos) RSS_COUNT
else {
if (number) RSS_START
else RSS_FREE
}
}
if (done) {
if (i_pos) {
*i_pos = ((loff_t)sector << sbi->dir_per_block_bits) + entry;
}
start = CF_LE_W(data[entry].start);
if (sbi->fat_bits == 32)
start |= (CF_LE_W(data[entry].starthi) << 16);
 
if (!res_bh)
fat_brelse(sb, bh);
else {
*res_bh = bh;
*res_de = &data[entry];
}
return start;
}
}
fat_brelse(sb, bh);
return -ENOENT;
}
 
 
/*
* raw_scan_root performs raw_scan_sector on the root directory until the
* requested entry is found or the end of the directory is reached.
*/
 
static int raw_scan_root(struct super_block *sb, const char *name,
int *number, loff_t *i_pos,
struct buffer_head **res_bh,
struct msdos_dir_entry **res_de)
{
int count,cluster;
 
for (count = 0;
count < MSDOS_SB(sb)->dir_entries / MSDOS_SB(sb)->dir_per_block;
count++) {
cluster = raw_scan_sector(sb, MSDOS_SB(sb)->dir_start + count,
name, number, i_pos, res_bh, res_de);
if (cluster >= 0)
return cluster;
}
return -ENOENT;
}
 
 
/*
* raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
* requested entry is found or the end of the directory is reached.
*/
 
static int raw_scan_nonroot(struct super_block *sb, int start, const char *name,
int *number, loff_t *i_pos,
struct buffer_head **res_bh,
struct msdos_dir_entry **res_de)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int count, cluster, sector;
 
#ifdef DEBUG
printk("raw_scan_nonroot: start=%d\n",start);
#endif
do {
for (count = 0; count < sbi->cluster_size; count++) {
sector = (start - 2) * sbi->cluster_size
+ count + sbi->data_start;
cluster = raw_scan_sector(sb, sector, name, number,
i_pos, res_bh, res_de);
if (cluster >= 0)
return cluster;
}
if (!(start = fat_access(sb,start,-1))) {
fat_fs_panic(sb,"FAT error");
break;
}
#ifdef DEBUG
printk("next start: %d\n",start);
#endif
}
while (start != -1);
return -ENOENT;
}
 
 
/*
* raw_scan performs raw_scan_sector on any sector.
*
* NOTE: raw_scan must not be used on a directory that is is the process of
* being created.
*/
 
static int raw_scan(struct super_block *sb, int start, const char *name,
loff_t *i_pos, struct buffer_head **res_bh,
struct msdos_dir_entry **res_de)
{
if (start)
return raw_scan_nonroot(sb,start,name,NULL,i_pos,res_bh,res_de);
else
return raw_scan_root(sb,name,NULL,i_pos,res_bh,res_de);
}
 
/*
* fat_subdirs counts the number of sub-directories of dir. It can be run
* on directories being created.
*/
int fat_subdirs(struct inode *dir)
{
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
int number;
 
number = 0;
if ((dir->i_ino == MSDOS_ROOT_INO) && (sbi->fat_bits != 32))
raw_scan_root(dir->i_sb, NULL, &number, NULL, NULL, NULL);
else {
if ((dir->i_ino != MSDOS_ROOT_INO) && !MSDOS_I(dir)->i_start)
return 0; /* in mkdir */
else {
raw_scan_nonroot(dir->i_sb, MSDOS_I(dir)->i_start,
NULL, &number, NULL, NULL, NULL);
}
}
return number;
}
 
 
/*
* Scans a directory for a given file (name points to its formatted name) or
* for an empty directory slot (name is NULL). Returns an error code or zero.
*/
 
int fat_scan(struct inode *dir, const char *name, struct buffer_head **res_bh,
struct msdos_dir_entry **res_de, loff_t *i_pos)
{
int res;
 
res = raw_scan(dir->i_sb, MSDOS_I(dir)->i_start, name, i_pos,
res_bh, res_de);
return (res < 0) ? res : 0;
}
/Makefile
0,0 → 1,17
#
# Makefile for the Linux fat filesystem support.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile.
 
O_TARGET := fat.o
 
export-objs := fatfs_syms.o
 
obj-y := buffer.o cache.o dir.o file.o inode.o misc.o cvf.o fatfs_syms.o
obj-m := $(O_TARGET)
 
include $(TOPDIR)/Rules.make

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.