patch-2.4.19 linux-2.4.19/fs/readdir.c

Next file: linux-2.4.19/fs/reiserfs/bitmap.c
Previous file: linux-2.4.19/fs/read_write.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/fs/readdir.c linux-2.4.19/fs/readdir.c
@@ -33,6 +33,64 @@
 	return res;
 }
 
+int dcache_dir_open(struct inode *inode, struct file *file)
+{
+	static struct qstr cursor_name = {len:1, name:"."};
+
+	file->private_data = d_alloc(file->f_dentry, &cursor_name);
+
+	return file->private_data ? 0 : -ENOMEM;
+}
+
+int dcache_dir_close(struct inode *inode, struct file *file)
+{
+	dput(file->private_data);
+	return 0;
+}
+
+loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+	down(&file->f_dentry->d_inode->i_sem);
+	switch (origin) {
+		case 1:
+			offset += file->f_pos;
+		case 0:
+			if (offset >= 0)
+				break;
+		default:
+			up(&file->f_dentry->d_inode->i_sem);
+			return -EINVAL;
+	}
+	if (offset != file->f_pos) {
+		file->f_pos = offset;
+		if (file->f_pos >= 2) {
+			struct list_head *p;
+			struct dentry *cursor = file->private_data;
+			loff_t n = file->f_pos - 2;
+
+			spin_lock(&dcache_lock);
+			p = file->f_dentry->d_subdirs.next;
+			while (n && p != &file->f_dentry->d_subdirs) {
+				struct dentry *next;
+				next = list_entry(p, struct dentry, d_child);
+				if (!list_empty(&next->d_hash) && next->d_inode)
+					n--;
+				p = p->next;
+			}
+			list_del(&cursor->d_child);
+			list_add_tail(&cursor->d_child, p);
+			spin_unlock(&dcache_lock);
+		}
+	}
+	up(&file->f_dentry->d_inode->i_sem);
+	return offset;
+}
+
+int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
+{
+	return 0;
+}
+
 /*
  * Directory is locked and all positive dentries in it are safe, since
  * for ramfs-type trees they can't go away without unlink() or rmdir(),
@@ -41,62 +99,65 @@
 
 int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
 {
-	int i;
 	struct dentry *dentry = filp->f_dentry;
+	struct dentry *cursor = filp->private_data;
+	struct list_head *p, *q = &cursor->d_child;
+	ino_t ino;
+	int i = filp->f_pos;
 
-	i = filp->f_pos;
 	switch (i) {
 		case 0:
-			if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
+			ino = dentry->d_inode->i_ino;
+			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
 				break;
-			i++;
 			filp->f_pos++;
+			i++;
 			/* fallthrough */
 		case 1:
-			if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+			spin_lock(&dcache_lock);
+			ino = dentry->d_parent->d_inode->i_ino;
+			spin_unlock(&dcache_lock);
+			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
 				break;
-			i++;
 			filp->f_pos++;
+			i++;
 			/* fallthrough */
-		default: {
-			struct list_head *list;
-			int j = i-2;
-
+		default:
 			spin_lock(&dcache_lock);
-			list = dentry->d_subdirs.next;
-
-			for (;;) {
-				if (list == &dentry->d_subdirs) {
-					spin_unlock(&dcache_lock);
-					return 0;
-				}
-				if (!j)
-					break;
-				j--;
-				list = list->next;
+			if (filp->f_pos == 2) {
+				list_del(q);
+				list_add(q, &dentry->d_subdirs);
 			}
-
-			while(1) {
-				struct dentry *de = list_entry(list, struct dentry, d_child);
-
-				if (!list_empty(&de->d_hash) && de->d_inode) {
-					spin_unlock(&dcache_lock);
-					if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0)
-						break;
-					spin_lock(&dcache_lock);
-				}
-				filp->f_pos++;
-				list = list->next;
-				if (list != &dentry->d_subdirs)
+			for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
+				struct dentry *next;
+				next = list_entry(p, struct dentry, d_child);
+				if (list_empty(&next->d_hash) || !next->d_inode)
 					continue;
+
 				spin_unlock(&dcache_lock);
-				break;
+				if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
+					return 0;
+				spin_lock(&dcache_lock);
+				/* next is still alive */
+				list_del(q);
+				list_add(q, p);
+				p = q;
+				filp->f_pos++;
 			}
-		}
+			spin_unlock(&dcache_lock);
 	}
 	return 0;
 }
 
+struct file_operations dcache_dir_ops = {
+	open:		dcache_dir_open,
+	release:	dcache_dir_close,
+	llseek:		dcache_dir_lseek,
+	read:		generic_read_dir,
+	readdir:	dcache_readdir,
+	fsync:		dcache_dir_fsync,
+};
+
 /*
  * Traditional linux readdir() handling..
  *

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