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 |