summaryrefslogtreecommitdiffstats
path: root/fs/vfat/namei.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
commit59223edaa18759982db0a8aced0e77457d10c68e (patch)
tree89354903b01fa0a447bffeefe00df3044495db2e /fs/vfat/namei.c
parentdb7d4daea91e105e3859cf461d7e53b9b77454b2 (diff)
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs/vfat/namei.c')
-rw-r--r--fs/vfat/namei.c1007
1 files changed, 278 insertions, 729 deletions
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index f79ef1b44..d372ead15 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -49,24 +49,6 @@
# define CHECK_STACK check_stack(__FILE__, __LINE__)
#endif
-struct vfat_find_info {
- const char *name;
- int len;
- int new_filename;
- int found;
- int is_long;
- off_t offset;
- off_t short_offset;
- int long_slots;
- ino_t ino;
- int posix;
- int anycase;
-};
-
-void vfat_read_inode(struct inode *inode);
-static int vfat_valid_shortname(const char *,int, int, int);
-static int vfat_format_name(const char *, int, char *, int, int);
-static int vfat_valid_longname(const char *, int, int, int);
static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
static int vfat_hash(struct dentry *parent, struct qstr *qstr);
static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
@@ -100,9 +82,8 @@ static struct dentry_operations vfat_dentry_ops[4] = {
}
};
-void vfat_put_super(struct super_block *sb)
+static void vfat_put_super_callback(struct super_block *sb)
{
- fat_put_super(sb);
MOD_DEC_USE_COUNT;
}
@@ -115,18 +96,6 @@ static int vfat_revalidate(struct dentry *dentry, int flags)
return 0;
}
-static struct super_operations vfat_sops = {
- vfat_read_inode,
- fat_write_inode,
- fat_put_inode,
- fat_delete_inode,
- fat_notify_change,
- vfat_put_super,
- NULL, /* write_super */
- fat_statfs,
- NULL /* remount */
-};
-
static int simple_getbool(char *s, int *setval)
{
if (s) {
@@ -283,40 +252,6 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
return 1;
}
-struct super_block *vfat_read_super(struct super_block *sb,void *data,
- int silent)
-{
- struct super_block *res;
-
- MOD_INC_USE_COUNT;
-
- MSDOS_SB(sb)->options.isvfat = 1;
-
- sb->s_op = &vfat_sops;
- res = fat_read_super(sb, data, silent);
- if (res == NULL) {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
-
- if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
- MOD_DEC_USE_COUNT;
- } else {
- MSDOS_SB(sb)->options.dotsOK = 0;
- if (MSDOS_SB(sb)->options.posixfs) {
- MSDOS_SB(sb)->options.name_check = 's';
- }
- if (MSDOS_SB(sb)->options.name_check != 's') {
- sb->s_root->d_op = &vfat_dentry_ops[0];
- } else {
- sb->s_root->d_op = &vfat_dentry_ops[2];
- }
- }
-
- return res;
-}
-
#ifdef DEBUG
static void
@@ -394,26 +329,17 @@ static const char *reserved4_names[] = {
static char bad_chars[] = "*?<>|\":/\\";
static char replace_chars[] = "[];,+=";
-static int vfat_find(struct inode *dir,struct qstr* name,
- int new_filename,int is_dir,
- struct vfat_slot_info *sinfo_out);
-
/* Checks the validity of a long MS-DOS filename */
/* Returns negative number on error, 0 for a normal
* return, and 1 for . or .. */
-static int vfat_valid_longname(const char *name, int len, int dot_dirs,
- int xlate)
+static int vfat_valid_longname(const char *name, int len, int xlate)
{
const char **reserved, *walk;
unsigned char c;
int i, baselen;
if (IS_FREE(name)) return -EINVAL;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- return 1;
- }
if (len && name[len-1] == ' ') return -EINVAL;
if (len >= 256) return -EINVAL;
@@ -443,19 +369,13 @@ static int vfat_valid_longname(const char *name, int len, int dot_dirs,
return 0;
}
-static int vfat_valid_shortname(const char *name,int len,
- int dot_dirs, int utf8)
+static int vfat_valid_shortname(const char *name,int len,int utf8)
{
const char *walk;
unsigned char c;
int space;
int baselen;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- return 1;
- }
-
space = 1; /* disallow names starting with a dot */
c = 0;
for (walk = name; len && walk-name < 8;) {
@@ -499,34 +419,21 @@ static int vfat_find_form(struct inode *dir,char *name)
{
struct msdos_dir_entry *de;
struct buffer_head *bh = NULL;
- loff_t pos = 0;
+ int ino,res;
- while(fat_get_entry(dir, &pos, &bh, &de) >= 0) {
- if (de->attr == ATTR_EXT)
- continue;
- if (memcmp(de->name,name,MSDOS_NAME))
- continue;
- fat_brelse(dir->i_sb,bh);
- return 0;
- }
- fat_brelse(dir->i_sb,bh);
- return -ENOENT;
+ res=fat_scan(dir,name,&bh,&de,&ino);
+ fat_brelse(dir->i_sb, bh);
+ if (res<0)
+ return -ENOENT;
+ return 0;
}
-static int vfat_format_name(const char *name,int len,char *res,
- int dot_dirs,int utf8)
+static int vfat_format_name(const char *name,int len,char *res,int utf8)
{
char *walk;
unsigned char c;
int space;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- memset(res+1,' ',10);
- while (len--) *res++ = '.';
- return 0;
- }
-
space = 1; /* disallow names starting with a dot */
for (walk = res; len-- && (c=*name++)!='.' ; walk++) {
if (walk-res == 8) return -EINVAL;
@@ -588,7 +495,7 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) {
if (i == len) {
if (vfat_format_name(msdos_name,
- len, name_res, 1, utf8) < 0)
+ len, name_res, utf8) < 0)
break;
PRINTK3(("vfat_create_shortname 1\n"));
if (vfat_find_form(dir, name_res) < 0)
@@ -732,60 +639,6 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
return 0;
}
-static loff_t vfat_find_free_slots(struct inode *dir,int slots)
-{
- struct super_block *sb = dir->i_sb;
- loff_t offset, curr;
- struct msdos_dir_entry *de;
- struct buffer_head *bh;
- struct inode *inode;
- int ino;
- int row;
- int done;
- int res;
- int added;
-
- PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots));
- offset = curr = 0;
- bh = NULL;
- row = 0;
- ino = fat_get_entry(dir,&curr,&bh,&de);
-
- for (added = 0; added < 2; added++) {
- while (ino > -1) {
- done = IS_FREE(de->name);
- if (done) {
- inode = iget(sb,ino);
- if (inode) {
- /* Directory slots of busy deleted files aren't available yet. */
- done = !MSDOS_I(inode)->i_busy;
- /* PRINTK3(("inode %d still busy\n", ino)); */
- iput(inode);
- }
- }
- if (done) {
- row++;
- if (row == slots) {
- fat_brelse(sb, bh);
- /* printk("----- Free offset at %d\n", offset); */
- return offset;
- }
- } else {
- row = 0;
- offset = curr;
- }
- ino = fat_get_entry(dir,&curr,&bh,&de);
- }
-
- if ((dir->i_ino == MSDOS_ROOT_INO) &&
- (MSDOS_SB(sb)->fat_bits != 32))
- return -ENOSPC;
- if ((res = fat_add_cluster(dir)) < 0) return res;
- ino = fat_get_entry(dir,&curr,&bh,&de);
- }
- return -ENOSPC;
-}
-
/* Translate a string, including coded sequences into Unicode */
static int
xlate_to_uni(const char *name, int len, char *outname, int *outlen,
@@ -889,249 +742,129 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots));
for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
- int end, j;
-
- PRINTK3(("vfat_fill_long_slots 4\n"));
ps->id = slot;
ps->attr = ATTR_EXT;
ps->reserved = 0;
ps->alias_checksum = cksum;
ps->start = 0;
- PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
offset = (slot - 1) * 26;
ip = &uniname[offset];
- j = offset;
- end = 0;
- for (i = 0; i < 10; i += 2) {
- ps->name0_4[i] = *ip++;
- ps->name0_4[i+1] = *ip++;
- }
- PRINTK3(("vfat_fill_long_slots 6\n"));
- for (i = 0; i < 12; i += 2) {
- ps->name5_10[i] = *ip++;
- ps->name5_10[i+1] = *ip++;
- }
- PRINTK3(("vfat_fill_long_slots 7\n"));
- for (i = 0; i < 4; i += 2) {
- ps->name11_12[i] = *ip++;
- ps->name11_12[i+1] = *ip++;
- }
+ memcpy(ps->name0_4, ip, 10);
+ memcpy(ps->name5_10, ip+10, 12);
+ memcpy(ps->name11_12, ip+22, 4);
}
- PRINTK3(("vfat_fill_long_slots 8\n"));
ds[0].id |= 0x40;
de = (struct msdos_dir_entry *) ps;
PRINTK3(("vfat_fill_long_slots 9\n"));
strncpy(de->name, msdos_name, MSDOS_NAME);
+ (*slots)++;
free_page(page);
return 0;
}
-
+
+/* We can't get "." or ".." here - VFS takes care of those cases */
+
static int vfat_build_slots(struct inode *dir,const char *name,int len,
- struct msdos_dir_slot *ds, int *slots, int *is_long)
+ struct msdos_dir_slot *ds, int *slots)
{
struct msdos_dir_entry *de;
char msdos_name[MSDOS_NAME];
int res, xlate, utf8;
struct nls_table *nls;
- PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
de = (struct msdos_dir_entry *) ds;
xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
nls = MSDOS_SB(dir->i_sb)->nls_io;
*slots = 1;
- *is_long = 0;
- if (len == 1 && name[0] == '.') {
- strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
- } else if (len == 2 && name[0] == '.' && name[1] == '.') {
- strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
- } else {
- PRINTK3(("vfat_build_slots 4\n"));
- res = vfat_valid_longname(name, len, 1, xlate);
- if (res < 0) {
- return res;
- }
- res = vfat_valid_shortname(name, len, 1, utf8);
- if (res > -1) {
- PRINTK3(("vfat_build_slots 5a\n"));
- res = vfat_format_name(name, len, de->name, 1, utf8);
- PRINTK3(("vfat_build_slots 5b\n"));
- } else {
- res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
- if (res < 0) {
- return res;
- }
-
- *is_long = 1;
-
- return vfat_fill_long_slots(ds, name, len, msdos_name,
- slots, xlate, utf8, nls);
- }
- }
- return 0;
-}
-
-static int vfat_readdir_cb(
- filldir_t filldir,
- void * buf,
- const char * name,
- int name_len,
- int is_long,
- off_t offset,
- off_t short_offset,
- int long_slots,
- ino_t ino)
-{
- struct vfat_find_info *vf = (struct vfat_find_info *) buf;
- const char *s1, *s2;
- int i;
-
-#ifdef DEBUG
- if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
- vf->name, vf->len, name, name_len);
-#endif
-
- if (vf->len != name_len) {
+ res = vfat_valid_longname(name, len, xlate);
+ if (res < 0)
+ return res;
+ if (vfat_valid_shortname(name, len, utf8) >= 0) {
+ vfat_format_name(name, len, de->name, utf8);
return 0;
}
-
- s1 = name; s2 = vf->name;
- for (i = 0; i < name_len; i++) {
- if (vf->anycase || (vf->new_filename && !vf->posix)) {
- if (tolower(*s1) != tolower(*s2))
- return 0;
- } else {
- if (*s1 != *s2)
- return 0;
- }
- s1++; s2++;
- }
- vf->found = 1;
- vf->is_long = is_long;
- vf->offset = (offset == 2) ? 0 : offset;
- vf->short_offset = (short_offset == 2) ? 0 : short_offset;
- vf->long_slots = long_slots;
- vf->ino = ino;
- return -1;
+ res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
+ if (res < 0)
+ return res;
+ return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate,
+ utf8, nls);
}
-static int vfat_find(struct inode *dir,struct qstr* qname,
- int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
+static int vfat_add_entry(struct inode *dir,struct qstr* qname,
+ int is_dir,struct vfat_slot_info *sinfo_out,
+ struct buffer_head **bh, struct msdos_dir_entry **de)
{
struct super_block *sb = dir->i_sb;
- struct vfat_find_info vf;
- struct file fil;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
struct msdos_dir_slot *ps;
loff_t offset;
struct msdos_dir_slot *ds;
- int is_long;
int slots, slot;
int res;
-
- PRINTK2(("Entering vfat_find\n"));
+ struct msdos_dir_entry *de1;
+ struct buffer_head *bh1;
+ int ino;
+ int len;
+ loff_t dummy;
ds = (struct msdos_dir_slot *)
kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
if (ds == NULL) return -ENOMEM;
- fil.f_pos = 0;
- vf.name = qname->name;
- vf.len = qname->len;
- while (vf.len && vf.name[vf.len-1] == '.') {
- vf.len--;
- }
- vf.new_filename = new_filename;
- vf.found = 0;
- vf.posix = MSDOS_SB(sb)->options.posixfs;
- vf.anycase = (MSDOS_SB(sb)->options.name_check != 's');
- res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,1,0);
- PRINTK3(("vfat_find: Debug 1\n"));
- if (res < 0) goto cleanup;
- if (vf.found) {
- if (new_filename) {
- res = -EEXIST;
- goto cleanup;
- }
- sinfo_out->longname_offset = vf.offset;
- sinfo_out->shortname_offset = vf.short_offset;
- sinfo_out->is_long = vf.is_long;
- sinfo_out->long_slots = vf.long_slots;
- sinfo_out->total_slots = vf.long_slots + 1;
- sinfo_out->ino = vf.ino;
-
- PRINTK3(("vfat_find: Debug 2\n"));
- res = 0;
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's') ||
+ !MSDOS_SB(sb)->options.posixfs,
+ &dummy, &dummy);
+ if (res > 0) /* found */
+ res = -EEXIST;
+ if (res)
goto cleanup;
- }
- PRINTK3(("vfat_find: Debug 3\n"));
- if (!new_filename) {
- res = -ENOENT;
- goto cleanup;
- }
-
- res = vfat_build_slots(dir, qname->name, vf.len, ds,
- &slots, &is_long);
- /* Here we either have is_long and slots>=0 or slots==1 */
+ res = vfat_build_slots(dir, qname->name, len, ds, &slots);
if (res < 0) goto cleanup;
- de = (struct msdos_dir_entry *) ds;
-
- bh = NULL;
-
- PRINTK3(("vfat_find: create file 1\n"));
- if (is_long) slots++;
- offset = vfat_find_free_slots(dir, slots);
+ offset = fat_add_entries(dir, slots, &bh1, &de1, &ino);
if (offset < 0) {
res = offset;
goto cleanup;
}
+ fat_brelse(sb, bh1);
- PRINTK3(("vfat_find: create file 2\n"));
/* Now create the new entry */
- bh = NULL;
+ *bh = NULL;
for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
- PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
- sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
- if (sinfo_out->ino < 0) {
- PRINTK3(("vfat_find: problem\n"));
- res = sinfo_out->ino;
+ if (fat_get_entry(dir,&offset,bh,de, &sinfo_out->ino) < 0) {
+ res = -EIO;
goto cleanup;
}
- memcpy(de, ps, sizeof(struct msdos_dir_slot));
- fat_mark_buffer_dirty(sb, bh, 1);
+ memcpy(*de, ps, sizeof(struct msdos_dir_slot));
+ fat_mark_buffer_dirty(sb, *bh, 1);
}
- PRINTK3(("vfat_find: create file 4\n"));
dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
mark_inode_dirty(dir);
- PRINTK3(("vfat_find: create file 5\n"));
+ fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+ (*de)->ctime_ms = 0;
+ (*de)->ctime = (*de)->time;
+ (*de)->adate = (*de)->cdate = (*de)->date;
+ (*de)->start = 0;
+ (*de)->starthi = 0;
+ (*de)->size = 0;
+ (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ (*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
- fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
- de->ctime_ms = 0;
- de->ctime = de->time;
- de->adate = de->cdate = de->date;
- de->start = 0;
- de->starthi = 0;
- de->size = 0;
- de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
- de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
-
- fat_mark_buffer_dirty(sb, bh, 1);
- fat_brelse(sb, bh);
+ fat_mark_buffer_dirty(sb, *bh, 1);
/* slots can't be less than 1 */
- sinfo_out->is_long = (slots > 1);
sinfo_out->long_slots = slots - 1;
- sinfo_out->total_slots = slots;
- sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
res = 0;
@@ -1140,6 +873,29 @@ cleanup:
return res;
}
+static int vfat_find(struct inode *dir,struct qstr* qname,
+ struct vfat_slot_info *sinfo, struct buffer_head **last_bh,
+ struct msdos_dir_entry **last_de)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset;
+ int res,len;
+
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's'),
+ &offset,&sinfo->longname_offset);
+ if (res>0) {
+ sinfo->long_slots = res-1;
+ if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->ino)>=0)
+ return 0;
+ res = -EIO;
+ }
+ return res ? res : -ENOENT;
+}
+
/* Find a hashed dentry for inode; NULL if there are none */
static struct dentry *find_alias(struct inode *inode)
{
@@ -1162,8 +918,10 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
{
int res;
struct vfat_slot_info sinfo;
- struct inode *result;
+ struct inode *inode;
struct dentry *alias;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
int table;
PRINTK2(("vfat_lookup: name=%s, len=%d\n",
@@ -1172,317 +930,160 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
dentry->d_op = &vfat_dentry_ops[table];
- result = NULL;
- if ((res = vfat_find(dir,&dentry->d_name,0,0,&sinfo)) < 0) {
- result = NULL;
- table++;
- goto error;
- }
- PRINTK3(("vfat_lookup 4.5\n"));
- if (!(result = iget(dir->i_sb,sinfo.ino)))
- return ERR_PTR(-EACCES);
- PRINTK3(("vfat_lookup 5\n"));
- if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
- iput(result);
- result = NULL;
+ inode = NULL;
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0) {
table++;
goto error;
}
- alias = find_alias(result);
+ inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res);
+ fat_brelse(dir->i_sb, bh);
+ if (res)
+ return ERR_PTR(res);
+ alias = find_alias(inode);
if (alias) {
if (d_invalidate(alias)==0)
dput(alias);
else {
- iput(result);
+ iput(inode);
return alias;
}
+
}
- PRINTK3(("vfat_lookup 6\n"));
error:
dentry->d_op = &vfat_dentry_ops[table];
dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_add(dentry,result);
- return 0;
+ d_add(dentry,inode);
+ return NULL;
}
-
-static int vfat_create_entry(struct inode *dir,struct qstr* qname,
- int is_dir, struct inode **result)
+int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
{
struct super_block *sb = dir->i_sb;
- int res,ino;
- loff_t offset;
- struct buffer_head *bh;
+ struct inode *inode = NULL;
+ struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
struct vfat_slot_info sinfo;
+ int res;
- *result=0;
- PRINTK1(("vfat_create_entry: Entering\n"));
- res = vfat_find(dir, qname, 1, is_dir, &sinfo);
- if (res < 0) {
+ res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);
+ if (res < 0)
return res;
- }
-
- offset = sinfo.shortname_offset;
-
- PRINTK3(("vfat_create_entry 2\n"));
- bh = NULL;
- ino = fat_get_entry(dir, &offset, &bh, &de);
- if (ino < 0) {
- PRINTK3(("vfat_mkdir problem\n"));
- if (bh)
- fat_brelse(sb, bh);
- return ino;
- }
- PRINTK3(("vfat_create_entry 3\n"));
-
- if ((*result = iget(dir->i_sb,ino)) != NULL)
- vfat_read_inode(*result);
+ inode = fat_build_inode(sb, de, sinfo.ino, &res);
fat_brelse(sb, bh);
- if (!*result)
- return -EIO;
- (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
- CURRENT_TIME;
- mark_inode_dirty(*result);
- (*result)->i_version = ++event;
+ if (!inode)
+ return res;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
dir->i_version = event;
-
- return 0;
-}
-
-int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
-{
- int res;
- struct inode *result;
-
- result=NULL;
- fat_lock_creation();
- res = vfat_create_entry(dir,&dentry->d_name,0,&result);
- fat_unlock_creation();
- if (res < 0) {
- PRINTK3(("vfat_create: unable to get new entry\n"));
- } else {
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_instantiate(dentry,result);
- }
- return res;
-}
-
-static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
- struct buffer_head *bh,
- struct msdos_dir_entry *de,int ino,const char *name, int isdot)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *dot;
-
- PRINTK2(("vfat_create_a_dotdir: Entering\n"));
-
- /*
- * XXX all times should be set by caller upon successful completion.
- */
- dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- mark_inode_dirty(dir);
- memcpy(de->name,name,MSDOS_NAME);
- de->lcase = 0;
- de->attr = ATTR_DIR;
- de->start = 0;
- de->starthi = 0;
- fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
- de->ctime_ms = 0;
- de->ctime = de->time;
- de->adate = de->cdate = de->date;
- de->size = 0;
- fat_mark_buffer_dirty(sb, bh, 1);
- dot = iget(dir->i_sb,ino);
- if (!dot)
- return -EIO;
- vfat_read_inode(dot);
- dot->i_mtime = dot->i_atime = CURRENT_TIME;
- mark_inode_dirty(dot);
- if (isdot) {
- dot->i_size = dir->i_size;
- MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
- MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
- dot->i_nlink = dir->i_nlink;
- } else {
- dot->i_size = parent->i_size;
- MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
- MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart;
- dot->i_nlink = parent->i_nlink;
- }
-
- iput(dot);
-
- PRINTK3(("vfat_create_a_dotdir 2\n"));
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry,inode);
return 0;
}
static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
{
struct super_block *sb = dir->i_sb;
- int res;
struct buffer_head *bh;
struct msdos_dir_entry *de;
- loff_t offset;
-
- PRINTK2(("vfat_create_dotdirs: Entering\n"));
- if ((res = fat_add_cluster(dir)) < 0) return res;
-
- PRINTK3(("vfat_create_dotdirs 2\n"));
- offset = 0;
- bh = NULL;
- if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
-
- PRINTK3(("vfat_create_dotdirs 3\n"));
- res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
- PRINTK3(("vfat_create_dotdirs 4\n"));
- if (res < 0) {
- fat_brelse(sb, bh);
- return res;
- }
- PRINTK3(("vfat_create_dotdirs 5\n"));
-
- if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
- fat_brelse(sb, bh);
- return res;
- }
- PRINTK3(("vfat_create_dotdirs 6\n"));
-
- res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
- PRINTK3(("vfat_create_dotdirs 7\n"));
+ __u16 date, time;
+
+ if ((bh = fat_add_cluster1(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].ctime = de[0].time = de[1].ctime = de[1].time = CT_LE_W(time);
+ de[0].adate = de[0].cdate = de[0].date = de[1].adate =
+ de[1].cdate = de[1].date = 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, 1);
fat_brelse(sb, bh);
+ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
- return res;
-}
-
-/***** See if directory is empty */
-static int vfat_empty(struct inode *dir)
-{
- struct super_block *sb = dir->i_sb;
- loff_t pos;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
-
- if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
- pos = 0;
- bh = NULL;
- while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
- /* Skip extended filename 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)) {
- fat_brelse(sb, bh);
- return -ENOTEMPTY;
- }
- }
- if (bh)
- fat_brelse(sb, bh);
- }
return 0;
}
-static void vfat_free_ino(struct inode *dir,struct buffer_head *bh,
- struct msdos_dir_entry *de,struct inode* victim)
+static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
+ struct buffer_head *bh, struct msdos_dir_entry *de)
{
struct super_block *sb = dir->i_sb;
- victim->i_nlink = 0;
- victim->i_mtime = dir->i_mtime = CURRENT_TIME;
- victim->i_atime = dir->i_atime = CURRENT_TIME;
+ loff_t offset;
+ int i,ino;
+
+ /* remove the shortname */
+ dir->i_mtime = CURRENT_TIME;
+ dir->i_atime = CURRENT_TIME;
dir->i_version = ++event;
- MSDOS_I(victim)->i_busy = 1;
mark_inode_dirty(dir);
- mark_inode_dirty(victim);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
-}
-
-static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
- struct inode* victim)
-{
- struct super_block *sb = dir->i_sb;
- loff_t offset;
- struct buffer_head *bh=NULL;
- struct msdos_dir_entry *de;
- int res, i;
-
- /* remove the shortname */
- offset = sinfo->shortname_offset;
- res = fat_get_entry(dir, &offset, &bh, &de);
- if (res < 0) return res;
- vfat_free_ino(dir,bh,de,victim);
/* remove the longname */
- offset = sinfo->longname_offset;
+ offset = sinfo->longname_offset; de = NULL;
for (i = sinfo->long_slots; i > 0; --i) {
- if (fat_get_entry(dir, &offset, &bh, &de) < 0)
+ if (fat_get_entry(dir, &offset, &bh, &de, &ino) < 0)
continue;
de->name[0] = DELETED_FLAG;
de->attr = 0;
fat_mark_buffer_dirty(sb, bh, 1);
}
if (bh) fat_brelse(sb, bh);
- return 0;
}
-static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
+int vfat_rmdir(struct inode *dir,struct dentry* dentry)
{
int res;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
- PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry));
- res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
-
- if (res >= 0 && sinfo.total_slots > 0) {
- if (!list_empty(&dentry->d_hash))
- return -EBUSY;
- res = vfat_empty(dentry->d_inode);
- if (res)
- return res;
-
- res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
- if (res >= 0) {
- dir->i_nlink--;
- res = 0;
- }
- }
- return res;
-}
+ if (!list_empty(&dentry->d_hash))
+ return -EBUSY;
-/***** Remove a directory */
-int vfat_rmdir(struct inode *dir,struct dentry* dentry)
-{
- int res;
- PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
+ res = fat_dir_empty(dentry->d_inode);
+ if (res)
+ return res;
- res = -EBUSY;
- if (list_empty(&dentry->d_hash)) {
- res = vfat_rmdirx(dir, dentry);
- /* If that went OK all aliases are already dropped */
- }
- return res;
+ res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);
+ if (res<0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ dir->i_nlink--;
+ return 0;
}
-static int vfat_unlinkx(
- struct inode *dir,
- struct dentry* dentry,
- int nospc) /* Flag special file ? */
+int vfat_unlink(struct inode *dir, struct dentry* dentry)
{
int res;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
- PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
-
- if (res >= 0 && sinfo.total_slots > 0) {
- if (!S_ISREG(dentry->d_inode->i_mode) && nospc) {
- return -EPERM;
- }
- res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
- if (res > 0) {
- res = 0;
- }
- }
+ PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name));
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ d_delete(dentry);
return res;
}
@@ -1490,64 +1091,46 @@ static int vfat_unlinkx(
int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
{
- struct inode *inode;
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
int res;
- PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- fat_lock_creation();
- if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) {
- fat_unlock_creation();
+ res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);
+ if (res < 0)
return res;
- }
-
+ inode = fat_build_inode(sb, de, sinfo.ino, &res);
+ if (!inode)
+ goto out;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
+ dir->i_version = event;
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
res = vfat_create_dotdirs(inode, dir);
if (res < 0)
goto mkdir_failed;
- fat_unlock_creation();
dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry,inode);
+out:
+ fat_brelse(sb, bh);
return res;
mkdir_failed:
- fat_unlock_creation();
- if (vfat_find(dir,&dentry->d_name,0,0,&sinfo) < 0)
- goto mkdir_panic;
- if (vfat_remove_entry(dir, &sinfo, inode) < 0)
- goto mkdir_panic;
+ inode->i_nlink = 0;
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_atime = CURRENT_TIME;
+ fat_detach(inode);
+ mark_inode_dirty(inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
iput(inode);
dir->i_nlink--;
return res;
-
-mkdir_panic:
- dir->i_version = ++event;
- fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
- return res;
-}
-
-/***** Unlink, as called for msdosfs */
-int vfat_unlink(struct inode *dir,struct dentry* dentry)
-{
- int res;
-
- PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- res = vfat_unlinkx (dir,dentry,1);
- if (res >= 0)
- d_delete(dentry);
- return res;
-}
-
-/***** Unlink, as called for uvfatfs */
-int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry)
-{
- int res;
-
- res = vfat_unlinkx (dir,dentry,0);
- iput(dir);
- return res;
}
int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
@@ -1555,148 +1138,86 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *old_bh,*new_bh,*dotdot_bh;
- struct msdos_dir_entry *old_de,*dotdot_de;
- loff_t old_offset,new_offset,old_longname_offset;
- int old_slots,old_ino,new_ino,dotdot_ino;
- struct inode *old_inode, *new_inode, *dotdot_inode;
- int res, is_dir, i;
- int locked = 0;
- struct vfat_slot_info sinfo;
+ struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+ int dotdot_ino;
+ struct inode *old_inode, *new_inode;
+ int res, is_dir;
+ struct vfat_slot_info old_sinfo,sinfo;
- PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
- old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino,
- new_dentry, new_dentry->d_inode,
- new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0));
-
- old_bh = new_bh = NULL;
- old_inode = new_inode = NULL;
- res = vfat_find(old_dir,&old_dentry->d_name,0,0,&sinfo);
+ old_bh = new_bh = dotdot_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);
PRINTK3(("vfat_rename 2\n"));
if (res < 0) goto rename_done;
- old_slots = sinfo.total_slots;
- old_longname_offset = sinfo.longname_offset;
- old_offset = sinfo.shortname_offset;
- old_ino = sinfo.ino;
- res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
- PRINTK3(("vfat_rename 3\n"));
- if (res < 0) goto rename_done;
-
- res = -ENOENT;
- old_inode = old_dentry->d_inode;
is_dir = S_ISDIR(old_inode->i_mode);
- fat_lock_creation(); locked = 1;
+ if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+ &dotdot_de,&dotdot_ino)) < 0)
+ goto rename_done;
if (new_dentry->d_inode) {
- /*
- * OK, we have to remove the target. We should do it so
- * that nobody might go and find it negative. Actually we
- * should give warranties wrt preserving target over the
- * possible crash, but that's another story. We can't
- * get here with the target unhashed, so the directory entry
- * must exist.
- */
-
- new_inode = new_dentry->d_inode;
- res = vfat_find(new_dir,&new_dentry->d_name,0,is_dir,&sinfo);
- if (res < 0 || new_inode->i_ino != sinfo.ino) {
+ res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,
+ &new_de);
+ if (res < 0 || MSDOS_I(new_inode)->i_location != sinfo.ino) {
/* WTF??? Cry and fail. */
printk(KERN_WARNING "vfat_rename: fs corrupted\n");
goto rename_done;
}
if (is_dir) {
- res = vfat_empty(new_inode);
+ res = fat_dir_empty(new_inode);
if (res)
goto rename_done;
}
- res = vfat_remove_entry(new_dir,&sinfo,new_inode);
- if (res)
- goto rename_done;
-
- if (is_dir)
- new_dir->i_nlink--;
+ fat_detach(new_inode);
+ } else {
+ res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,
+ &new_bh,&new_de);
+ if (res < 0) goto rename_done;
}
- /* Serious lossage here. FAT uses braindead inode numbers scheme,
- * so we can't simply cannibalize the entry. It means that we have
- * no warranties that crash here will not make target disappear
- * after reboot. Lose, lose. Nothing to do with that until we'll
- * separate the functions of i_ino: it serves both as a search key
- * in icache and as a part of stat output. It would kill all the
- * 'busy' stuff on the spot. Later.
- */
-
- res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo);
-
- if (res < 0) goto rename_done;
-
- new_offset = sinfo.shortname_offset;
- new_ino = sinfo.ino;
-
- /* XXX: take care of other owners */
+ new_dir->i_version = ++event;
- remove_inode_hash(old_inode);
- fat_cache_inval_inode(old_inode);
- old_inode->i_ino = new_ino;
- old_inode->i_version = ++event;
- insert_inode_hash(old_inode);
+ /* releases old_bh */
+ vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);
+ old_bh=NULL;
+ fat_detach(old_inode);
+ fat_attach(old_inode, sinfo.ino);
mark_inode_dirty(old_inode);
old_dir->i_version = ++event;
- new_dir->i_version = ++event;
-
- /* remove the old entry */
- for (i = old_slots; i > 0; --i) {
- res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
- if (res < 0) {
- printk("vfat_rename: problem 1\n");
- continue;
- }
- old_de->name[0] = DELETED_FLAG;
- old_de->attr = 0;
- fat_mark_buffer_dirty(sb, old_bh, 1);
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime=CURRENT_TIME;
}
if (is_dir) {
- if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
- &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
- if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
- fat_brelse(sb, dotdot_bh);
- res = -EIO;
- goto rename_done;
- }
- MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
- MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
- dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
- dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
- mark_inode_dirty(dotdot_inode);
+ int start = MSDOS_I(new_dir)->i_logstart;
+ dotdot_de->start = CT_LE_W(start);
+ dotdot_de->starthi = CT_LE_W(start>>16);
fat_mark_buffer_dirty(sb, dotdot_bh, 1);
old_dir->i_nlink--;
- new_dir->i_nlink++;
- /* no need to mark them dirty */
- dotdot_inode->i_nlink = new_dir->i_nlink;
- iput(dotdot_inode);
- fat_brelse(sb, dotdot_bh);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ } else {
+ new_dir->i_nlink++;
+ mark_inode_dirty(new_dir);
+ }
}
- if (res >= 0)
- res = 0;
-
rename_done:
- if (locked)
- fat_unlock_creation();
- if (old_bh)
- fat_brelse(sb, old_bh);
- if (new_bh)
- fat_brelse(sb, new_bh);
+ fat_brelse(sb, dotdot_bh);
+ fat_brelse(sb, old_bh);
+ fat_brelse(sb, new_bh);
return res;
}
-
/* Public inode operations for the VFAT fs */
struct inode_operations vfat_dir_inode_operations = {
&fat_dir_operations, /* default directory file-ops */
@@ -1718,10 +1239,38 @@ struct inode_operations vfat_dir_inode_operations = {
NULL /* permission */
};
-
-void vfat_read_inode(struct inode *inode)
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+ int silent)
{
- fat_read_inode(inode, &vfat_dir_inode_operations);
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ MSDOS_SB(sb)->options.isvfat = 1;
+
+ res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations);
+ if (res == NULL) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
+ MOD_DEC_USE_COUNT;
+ } else {
+ MSDOS_SB(sb)->put_super_callback=vfat_put_super_callback;
+ MSDOS_SB(sb)->options.dotsOK = 0;
+ if (MSDOS_SB(sb)->options.posixfs) {
+ MSDOS_SB(sb)->options.name_check = 's';
+ }
+ if (MSDOS_SB(sb)->options.name_check != 's') {
+ sb->s_root->d_op = &vfat_dentry_ops[0];
+ } else {
+ sb->s_root->d_op = &vfat_dentry_ops[2];
+ }
+ }
+
+ return res;
}
#ifdef MODULE