|
@@ -222,6 +222,80 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size,
|
|
|
return len;
|
|
|
}
|
|
|
|
|
|
+enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, };
|
|
|
+
|
|
|
+/**
|
|
|
+ * fat_parse_long - Parse extended directory entry.
|
|
|
+ *
|
|
|
+ * This function returns zero on success, negative value on error, or one of
|
|
|
+ * the following:
|
|
|
+ *
|
|
|
+ * %PARSE_INVALID - Directory entry is invalid.
|
|
|
+ * %PARSE_NOT_LONGNAME - Directory entry does not contain longname.
|
|
|
+ * %PARSE_EOF - Directory has no more entries.
|
|
|
+ */
|
|
|
+static int fat_parse_long(struct inode *dir, loff_t *pos,
|
|
|
+ struct buffer_head **bh, struct msdos_dir_entry **de,
|
|
|
+ wchar_t **unicode, unsigned char *nr_slots)
|
|
|
+{
|
|
|
+ struct msdos_dir_slot *ds;
|
|
|
+ unsigned char id, slot, slots, alias_checksum;
|
|
|
+
|
|
|
+ if (!*unicode) {
|
|
|
+ *unicode = (wchar_t *)__get_free_page(GFP_KERNEL);
|
|
|
+ if (!*unicode) {
|
|
|
+ brelse(*bh);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+parse_long:
|
|
|
+ slots = 0;
|
|
|
+ ds = (struct msdos_dir_slot *)*de;
|
|
|
+ id = ds->id;
|
|
|
+ if (!(id & 0x40))
|
|
|
+ return PARSE_INVALID;
|
|
|
+ slots = id & ~0x40;
|
|
|
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
|
|
|
+ return PARSE_INVALID;
|
|
|
+ *nr_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(dir, pos, bh, de) < 0)
|
|
|
+ return PARSE_EOF;
|
|
|
+ if (slot == 0)
|
|
|
+ break;
|
|
|
+ ds = (struct msdos_dir_slot *)*de;
|
|
|
+ if (ds->attr != ATTR_EXT)
|
|
|
+ return PARSE_NOT_LONGNAME;
|
|
|
+ if ((ds->id & ~0x40) != slot)
|
|
|
+ goto parse_long;
|
|
|
+ if (ds->alias_checksum != alias_checksum)
|
|
|
+ goto parse_long;
|
|
|
+ }
|
|
|
+ if ((*de)->name[0] == DELETED_FLAG)
|
|
|
+ return PARSE_INVALID;
|
|
|
+ if ((*de)->attr == ATTR_EXT)
|
|
|
+ goto parse_long;
|
|
|
+ if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME))
|
|
|
+ return PARSE_INVALID;
|
|
|
+ if (fat_checksum((*de)->name) != alias_checksum)
|
|
|
+ *nr_slots = 0;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Return values: negative -> error, 0 -> not found, positive -> found,
|
|
|
* value is the total amount of slots, including the shortname entry.
|
|
@@ -259,65 +333,16 @@ parse_record:
|
|
|
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 alias_checksum;
|
|
|
-
|
|
|
- if (!unicode) {
|
|
|
- unicode = (wchar_t *)
|
|
|
- __get_free_page(GFP_KERNEL);
|
|
|
- if (!unicode) {
|
|
|
- brelse(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;
|
|
|
- nr_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) < 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] == DELETED_FLAG)
|
|
|
- continue;
|
|
|
- if (de->attr == ATTR_EXT)
|
|
|
- goto parse_long;
|
|
|
- if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
|
|
|
+ int status = fat_parse_long(inode, &cpos, &bh, &de,
|
|
|
+ &unicode, &nr_slots);
|
|
|
+ if (status < 0)
|
|
|
+ return status;
|
|
|
+ else if (status == PARSE_INVALID)
|
|
|
continue;
|
|
|
- if (fat_checksum(de->name) != alias_checksum)
|
|
|
- nr_slots = 0;
|
|
|
+ else if (status == PARSE_NOT_LONGNAME)
|
|
|
+ goto parse_record;
|
|
|
+ else if (status == PARSE_EOF)
|
|
|
+ goto EODir;
|
|
|
}
|
|
|
|
|
|
memcpy(work, de->name, sizeof(de->name));
|
|
@@ -405,8 +430,8 @@ struct fat_ioctl_filldir_callback {
|
|
|
int short_len;
|
|
|
};
|
|
|
|
|
|
-static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
|
|
|
- filldir_t filldir, int short_only, int both)
|
|
|
+static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
|
|
|
+ filldir_t filldir, int short_only, int both)
|
|
|
{
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
@@ -455,9 +480,10 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
|
|
|
|
|
|
bh = NULL;
|
|
|
GetNew:
|
|
|
- long_slots = 0;
|
|
|
if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
|
|
|
goto EODir;
|
|
|
+parse_record:
|
|
|
+ long_slots = 0;
|
|
|
/* Check for long filename entry */
|
|
|
if (isvfat) {
|
|
|
if (de->name[0] == DELETED_FLAG)
|
|
@@ -472,66 +498,18 @@ GetNew:
|
|
|
}
|
|
|
|
|
|
if (isvfat && de->attr == ATTR_EXT) {
|
|
|
- struct msdos_dir_slot *ds;
|
|
|
- unsigned char id;
|
|
|
- unsigned char slot;
|
|
|
- unsigned char slots;
|
|
|
- unsigned char alias_checksum;
|
|
|
-
|
|
|
- if (!unicode) {
|
|
|
- unicode = (wchar_t *)__get_free_page(GFP_KERNEL);
|
|
|
- if (!unicode) {
|
|
|
- filp->f_pos = cpos;
|
|
|
- brelse(bh);
|
|
|
- ret = -ENOMEM;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
-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) */
|
|
|
+ int status = fat_parse_long(inode, &cpos, &bh, &de,
|
|
|
+ &unicode, &long_slots);
|
|
|
+ if (status < 0) {
|
|
|
+ filp->f_pos = cpos;
|
|
|
+ ret = status;
|
|
|
+ goto out;
|
|
|
+ } else if (status == PARSE_INVALID)
|
|
|
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) == -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] == DELETED_FLAG)
|
|
|
- goto RecEnd;
|
|
|
- if (de->attr == ATTR_EXT)
|
|
|
- goto ParseLong;
|
|
|
- if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
|
|
|
- goto RecEnd;
|
|
|
- if (fat_checksum(de->name) != alias_checksum)
|
|
|
- long_slots = 0;
|
|
|
+ else if (status == PARSE_NOT_LONGNAME)
|
|
|
+ goto parse_record;
|
|
|
+ else if (status == PARSE_EOF)
|
|
|
+ goto EODir;
|
|
|
}
|
|
|
|
|
|
if (sbi->options.dotsOK) {
|
|
@@ -665,7 +643,7 @@ out:
|
|
|
static 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);
|
|
|
+ return __fat_readdir(inode, filp, dirent, filldir, 0, 0);
|
|
|
}
|
|
|
|
|
|
static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
|
|
@@ -754,8 +732,8 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp,
|
|
|
down(&inode->i_sem);
|
|
|
ret = -ENOENT;
|
|
|
if (!IS_DEADDIR(inode)) {
|
|
|
- ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir,
|
|
|
- short_only, both);
|
|
|
+ ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir,
|
|
|
+ short_only, both);
|
|
|
}
|
|
|
up(&inode->i_sem);
|
|
|
if (ret >= 0)
|