patch-2.1.60 linux/fs/vfat/namei.c

Next file: linux/fs/vfat/vfatfs_syms.c
Previous file: linux/fs/vfat/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.59/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
@@ -11,21 +11,20 @@
  */
 
 #include <linux/config.h>
+#define __NO_VERSION__
 #include <linux/module.h>
 
 #include <linux/sched.h>
 #include <linux/msdos_fs.h>
+#include <linux/nls.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
-#include <linux/init.h>
-
-#include <asm/uaccess.h>
+#include <linux/malloc.h>
 
 #include "../fat/msbuffer.h"
-#include "../fat/tables.h"
 
 #if 0
 # define PRINTK(x) printk x
@@ -79,13 +78,30 @@
 	NULL  /* remount */
 };
 
+static int simple_getbool(char *s, int *setval)
+{
+	if (s) {
+		if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) {
+			*setval = 1;
+		} else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) {
+			*setval = 0;
+		} else {
+			return 0;
+		}
+	} else {
+		*setval = 1;
+	}
+	return 1;
+}
+
 static int parse_options(char *options,	struct fat_mount_options *opts)
 {
 	char *this_char,*value,save,*savep;
-	int ret;
+	int ret, val;
 
 	opts->unicode_xlate = opts->posixfs = 0;
 	opts->numtail = 1;
+	opts->utf8 = 0;
 
 	if (!options) return 1;
 	save = 0;
@@ -97,25 +113,19 @@
 			savep = value;
 			*value++ = 0;
 		}
-		if (!strcmp(this_char,"uni_xlate")) {
-			if (value) {
-				ret = 0;
-			} else {
-				opts->unicode_xlate = 1;
-			}
-		}
-		else if (!strcmp(this_char,"posix")) {
-			if (value) {
-				ret = 0;
-			} else {
-				opts->posixfs = 1;
-			}
-		}
-		else if (!strcmp(this_char,"nonumtail")) {
-			if (value) {
-				ret = 0;
-			} else {
-				opts->numtail = 0;
+		if (!strcmp(this_char,"utf8")) {
+			ret = simple_getbool(value, &val);
+			if (ret) opts->utf8 = val;
+		} else if (!strcmp(this_char,"uni_xlate")) {
+			ret = simple_getbool(value, &val);
+			if (ret) opts->unicode_xlate = val;
+		} else if (!strcmp(this_char,"posix")) {
+			ret = simple_getbool(value, &val);
+			if (ret) opts->posixfs = val;
+		} else if (!strcmp(this_char,"nonumtail")) {
+			ret = simple_getbool(value, &val);
+			if (ret) {
+				opts->numtail = !val;
 			}
 		}
 		if (this_char != options)
@@ -127,6 +137,9 @@
 			return 0;
 		}
 	}
+	if (opts->unicode_xlate) {
+		opts->utf8 = 0;
+	}
 	return 1;
 }
 
@@ -137,6 +150,8 @@
   
 	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) {
@@ -148,7 +163,6 @@
 	if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
 		MOD_DEC_USE_COUNT;
 	} else {
-		MSDOS_SB(sb)->options.isvfat = 1;
 		MSDOS_SB(sb)->options.dotsOK = 0;
 	}
 
@@ -225,7 +239,6 @@
 /* Characters that are undesirable in an MS-DOS file name */
 
 static char bad_chars[] = "*?<>|\":/\\";
-static char bad_if_strict[] = "+=,; []";
 static char replace_chars[] = "[];,+=";
 
 static int vfat_find(struct inode *dir,struct qstr* name,
@@ -265,8 +278,8 @@
 	return 0;
 }
 
-static int vfat_valid_shortname(char conv,const char *name,int len,
-				 int dot_dirs)
+static int vfat_valid_shortname(const char *name,int len,
+				int dot_dirs, int utf8)
 {
 	const char *walk, **reserved;
 	unsigned char c;
@@ -283,17 +296,17 @@
 	for (walk = name; len && walk-name < 8;) {
 	    	c = *walk++;
 		len--;
-		if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
-		if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
-		if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
-  		if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+		if (utf8 && (c & 0x80)) return -EINVAL;
+		if (strchr(bad_chars,c)) return -EINVAL;
+		if (strchr(replace_chars,c)) return -EINVAL;
+  		if (c >= 'A' && c <= 'Z') return -EINVAL;
 		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
 		if ((walk == name) && (c == 0xE5)) c = 0x05;
 		if (c == '.') break;
 		space = c == ' ';
 	}
 	if (space) return -EINVAL;
-	if ((conv == 's' || conv == 'x') && len && c != '.') {
+	if (len && c != '.') {
 		c = *walk++;
 		len--;
 		if (c != '.') return -EINVAL;
@@ -304,18 +317,17 @@
 		while (len > 0 && walk-name < (MSDOS_NAME+1)) {
 			c = *walk++;
 			len--;
-			if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
-			if (conv == 's' && strchr(bad_if_strict,c))
-				return -EINVAL;
-			if (conv == 'x' && strchr(replace_chars,c))
+			if (utf8 && (c & 0x80)) return -EINVAL;
+			if (strchr(bad_chars,c)) return -EINVAL;
+			if (strchr(replace_chars,c))
 				return -EINVAL;
 			if (c < ' ' || c == ':' || c == '\\' || c == '.')
 				return -EINVAL;
-			if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+			if (c >= 'A' && c <= 'Z') return -EINVAL;
 			space = c == ' ';
 		}
 		if (space) return -EINVAL;
-		if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+		if (len) return -EINVAL;
 	}
 	for (reserved = reserved_names; *reserved; reserved++)
 		if (!strncmp(name,*reserved,8)) return -EINVAL;
@@ -328,8 +340,8 @@
  * returned.  The formatted short filename is returned in 'res'.
  */
 
-static int vfat_format_name(char conv,const char *name,int len,char *res,
-  int dot_dirs)
+static int vfat_format_name(const char *name,int len,char *res,
+  int dot_dirs,int utf8)
 {
 	char *walk;
 	const char **reserved;
@@ -349,17 +361,17 @@
 	for (walk = res; len && walk-res < 8; walk++) {
 	    	c = *name++;
 		len--;
-		if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
-		if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
-		if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
-		if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+		if (utf8 && (c & 0x80)) return -EINVAL;
+		if (strchr(bad_chars,c)) return -EINVAL;
+		if (strchr(replace_chars,c)) return -EINVAL;
+		if (c >= 'A' && c <= 'Z') return -EINVAL;
 		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
 		if (c == '.') break;
 		space = c == ' ';
 		*walk = c >= 'a' && c <= 'z' ? c-32 : c;
 	}
 	if (space) return -EINVAL;
-	if ((conv == 's' || conv == 'x') && len && c != '.') {
+	if (len && c != '.') {
 		c = *name++;
 		len--;
 		if (c != '.') return -EINVAL;
@@ -370,19 +382,18 @@
 		while (len > 0 && walk-res < MSDOS_NAME) {
 			c = *name++;
 			len--;
-			if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
-			if (conv == 's' && strchr(bad_if_strict,c))
-				return -EINVAL;
-			if (conv == 'x' && strchr(replace_chars,c))
+			if (utf8 && (c & 0x80)) return -EINVAL;
+			if (strchr(bad_chars,c)) return -EINVAL;
+			if (strchr(replace_chars,c))
 				return -EINVAL;
 			if (c < ' ' || c == ':' || c == '\\' || c == '.')
 				return -EINVAL;
-			if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+			if (c >= 'A' && c <= 'Z') return -EINVAL;
 			space = c == ' ';
 			*walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
 		}
 		if (space) return -EINVAL;
-		if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+		if (len) return -EINVAL;
 	}
 	while (walk-res < MSDOS_NAME) *walk++ = ' ';
 	for (reserved = reserved_names; *reserved; reserved++)
@@ -397,7 +408,7 @@
  * shortname does not exist
  */
 static int vfat_create_shortname(struct inode *dir, const char *name,
-     int len, char *name_res)
+     int len, char *name_res, int utf8)
 {
 	const char *ip, *ext_start, *end;
 	char *p;
@@ -417,16 +428,25 @@
 	if (len && name[len-1]==' ') return -EINVAL;
 	if (len <= 12) {
 		/* Do a case insensitive search if the name would be a valid
-		 * shortname if is were all capitalized */
+		 * shortname if is were all capitalized.  However, do not
+		 * allow spaces in short names because Win95 scandisk does
+		 * not like that */
+		res = 0;
 		for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++)
 		{
+			if (*ip == ' ') {
+				res = -1;
+				break;
+			}
 			if (*ip >= 'A' && *ip <= 'Z') {
 				*p = *ip + 32;
 			} else {
 				*p = *ip;
 			}
 		}
-		res = vfat_format_name('x', msdos_name, len, name_res, 1);
+		if (res == 0) {
+			res = vfat_format_name(msdos_name, len, name_res, 1, utf8);
+		}
 		if (res > -1) {
 			PRINTK(("vfat_create_shortname 1\n"));
 			qname.name=msdos_name;
@@ -476,7 +496,10 @@
 
 	for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++)
 	{
-		if (!strchr(skip_chars, *ip)) {
+		if (utf8 && (*ip & 0x80)) {
+			*p++ = '_';
+			baselen++;
+		} else if (!strchr(skip_chars, *ip)) {
 			if (*ip >= 'A' && *ip <= 'Z') {
 				*p = *ip + 32;
 			} else {
@@ -496,7 +519,10 @@
 	if (ext_start) {
 		extlen = 0;
 		for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
-			if (!strchr(skip_chars, *ip)) {
+			if (utf8 && (*ip & 0x80)) {
+				*p++ = '_';
+				extlen++;
+			} else if (!strchr(skip_chars, *ip)) {
 				if (*ip >= 'A' && *ip <= 'Z') {
 					*p = *ip + 32;
 				} else {
@@ -547,7 +573,7 @@
 		qname.len=totlen;
 		res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
 	}
-	res = vfat_format_name('x', msdos_name, totlen, name_res, 1);
+	res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8);
 	return res;
 }
 
@@ -596,7 +622,9 @@
 			ino = fat_get_entry(dir,&curr,&bh,&de);
 		}
 
-		if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+		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);
 	}
@@ -605,7 +633,8 @@
 
 /* Translate a string, including coded sequences into Unicode */
 static int
-xlate_to_uni(const char *name, int len, char *outname, int *outlen, int escape)
+xlate_to_uni(const char *name, int len, char *outname, int *outlen,
+	     int escape, int utf8, struct nls_table *nls)
 {
 	int i;
 	const unsigned char *ip;
@@ -613,22 +642,43 @@
 	int fill;
 	unsigned char c1, c2, c3;
 
-	op = outname;
-	for (i = 0, ip = name, op = outname, *outlen = 0;
-	     i < len && *outlen <= 260; i++, *outlen += 1)
-	{
-		if (escape && (i < len - 4) && 
-		    (*ip == ':') &&
-		    ((c1 = fat_code2uni[ip[1]]) != 255) &&
-		    ((c2 = fat_code2uni[ip[2]]) != 255) &&
-		    ((c3 = fat_code2uni[ip[3]]) != 255)) {
-			*op++ = (c1 << 4) + (c2 >> 2);
-			*op++ = ((c2 & 0x3) << 6) + c3;
-			ip += 4;
+	if (utf8) {
+		*outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE);
+		if (name[len-1] == '.')
+			*outlen-=2;
+		op = &outname[*outlen * sizeof(__u16)];
+	} else {
+		if (name[len-1] == '.') 
+			len--;
+		op = outname;
+		if (nls) {
+			/* XXX: i is incorrectly computed. */
+			for (i = 0, ip = name, op = outname, *outlen = 0;
+			     i < len && *outlen <= 260; i++, *outlen += 1)
+			{
+				if (escape && (*ip == ':')) {
+					if (i > len - 4) return -EINVAL;
+					c1 = fat_esc2uni[ip[1]];
+					c2 = fat_esc2uni[ip[2]];
+					c3 = fat_esc2uni[ip[3]];
+					if (c1 == 255 || c2 == 255 || c3 == 255)
+						return -EINVAL;
+					*op++ = (c1 << 4) + (c2 >> 2);
+					*op++ = ((c2 & 0x3) << 6) + c3;
+					ip += 4;
+				} else {
+					*op++ = nls->charset2uni[*ip].uni1;
+					*op++ = nls->charset2uni[*ip].uni2;
+					ip++;
+				}
+			}
 		} else {
-			*op++ = fat_a2uni[*ip].uni1;
-			*op++ = fat_a2uni[*ip].uni2;
-			ip++;
+			for (i = 0, ip = name, op = outname, *outlen = 0;
+			     i < len && *outlen <= 260; i++, *outlen += 1)
+			{
+				*op++ = *ip++;
+				*op++ = 0;
+			}
 		}
 	}
 	if (*outlen > 260)
@@ -653,7 +703,8 @@
 
 static int
 vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
-		     char *msdos_name, int *slots, int uni_xlate)
+		     char *msdos_name, int *slots,
+		     int uni_xlate, int utf8, struct nls_table *nls)
 {
 	struct msdos_dir_slot *ps;
 	struct msdos_dir_entry *de;
@@ -667,10 +718,11 @@
 	int i;
 	loff_t offset;
 
+	if (name[len-1] == '.') len--;
 	if(!(page = __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	uniname = (char *) page;
-	res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate);
+	res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate, utf8, nls);
 	if (res < 0) {
 		free_page(page);
 		return res;
@@ -690,8 +742,7 @@
 		ps->attr = ATTR_EXT;
 		ps->reserved = 0;
 		ps->alias_checksum = cksum;
-		ps->start[0] = 0;
-		ps->start[1] = 0;
+		ps->start = 0;
 		PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
 		offset = (slot - 1) * 26;
 		ip = &uniname[offset];
@@ -728,11 +779,14 @@
 {
 	struct msdos_dir_entry *de;
 	char msdos_name[MSDOS_NAME];
-	int res, xlate;
+	int res, xlate, utf8;
+	struct nls_table *nls;
 
 	PRINTK(("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;
@@ -742,13 +796,13 @@
 		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
 	} else {
 		PRINTK(("vfat_build_slots 4\n"));
-		res = vfat_valid_shortname('x', name, len, 1);
+		res = vfat_valid_shortname(name, len, 1, utf8);
 		if (res > -1) {
 			PRINTK(("vfat_build_slots 5a\n"));
-			res = vfat_format_name('x', name, len, de->name, 1);
+			res = vfat_format_name(name, len, de->name, 1, utf8);
 			PRINTK(("vfat_build_slots 5b\n"));
 		} else {
-			res = vfat_create_shortname(dir, name, len, msdos_name);
+			res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
 			if (res < 0) {
 				return res;
 			}
@@ -761,7 +815,7 @@
 			*is_long = 1;
 
 			return vfat_fill_long_slots(ds, name, len, msdos_name,
-						    slots, xlate);
+						    slots, xlate, utf8, nls);
 		}
 	}
 	return 0;
@@ -787,8 +841,11 @@
 			  vf->name, vf->len, name, name_len);
 #endif
 
+	/* Filenames cannot end in '.' or we treat like it has none */
 	if (vf->len != name_len) {
-		return 0;
+		if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) {
+			return 0;
+		}
 	}
 
 	s1 = name; s2 = vf->name;
@@ -821,12 +878,17 @@
 	struct msdos_dir_entry *de;
 	struct msdos_dir_slot *ps;
 	loff_t offset;
-	struct msdos_dir_slot ds[MSDOS_SLOTS];
+	struct msdos_dir_slot *ds;
 	int is_long;
 	int slots, slot;
 	int res;
 
 	PRINTK(("Entering vfat_find\n"));
+
+	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;
@@ -835,10 +897,11 @@
 	vf.posix = MSDOS_SB(sb)->options.posixfs;
 	res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0);
 	PRINTK(("vfat_find: Debug 1\n"));
-	if (res < 0) return res;
+	if (res < 0) goto cleanup;
 	if (vf.found) {
 		if (new_filename) {
-			return -EEXIST;
+			res = -EEXIST;
+			goto cleanup;
 		}
 		sinfo_out->longname_offset = vf.offset;
 		sinfo_out->shortname_offset = vf.short_offset;
@@ -848,16 +911,19 @@
 		sinfo_out->ino = vf.ino;
 
 		PRINTK(("vfat_find: Debug 2\n"));
-		return 0;
+		res = 0;
+		goto cleanup;
 	}
 
 	PRINTK(("vfat_find: Debug 3\n"));
-	if (!vf.found && !new_filename)
-		return -ENOENT;
+	if (!vf.found && !new_filename) {
+		res = -ENOENT;
+		goto cleanup;
+	}
 	
 	res = vfat_build_slots(dir, qname->name, qname->len, ds, 
 			       &slots, &is_long);
-	if (res < 0) return res;
+	if (res < 0) goto cleanup;
 
 	de = (struct msdos_dir_entry *) ds;
 
@@ -867,7 +933,8 @@
 		if (is_long) slots++;
 		offset = vfat_find_free_slots(dir, slots);
 		if (offset < 0) {
-			return offset;
+			res = offset;
+			goto cleanup;
 		}
 
 		PRINTK(("vfat_find: create file 2\n"));
@@ -878,7 +945,8 @@
 			sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
 			if (sinfo_out->ino < 0) {
 				PRINTK(("vfat_find: problem\n"));
-				return sinfo_out->ino;
+				res = sinfo_out->ino;
+				goto cleanup;
 			}
 			memcpy(de, ps, sizeof(struct msdos_dir_slot));
 			fat_mark_buffer_dirty(sb, bh, 1);
@@ -890,12 +958,12 @@
 
 		PRINTK(("vfat_find: create file 5\n"));
 
-		memset(de->unused, 0, sizeof(de->unused));
 		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;
@@ -913,10 +981,15 @@
 		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;
 		return 0;
+	} else {
+		res = -ENOENT;
 	}
 
-	return -ENOENT;
+cleanup:
+	kfree(ds);
+	return res;
 }
 
 int vfat_lookup(struct inode *dir,struct dentry *dentry)
@@ -1040,10 +1113,10 @@
 	dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(dir);
 	memcpy(de->name,name,MSDOS_NAME);
-	memset(de->unused, 0, sizeof(de->unused));
 	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;
@@ -1058,10 +1131,12 @@
 	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;
 	}
 
@@ -1244,10 +1319,8 @@
 		if (res > 0) {
 			res = 0;
 		}
-	} else {
-		printk("Problem in vfat_rmdirx\n");
+		dir->i_version = ++event;
 	}
-	dir->i_version = ++event;
 
 rmdir_done:
 	fat_brelse(sb, bh);
@@ -1275,19 +1348,15 @@
 	struct slot_info sinfo;
 
 	bh = NULL;
-	if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0)
-		goto unlink_done;
+	res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
 
 	if (res >= 0 && sinfo.total_slots > 0) {
 		res = vfat_remove_entry(dir,&sinfo,&bh,dentry,0,nospc);
 		if (res > 0) {
 			res = 0;
 		}
-	} else {
-		printk("Problem in vfat_unlinkx: res=%d, total_slots=%d\n",res, sinfo.total_slots);
 	}
 
-unlink_done:
 	fat_brelse(sb, bh);
 	return res;
 }
@@ -1329,7 +1398,16 @@
 	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,
 		struct inode *new_dir,struct dentry *new_dentry)
 {
@@ -1417,10 +1495,13 @@
 			PRINTK(("vfat_rename 8\n"));
 			if (res < 0) goto rename_done;
 		} else {
-			PRINTK(("vfat_rename 9\n"));
-			res = vfat_unlinkx(new_dir,new_dentry,1);
-			PRINTK(("vfat_rename 10\n"));
-			if (res < 0) goto rename_done;
+			/* Is this the same file, different case? */
+			if (new_inode != old_inode) {
+				PRINTK(("vfat_rename 9\n"));
+				res = vfat_unlinkx(new_dir,new_dentry,1);
+				PRINTK(("vfat_rename 10\n"));
+				if (res < 0) goto rename_done;
+			}
 		}
 	}
 
@@ -1444,6 +1525,7 @@
 	new_de->cdate = old_de->cdate;
 	new_de->adate = old_de->adate;
 	new_de->start = old_de->start;
+	new_de->starthi = old_de->starthi;
 	new_de->size = old_de->size;
 
 	if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
@@ -1492,8 +1574,10 @@
 			res = -EIO;
 			goto rename_done;
 		}
-		dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
-		    MSDOS_I(new_dir)->i_start;
+		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);
 		fat_mark_buffer_dirty(sb, dotdot_bh, 1);
 		old_dir->i_nlink--;
@@ -1545,28 +1629,6 @@
 void vfat_read_inode(struct inode *inode)
 {
 	fat_read_inode(inode, &vfat_dir_inode_operations);
-}
-
-static struct file_system_type vfat_fs_type = {
-	"vfat",
-	FS_REQUIRES_DEV,
-	vfat_read_super,
-	NULL
-};
-
-EXPORT_SYMBOL(vfat_create);
-EXPORT_SYMBOL(vfat_unlink);
-EXPORT_SYMBOL(vfat_mkdir);
-EXPORT_SYMBOL(vfat_rmdir);
-EXPORT_SYMBOL(vfat_rename);
-EXPORT_SYMBOL(vfat_put_super);
-EXPORT_SYMBOL(vfat_read_super);
-EXPORT_SYMBOL(vfat_read_inode);
-EXPORT_SYMBOL(vfat_lookup);
-
-__initfunc(int init_vfat_fs(void))
-{
-	return register_filesystem(&vfat_fs_type);
 }
 
 #ifdef MODULE

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov