patch-2.2.11 linux/fs/fat/dir.c

Next file: linux/fs/fat/fatfs_syms.c
Previous file: linux/fs/fat/cache.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.10/linux/fs/fat/dir.c linux/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,369 @@
 	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 = MSDOS_I(inode)->i_last_pos;
+	loff_t barrier = 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;
+	int res = 0;
 
-/* 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;
+	while(1) {
+		if (barrier && cpos>=barrier)
+			goto EODir;
+rescan:
+		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 ((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++;
+		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;
+		}
+	}
+
+Found:
+	fat_brelse(sb, bh);
+	res = long_slots + 1;
+	*spos = cpos - sizeof(struct msdos_dir_entry);
+	*lpos = cpos - res*sizeof(struct msdos_dir_entry);
+	MSDOS_I(inode)->i_last_pos = cpos;
+exit:
+	if (unicode) {
+		free_page((unsigned long) unicode);
+	}
+	return res;
+EODir:
+	if (!MSDOS_I(inode)->i_last_pos)
+		goto exit;
+	if (barrier) {
+		MSDOS_I(inode)->i_last_pos = 0;
+		goto exit;
+	}
+	barrier = MSDOS_I(inode)->i_last_pos;
+	cpos = 0;
+	goto rescan;
+}
+
+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;
+	unsigned long *furrfu = &lpos;
+	unsigned long dummy;
+
+	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) {
+			dummy = 2;
+			furrfu = &dummy;
+			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;
 		}
-		ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);	
+		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, *furrfu, 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;
+		}
+		if (filldir(dirent, longname, long_len, *furrfu, inum) < 0)
+			goto FillFailed;
+	}
+
+RecEnd:
+	furrfu = &lpos;
+	filp->f_pos = cpos;
+	goto GetNew;
+EODir:
+	filp->f_pos = cpos;
+FillFailed:
 	if (bh)
 		fat_brelse(sb, bh);
 	if (unicode) {
@@ -344,39 +490,17 @@
 	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 +562,7 @@
 			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 +571,7 @@
 		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 */
@@ -459,6 +583,62 @@
 	}
 
 	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;
 }
 
 /*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)