summaryrefslogtreecommitdiffstats
path: root/fs/fat/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/dir.c')
-rw-r--r--fs/fat/dir.c571
1 files changed, 365 insertions, 206 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index b6d6fb405..594baa0e2 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -9,7 +9,7 @@
*
* VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
* Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
- * Plugged buffer overrun in readdir(). AV
+ * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
*/
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
@@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/dirent.h>
#include <linux/mm.h>
+#include <linux/ctype.h>
#include <asm/uaccess.h>
@@ -118,224 +119,348 @@ static void dump_de(struct msdos_dir_entry *de)
printk("]\n");
}
#endif
-int fat_readdirx(
- struct inode *inode,
- struct file *filp,
- void *dirent,
- fat_filldir_t fat_filldir,
- filldir_t filldir,
- int shortnames,
- int longnames,
- int both)
+static int memicmp(const char *s1, const char *s2, int len) {
+ while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1;
+ return 0;
+}
+
+/*
+ * 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;
int ino,i,i2,last;
char c;
- struct buffer_head *bh;
+ struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
- unsigned long oldpos = filp->f_pos;
- unsigned long spos;
- int is_long;
- char longname[275];
- unsigned char long_len = 0; /* Make compiler warning go away */
- unsigned char alias_checksum = 0; /* Make compiler warning go away */
- unsigned char long_slots = 0;
+ loff_t cpos = 0;
+ char bufname[14];
+ unsigned char long_slots;
int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
int utf8 = MSDOS_SB(sb)->options.utf8;
unsigned char *unicode = NULL;
struct nls_table *nls = MSDOS_SB(sb)->nls_io;
-
-/* Fake . and .. for the root directory. */
- if (inode->i_ino == MSDOS_ROOT_INO) {
- while (oldpos < 2) {
- if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
- return 0;
- oldpos++;
- filp->f_pos++;
- }
- if (oldpos == 2)
- filp->f_pos = 0;
- }
- if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
- return -ENOENT;
-
- bh = NULL;
- longname[0] = longname[1] = 0;
- is_long = 0;
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
- while (ino > -1) {
-#if 0
- dump_de(de);
-#endif
- /* Check for long filename entry */
- if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
- is_long = 0;
- oldpos = filp->f_pos;
- } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
- int get_new_entry;
+ int res = 0;
+
+ while(1) {
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -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;
int offset;
unsigned char id;
unsigned char slot;
- unsigned char slots = 0;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
if (!unicode) {
unicode = (unsigned char *)
__get_free_page(GFP_KERNEL);
- if (!unicode)
+ if (!unicode) {
+ fat_brelse(sb, bh);
return -ENOMEM;
+ }
}
-
+parse_long:
+ slots = 0;
offset = 0;
ds = (struct msdos_dir_slot *) de;
id = ds->id;
- if (id & 0x40) {
- slots = id & ~0x40;
- /*
- * Dirty, but not dirtier than the original,
- * and plugs the hole.
- */
- if (slots > 20)
- slots = 0;
- else {
- long_slots = slots;
- is_long = 1;
- alias_checksum = ds->alias_checksum;
- }
- }
+ if (!(id & 0x40))
+ continue;
+ slots = id & ~0x40;
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
+ continue;
+ long_slots = slots;
+ alias_checksum = ds->alias_checksum;
- get_new_entry = 1;
slot = slots;
- while (slot > 0) {
- PRINTK(("1. get_new_entry: %d\n", get_new_entry));
- if (ds->attr != ATTR_EXT) {
- is_long = 0;
- get_new_entry = 0;
- break;
- }
- if ((ds->id & ~0x40) != slot) {
- is_long = 0;
- break;
- }
- if (ds->alias_checksum != alias_checksum) {
- is_long = 0;
- break;
- }
+ while (1) {
slot--;
offset = slot * 26;
- PRINTK(("2. get_new_entry: %d\n", get_new_entry));
memcpy(&unicode[offset], ds->name0_4, 10);
- offset += 10;
- memcpy(&unicode[offset], ds->name5_10, 12);
- offset += 12;
- memcpy(&unicode[offset], ds->name11_12, 4);
- offset += 4;
+ memcpy(&unicode[offset+10], ds->name5_10, 12);
+ memcpy(&unicode[offset+22], ds->name11_12, 4);
+ offset += 26;
if (ds->id & 0x40) {
unicode[offset] = 0;
unicode[offset+1] = 0;
}
- if (slot > 0) {
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
- PRINTK(("4. get_new_entry: %d\n", get_new_entry));
- if (ino == -1) {
- is_long = 0;
- get_new_entry = 0;
- break;
- }
- ds = (struct msdos_dir_slot *) de;
- }
- PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino)<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;
}
- } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
- char bufname[14];
- char *ptname = bufname;
- int dotoffset = 0;
- int was_long = is_long;
-
- if (is_long) {
- unsigned char sum;
- for (sum = 0, i = 0; i < 11; i++) {
- sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
- }
+ 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;
+ }
- if (sum != alias_checksum) {
- PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
- is_long = 0;
- long_slots = 0;
- }
- if (utf8) {
- long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
- } else {
- long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
- }
- }
+ for (i = 0, last = 0; i < 8;) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if (c == 0x05) c = 0xE5;
+ if ((bufname[i++] = c) != ' ')
+ last = i;
+ }
+ i = last;
+ bufname[i++] = '.';
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if ((bufname[i++] = c) != ' ')
+ last = i;
+ }
+ if (!last)
+ continue;
+
+ if (last==name_len)
+ if ((!anycase && !memcmp(name, bufname, last)) ||
+ (anycase && !memicmp(name, bufname, last)))
+ goto Found;
+ if (long_slots) {
+ char longname[260]; /* 256 + 4 */
+ unsigned char long_len;
+ long_len = utf8
+ ?utf8_wcstombs(longname, (__u16 *) unicode, 260)
+ :uni16_to_x8(longname, unicode, uni_xlate, nls);
+ if (long_len != name_len)
+ continue;
+ if ((!anycase && !memcmp(name, longname, long_len)) ||
+ (anycase && !memicmp(name, longname, long_len)))
+ goto Found;
+ }
+ }
- if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
- bufname[0] = '.';
- dotoffset = 1;
- ptname = bufname+1;
- }
- for (i = 0, last = 0; i < 8; i++) {
- if (!(c = de->name[i])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- /* see namei.c, msdos_format_name */
- if (c == 0x05) c = 0xE5;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- }
- i = last;
- ptname[i] = '.';
- i++;
- for (i2 = 0; i2 < 3; i2++) {
- if (!(c = de->ext[i2])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- i++;
+Found:
+ fat_brelse(sb, bh);
+ res = long_slots + 1;
+ *spos = cpos - sizeof(struct msdos_dir_entry);
+ *lpos = cpos - res*sizeof(struct msdos_dir_entry);
+EODir:
+ 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;
+ int ino,inum,i,i2,last;
+ char c;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ unsigned long lpos;
+ loff_t cpos;
+ unsigned char long_slots;
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ int utf8 = MSDOS_SB(sb)->options.utf8;
+ unsigned char *unicode = NULL;
+ struct nls_table *nls = MSDOS_SB(sb)->nls_io;
+ char bufname[14];
+ char *ptname = bufname;
+ int dotoffset = 0;
+
+ 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) < 0)
+ return 0;
+ cpos++;
+ filp->f_pos++;
+ }
+ if (cpos == 2)
+ 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,&ino) == -1)
+ goto EODir;
+ /* Check for long filename entry */
+ if (MSDOS_SB(sb)->options.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 (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
+ struct msdos_dir_slot *ds;
+ int offset;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
+
+ if (!unicode) {
+ unicode = (unsigned char *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode) {
+ filp->f_pos = cpos;
+ fat_brelse(sb, bh);
+ return -ENOMEM;
}
- if ((i = last) != 0) {
- if (!strcmp(de->name,MSDOS_DOT))
- ino = inode->i_ino;
- else if (!strcmp(de->name,MSDOS_DOTDOT))
- ino = fat_parent_ino(inode,0);
-
- if (shortnames || !is_long) {
- if (both)
- bufname[i+dotoffset] = '\0';
- spos = oldpos;
- if (was_long) {
- spos = filp->f_pos - sizeof(struct msdos_dir_entry);
- } else {
- long_slots = 0;
- }
- if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
- filp->f_pos = oldpos;
- break;
- }
- }
- if (is_long && longnames) {
- if (both) {
- memcpy(&longname[long_len+1], bufname, i+dotoffset);
- long_len += i+dotoffset;
- }
- spos = filp->f_pos - sizeof(struct msdos_dir_entry);
- if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
- filp->f_pos = oldpos;
- break;
- }
- }
- oldpos = filp->f_pos;
+ }
+ParseLong:
+ slots = 0;
+ offset = 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) {
+ slot--;
+ offset = slot * 26;
+ memcpy(&unicode[offset], ds->name0_4, 10);
+ memcpy(&unicode[offset+10], ds->name5_10, 12);
+ memcpy(&unicode[offset+22], ds->name11_12, 4);
+ offset += 26;
+
+ if (ds->id & 0x40) {
+ unicode[offset] = 0;
+ unicode[offset+1] = 0;
}
- is_long = 0;
- } else {
- is_long = 0;
- oldpos = filp->f_pos;
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -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, last = 0; i < 8;) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ /* see namei.c, msdos_format_name */
+ if (c == 0x05) c = 0xE5;
+ if ((ptname[i++] = c) != ' ')
+ last = i;
+ }
+ i = last;
+ ptname[i++] = '.';
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if ((ptname[i++] = c) != ' ')
+ last = i;
+ }
+ if (!last)
+ goto RecEnd;
+
+ i = last + dotoffset;
+
+ 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, ino);
+ if (tmp) {
+ inum = tmp->i_ino;
+ iput(tmp);
+ } else
+ inum = iunique(sb, MSDOS_ROOT_INO);
+ }
+
+ if (!long_slots||shortnames) {
+ if (both)
+ bufname[i] = '\0';
+ if (filldir(dirent, bufname, i, lpos, inum) < 0)
+ goto FillFailed;
+ } else {
+ char longname[275];
+ unsigned char long_len = utf8
+ ? utf8_wcstombs(longname, (__u16 *) unicode, 275)
+ : uni16_to_x8(longname, unicode, uni_xlate, nls);
+ if (both) {
+ memcpy(&longname[long_len+1], bufname, i);
+ long_len += i;
}
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ if (filldir(dirent, longname, long_len, lpos, inum) < 0)
+ goto FillFailed;
}
+
+RecEnd:
+ filp->f_pos = cpos;
+ goto GetNew;
+EODir:
+ filp->f_pos = cpos;
+FillFailed:
if (bh)
fat_brelse(sb, bh);
if (unicode) {
@@ -344,39 +469,17 @@ int fat_readdirx(
return 0;
}
-static int fat_filldir(
- 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)
-{
- return filldir(buf, name, name_len, offset, ino);
-}
-
-int fat_readdir(
- struct file *filp,
- void *dirent,
- filldir_t filldir)
+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, fat_filldir, filldir,
- 0, 1, 0);
+ return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
}
static int vfat_ioctl_fill(
- 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 dirent *d1 = (struct dirent *)buf;
@@ -438,7 +541,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
return err;
put_user(0, &d1->d_reclen);
return fat_readdirx(inode,filp,(void *)arg,
- vfat_ioctl_fill, NULL, 0, 1, 1);
+ vfat_ioctl_fill, 0, 1);
}
case VFAT_IOCTL_READDIR_SHORT: {
struct dirent *d1 = (struct dirent *)arg;
@@ -447,7 +550,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
if (err)
return err;
return fat_readdirx(inode,filp,(void *)arg,
- vfat_ioctl_fill, NULL, 1, 0, 1);
+ vfat_ioctl_fill, 1, 1);
}
default:
/* forward ioctl to CVF extension */
@@ -461,6 +564,62 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
return 0;
}
+/***** See if directory is empty */
+int fat_dir_empty(struct inode *dir)
+{
+ loff_t pos;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ int ino,result = 0;
+
+ pos = 0;
+ bh = NULL;
+ while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -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, int *ino)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, curr;
+ int row;
+ int res;
+
+ offset = curr = 0;
+ *bh = NULL;
+ row = 0;
+ while (fat_get_entry(dir,&curr,bh,de,ino) > -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;
+ if ((res = fat_add_cluster(dir)) < 0) return res;
+ do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+ return offset;
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically