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

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