diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
commit | 1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch) | |
tree | 141e31f89f18b9fe0831f31852e0435ceaccafc5 /fs/readdir.c | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'fs/readdir.c')
-rw-r--r-- | fs/readdir.c | 102 |
1 files changed, 94 insertions, 8 deletions
diff --git a/fs/readdir.c b/fs/readdir.c index e1e90c113..cd8f7ad3d 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -13,9 +13,7 @@ #include <asm/uaccess.h> -int vfs_readdir(struct file *file, - int (*filler)(void *,const char *,int,off_t,ino_t), - void *buf) +int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_dentry->d_inode; int res = -ENOTDIR; @@ -49,13 +47,13 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) + if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) break; i++; filp->f_pos++; /* fallthrough */ case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) + if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) break; i++; filp->f_pos++; @@ -83,7 +81,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) 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) < 0) + 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); } @@ -124,7 +122,8 @@ struct readdir_callback { int count; }; -static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) { struct readdir_callback * buf = (struct readdir_callback *) __buf; struct old_linux_dirent * dirent; @@ -184,7 +183,8 @@ struct getdents_callback { int error; }; -static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +static int filldir(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) { struct linux_dirent * dirent; struct getdents_callback * buf = (struct getdents_callback *) __buf; @@ -240,3 +240,89 @@ out_putf: out: return error; } + +/* + * And even better one including d_type field and 64bit d_ino and d_off. + */ +struct linux_dirent64 { + u64 d_ino; + s64 d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) + +struct getdents_callback64 { + struct linux_dirent64 * current_dir; + struct linux_dirent64 * previous; + int count; + int error; +}; + +static int filldir64(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) +{ + struct linux_dirent64 * dirent, d; + struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; + int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) { + d.d_off = offset; + copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off)); + } + dirent = buf->current_dir; + buf->previous = dirent; + memset(&d, 0, NAME_OFFSET(&d)); + d.d_ino = ino; + d.d_reclen = reclen; + d.d_type = d_type; + copy_to_user(dirent, &d, NAME_OFFSET(&d)); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent64 * lastdirent; + struct getdents_callback64 buf; + int error; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct linux_dirent64 *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir64, &buf); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + struct linux_dirent64 d; + d.d_off = file->f_pos; + copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off)); + error = count - buf.count; + } + +out_putf: + fput(file); +out: + return error; +} + |