patch-2.1.43 linux/fs/readdir.c
Next file: linux/fs/romfs/inode.c
Previous file: linux/fs/read_write.c
Back to the patch index
Back to the overall index
- Lines: 267
- Date:
Thu Jun 12 16:22:09 1997
- Orig file:
v2.1.42/linux/fs/readdir.c
- Orig date:
Sun Jan 26 02:07:45 1997
diff -u --recursive --new-file v2.1.42/linux/fs/readdir.c linux/fs/readdir.c
@@ -1,20 +1,35 @@
/*
- * linux/fs/readdir.c
+ * fs/readdir.c
*
* Copyright (C) 1995 Linus Torvalds
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
+#ifdef CONFIG_TRANS_NAMES
+#include <linux/nametrans.h>
+#endif
+#include <linux/dalloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
+/* [T.Schoebel-Theuer] I am assuming that directories never get too large.
+ * The problem is that getdents() delivers d_offset's that can be used
+ * for lseek() by the user, so I must encode the status information for
+ * name translation and dcache baskets in the offset.
+ * Note that the linux man page getdents(2) does not mention that
+ * the d_offset is fs-specific and can be used for lseek().
+ */
+#define BASKET_BIT (1<<30) /* 31 is already used by affs */
+#define TRANS_BIT (1<<29)
+
/*
* Traditional linux readdir() handling..
*
@@ -35,6 +50,9 @@
struct readdir_callback {
struct old_linux_dirent * dirent;
+ struct file * file;
+ int translate;
+ off_t oldoffset;
int count;
};
@@ -47,11 +65,26 @@
return -EINVAL;
buf->count++;
dirent = buf->dirent;
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+#ifdef CONFIG_TRANS_NAMES
+ if(!buf->translate) {
+ char * cut;
+#ifdef CONFIG_TRANS_RESTRICT
+ struct inode * inode = buf->file->f_inode;
+ cut = testname(inode && inode->i_gid != CONFIG_TRANS_GID, dirent->d_name);
+#else
+ cut = testname(1, dirent->d_name);
+#endif
+ if(cut) {
+ put_user(0, cut);
+ buf->translate = 1;
+ }
+ }
+#endif
put_user(ino, &dirent->d_ino);
put_user(offset, &dirent->d_offset);
put_user(namlen, &dirent->d_namlen);
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
return 0;
}
@@ -60,6 +93,7 @@
int error = -EBADF;
struct file * file;
struct readdir_callback buf;
+ off_t oldpos;
lock_kernel();
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
@@ -70,11 +104,21 @@
error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent));
if (error)
goto out;
- buf.count = 0;
+ oldpos = file->f_pos;
+ buf.file = file;
buf.dirent = dirent;
+ buf.count = 0;
+ buf.translate = 0;
+ if(file->f_pos & TRANS_BIT) {
+ file->f_pos &= ~TRANS_BIT;
+ buf.translate = 1;
+ }
error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir);
if (error < 0)
goto out;
+ if(buf.translate) {
+ file->f_pos = oldpos | TRANS_BIT;
+ }
error = buf.count;
out:
unlock_kernel();
@@ -95,8 +139,11 @@
struct getdents_callback {
struct linux_dirent * current_dir;
struct linux_dirent * previous;
+ struct file * file;
int count;
- int error;
+ int error;
+ int restricted;
+ int do_preload;
};
static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
@@ -105,18 +152,51 @@
struct getdents_callback * buf = (struct getdents_callback *) __buf;
int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
- buf->error = -EINVAL; /* only used if we fail.. */
+ /* Do not touch buf->error any more if everything is ok! */
if (reclen > buf->count)
- return -EINVAL;
- dirent = buf->previous;
- if (dirent)
- put_user(offset, &dirent->d_off);
+ return (buf->error = -EINVAL);
+#ifdef CONFIG_DCACHE_PRELOAD
+ if(buf->do_preload && (name[0] != '.' || namlen > 2)) {
+ struct qstr qname = { name, namlen };
+ struct inode * dir = buf->file->f_inode;
+ d_entry_preliminary(dir->i_dentry, &qname, ino);
+ }
+#endif
dirent = buf->current_dir;
- buf->previous = dirent;
- put_user(ino, &dirent->d_ino);
- put_user(reclen, &dirent->d_reclen);
copy_to_user(dirent->d_name, name, namlen);
put_user(0, dirent->d_name + namlen);
+#ifdef CONFIG_TRANS_NAMES
+ {
+ char * cut;
+#ifdef CONFIG_TRANS_RESTRICT
+ cut = testname(buf->restricted, dirent->d_name);
+#else
+ cut = testname(1, dirent->d_name);
+#endif
+ if(cut) {
+ int newlen = (int)cut - (int)dirent->d_name;
+ int newreclen = ROUND_UP(NAME_OFFSET(dirent) + newlen + 1);
+ /* Either both must fit or none. This way we need
+ * no status information in f_pos */
+ if (reclen+newlen > buf->count)
+ return -EINVAL;
+ put_user(0, cut);
+ put_user(ino, &dirent->d_ino);
+ put_user(newreclen, &dirent->d_reclen);
+ put_user(offset, &dirent->d_off);
+ ((char *) dirent) += newreclen;
+ buf->count -= newreclen;
+ put_user(offset, &dirent->d_off);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ }
+ }
+#endif
+ put_user(ino, &dirent->d_ino);
+ put_user(reclen, &dirent->d_reclen);
+ if (buf->previous)
+ put_user(buf->file->f_pos, &buf->previous->d_off);
+ buf->previous = dirent;
((char *) dirent) += reclen;
buf->current_dir = dirent;
buf->count -= reclen;
@@ -126,7 +206,6 @@
asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
{
struct file * file;
- struct linux_dirent * lastdirent;
struct getdents_callback buf;
int error = -EBADF;
@@ -139,18 +218,72 @@
error = verify_area(VERIFY_WRITE, dirent, count);
if (error)
goto out;
+ buf.file = file;
buf.current_dir = (struct linux_dirent *) dirent;
buf.previous = NULL;
buf.count = count;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
- if (error < 0)
- goto out;
- lastdirent = buf.previous;
- if (!lastdirent) {
+ buf.restricted = 0;
+#ifdef CONFIG_TRANS_RESTRICT
+ buf.restricted = file->f_inode && file->f_inode->i_gid != CONFIG_TRANS_GID;
+#endif
+ buf.do_preload = 0;
+#ifdef CONFIG_DCACHE_PRELOAD
+ if(file->f_inode && file->f_inode->i_dentry &&
+ !(file->f_inode->i_sb->s_type->fs_flags & (FS_NO_DCACHE|FS_NO_PRELIM)) &&
+ !(file->f_inode->i_dentry->d_flag & D_PRELOADED))
+ buf.do_preload = 1;
+#endif
+
+ if(!(file->f_pos & BASKET_BIT)) {
+ int oldcount;
+ do {
+ oldcount = buf.count;
+ error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
+ if (error < 0)
+ goto out;
+ } while(!buf.error && buf.count != oldcount);
+ }
+ if(!buf.error) {
+ int nr = 0;
+ struct dentry * list = file->f_inode ?
+ d_basket(file->f_inode->i_dentry) : NULL;
+ struct dentry * ptr = list;
+#ifdef CONFIG_DCACHE_PRELOAD
+ if(buf.do_preload) {
+ buf.do_preload = 0;
+ file->f_inode->i_dentry->d_flag |= D_PRELOADED;
+ }
+#endif
+ if(ptr) {
+ if(!(file->f_pos & BASKET_BIT))
+ file->f_pos = BASKET_BIT;
+ do {
+ struct dentry * next = ptr->d_basket_next;
+ struct inode * inode;
+ /* vfs_locks() are missing here */
+ inode = d_inode(&ptr);
+ if(inode) {
+ nr++;
+ if(nr > (file->f_pos & ~BASKET_BIT)) {
+ int err = filldir(&buf, ptr->d_name,
+ ptr->d_len,
+ file->f_pos,
+ inode->i_ino);
+ if(err)
+ break;
+ file->f_pos++;
+ }
+ iput(inode);
+ }
+ ptr = next;
+ } while(ptr != list);
+ }
+ }
+ if (!buf.previous) {
error = buf.error;
} else {
- put_user(file->f_pos, &lastdirent->d_off);
+ put_user(file->f_pos, &buf.previous->d_off);
error = count - buf.count;
}
out:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov