patch-1.3.72 linux/fs/fat/dir.c
Next file: linux/fs/fat/fatfs_syms.c
Previous file: linux/fs/exec.c
Back to the patch index
Back to the overall index
- Lines: 356
- Date:
Tue Mar 5 13:03:26 1996
- Orig file:
v1.3.71/linux/fs/fat/dir.c
- Orig date:
Sun Feb 11 15:32:46 1996
diff -u --recursive --new-file v1.3.71/linux/fs/fat/dir.c linux/fs/fat/dir.c
@@ -7,7 +7,8 @@
*
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
*
- * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
+ * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
*/
#include <linux/fs.h>
@@ -18,6 +19,7 @@
#include <linux/string.h>
#include <linux/ioctl.h>
#include <linux/dirent.h>
+#include <linux/mm.h>
#include <asm/segment.h>
@@ -45,11 +47,58 @@
file_fsync /* fsync */
};
+/* Convert Unicode string to 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 three
+ * characters are a sort of uuencoded 16 bit 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.
+ */
+static int
+uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
+{
+ unsigned char *ip, *op;
+ unsigned char page, pg_off;
+ unsigned char *uni_page;
+ unsigned short val;
+
+ ip = uni;
+ op = ascii;
+
+ while (*ip || ip[1]) {
+ pg_off = *ip++;
+ page = *ip++;
+
+ uni_page = fat_uni2asc_pg[page];
+ if (uni_page && uni_page[pg_off]) {
+ *op++ = uni_page[pg_off];
+ } else {
+ if (uni_xlate == 1) {
+ *op++ = ':';
+ val = (pg_off << 8) + page;
+ op[2] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ op[1] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ *op = fat_uni2code[val & 0x3f];
+ op += 3;
+ } else {
+ *op++ = '?';
+ }
+ }
+ }
+ *op = 0;
+ return (op - ascii);
+}
+
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)
{
struct super_block *sb = inode->i_sb;
@@ -58,18 +107,21 @@
struct buffer_head *bh;
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;
+ int uni_xlate = MSDOS_SB(sb)->unicode_xlate;
+ unsigned char *unicode = NULL;
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
/* Fake . and .. for the root directory. */
if (inode->i_ino == MSDOS_ROOT_INO) {
while (oldpos < 2) {
- if (filldir(dirent, "..", oldpos+1, oldpos, MSDOS_ROOT_INO) < 0)
+ if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
return 0;
oldpos++;
filp->f_pos++;
@@ -81,23 +133,10 @@
return -ENOENT;
bh = NULL;
- longname[0] = '\0';
+ longname[0] = longname[1] = 0;
is_long = 0;
ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
while (ino > -1) {
- /* Should we warn about finding extended entries on fs
- * mounted non-vfat ? Deleting or renaming files will cause
- * corruption, as extended entries are not updated.
- */
- if (!MSDOS_SB(sb)->vfat &&
- !MSDOS_SB(sb)->umsdos && /* umsdos is safe for us */
- !IS_RDONLY(inode) &&
- (de->attr == ATTR_EXT) &&
- !MSDOS_SB(sb)->quiet) {
- printk("MSDOS-fs warning: vfat directory entry found on fs mounted non-vfat (device %s)\n",
- kdevname(sb->s_dev));
- }
-
/* Check for long filename entry */
if (MSDOS_SB(sb)->vfat && (de->name[0] == (__s8) DELETED_FLAG)) {
is_long = 0;
@@ -105,19 +144,24 @@
} else if (MSDOS_SB(sb)->vfat && de->attr == ATTR_EXT) {
int get_new_entry;
struct msdos_dir_slot *ds;
- unsigned char page, pg_off, ascii;
- unsigned char *uni_page;
- unsigned char offset;
+ int offset;
unsigned char id;
unsigned char slot;
unsigned char slots = 0;
- int i;
+
+ if (!unicode) {
+ unicode = (unsigned char *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode)
+ return -ENOMEM;
+ }
offset = 0;
ds = (struct msdos_dir_slot *) de;
id = ds->id;
if (id & 0x40) {
slots = id & ~0x40;
+ long_slots = slots;
is_long = 1;
alias_checksum = ds->alias_checksum;
}
@@ -140,43 +184,18 @@
break;
}
slot--;
- offset = slot * 13;
+ offset = slot * 26;
PRINTK(("2. get_new_entry: %d\n", get_new_entry));
- for (i = 0; i < 10; i += 2) {
- pg_off = ds->name0_4[i];
- page = ds->name0_4[i+1];
- if (pg_off == 0 && page == 0) {
- goto found_end;
- }
- uni_page = fat_uni2asc_pg[page];
- ascii = uni_page[pg_off];
- longname[offset++] = ascii ? ascii : '?';
- }
- for (i = 0; i < 12; i += 2) {
- pg_off = ds->name5_10[i];
- page = ds->name5_10[i+1];
- if (pg_off == 0 && page == 0) {
- goto found_end;
- }
- uni_page = fat_uni2asc_pg[page];
- ascii = uni_page[pg_off];
- longname[offset++] = ascii ? ascii : '?';
- }
- for (i = 0; i < 4; i += 2) {
- pg_off = ds->name11_12[i];
- page = ds->name11_12[i+1];
- if (pg_off == 0 && page == 0) {
- goto found_end;
- }
- uni_page = fat_uni2asc_pg[page];
- ascii = uni_page[pg_off];
- longname[offset++] = ascii ? ascii : '?';
- }
- found_end:
- PRINTK(("3. 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;
+
if (ds->id & 0x40) {
- longname[offset] = '\0';
- long_len = offset;
+ unicode[offset] = 0;
+ unicode[offset+1] = 0;
}
if (slot > 0) {
ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
@@ -190,7 +209,6 @@
}
PRINTK(("5. get_new_entry: %d\n", get_new_entry));
}
- PRINTK(("Long filename: %s, get_new_entry: %d\n", longname, get_new_entry));
} else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
char bufname[14];
char *ptname = bufname;
@@ -198,8 +216,8 @@
if (is_long) {
unsigned char sum;
-
- for (sum = i = 0; i < 11; i++) {
+ long_len = uni2ascii(unicode, longname, uni_xlate);
+ for (sum = 0, i = 0; i < 11; i++) {
sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
}
@@ -214,7 +232,7 @@
dotoffset = 1;
ptname = bufname+1;
}
- for (i = last = 0; i < 8; i++) {
+ 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 */
@@ -240,22 +258,30 @@
else if (!strcmp(de->name,MSDOS_DOTDOT))
ino = fat_parent_ino(inode,0);
- if (!is_long) {
+ if (shortnames || !is_long) {
dcache_add(inode, bufname, i+dotoffset, ino);
if (both) {
bufname[i+dotoffset] = '\0';
}
- if (filldir(dirent, bufname, i+dotoffset, oldpos, ino) < 0) {
+ spos = oldpos;
+ if (is_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;
}
- } else {
+ }
+ if (is_long && longnames) {
dcache_add(inode, longname, long_len, ino);
if (both) {
memcpy(&longname[long_len+1], bufname, i+dotoffset);
long_len += i+dotoffset;
}
- if (filldir(dirent, longname, long_len, oldpos, ino) < 0) {
+ 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;
}
@@ -270,22 +296,45 @@
ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
}
if (bh) brelse(bh);
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
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 inode *inode,
struct file *filp,
void *dirent,
filldir_t filldir)
{
- return fat_readdirx(inode, filp, dirent, filldir, 0);
+ return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
+ 0, 1, 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;
@@ -341,7 +390,14 @@
case VFAT_IOCTL_READDIR_BOTH: {
struct dirent *d1 = (struct dirent *)arg;
put_user(0, &d1->d_reclen);
- return fat_readdirx(inode,filp,(void *)arg,vfat_ioctl_fill,1);
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 0, 1, 1);
+ }
+ case VFAT_IOCTL_READDIR_SHORT: {
+ struct dirent *d1 = (struct dirent *)arg;
+ put_user(0, &d1->d_reclen);
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 1, 0, 1);
}
default:
return -EINVAL;
@@ -349,3 +405,20 @@
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:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this