1/* 2 * linux/fs/readdir.c 3 * 4 * Copyright (C) 1995 Linus Torvalds 5 */ 6 7#include <linux/sched.h> 8#include <linux/mm.h> 9#include <linux/errno.h> 10#include <linux/stat.h> 11#include <linux/file.h> 12#include <linux/smp_lock.h> 13 14#include <asm/uaccess.h> 15 16int vfs_readdir(struct file *file, filldir_t filler, void *buf) 17{ 18 struct inode *inode = file->f_dentry->d_inode; 19 int res = -ENOTDIR; 20 if (!file->f_op || !file->f_op->readdir) 21 goto out; 22 down(&inode->i_sem); 23 down(&inode->i_zombie); 24 res = -ENOENT; 25 if (!IS_DEADDIR(inode)) { 26 lock_kernel(); 27 res = file->f_op->readdir(file, buf, filler); 28 unlock_kernel(); 29 } 30 up(&inode->i_zombie); 31 up(&inode->i_sem); 32out: 33 return res; 34} 35 36int dcache_dir_open(struct inode *inode, struct file *file) 37{ 38 static struct qstr cursor_name = {len:1, name:"."}; 39 40 file->private_data = d_alloc(file->f_dentry, &cursor_name); 41 42 return file->private_data ? 0 : -ENOMEM; 43} 44 45int dcache_dir_close(struct inode *inode, struct file *file) 46{ 47 dput(file->private_data); 48 return 0; 49} 50 51loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) 52{ 53 down(&file->f_dentry->d_inode->i_sem); 54 switch (origin) { 55 case 1: 56 offset += file->f_pos; 57 case 0: 58 if (offset >= 0) 59 break; 60 default: 61 up(&file->f_dentry->d_inode->i_sem); 62 return -EINVAL; 63 } 64 if (offset != file->f_pos) { 65 file->f_pos = offset; 66 if (file->f_pos >= 2) { 67 struct list_head *p; 68 struct dentry *cursor = file->private_data; 69 loff_t n = file->f_pos - 2; 70 71 spin_lock(&dcache_lock); 72 p = file->f_dentry->d_subdirs.next; 73 while (n && p != &file->f_dentry->d_subdirs) { 74 struct dentry *next; 75 next = list_entry(p, struct dentry, d_child); 76 if (!list_empty(&next->d_hash) && next->d_inode) 77 n--; 78 p = p->next; 79 } 80 list_del(&cursor->d_child); 81 list_add_tail(&cursor->d_child, p); 82 spin_unlock(&dcache_lock); 83 } 84 } 85 up(&file->f_dentry->d_inode->i_sem); 86 return offset; 87} 88 89int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync) 90{ 91 return 0; 92} 93 94/* 95 * Directory is locked and all positive dentries in it are safe, since 96 * for ramfs-type trees they can't go away without unlink() or rmdir(), 97 * both impossible due to the lock on directory. 98 */ 99 100int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) 101{ 102 struct dentry *dentry = filp->f_dentry; 103 struct dentry *cursor = filp->private_data; 104 struct list_head *p, *q = &cursor->d_child; 105 ino_t ino; 106 int i = filp->f_pos; 107 108 switch (i) { 109 case 0: 110 ino = dentry->d_inode->i_ino; 111 if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) 112 break; 113 filp->f_pos++; 114 i++; 115 /* fallthrough */ 116 case 1: 117 spin_lock(&dcache_lock); 118 ino = dentry->d_parent->d_inode->i_ino; 119 spin_unlock(&dcache_lock); 120 if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) 121 break; 122 filp->f_pos++; 123 i++; 124 /* fallthrough */ 125 default: 126 spin_lock(&dcache_lock); 127 if (filp->f_pos == 2) { 128 list_del(q); 129 list_add(q, &dentry->d_subdirs); 130 } 131 for (p=q->next; p != &dentry->d_subdirs; p=p->next) { 132 struct dentry *next; 133 next = list_entry(p, struct dentry, d_child); 134 if (list_empty(&next->d_hash) || !next->d_inode) 135 continue; 136 137 spin_unlock(&dcache_lock); 138 if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0) 139 return 0; 140 spin_lock(&dcache_lock); 141 /* next is still alive */ 142 list_del(q); 143 list_add(q, p); 144 p = q; 145 filp->f_pos++; 146 } 147 spin_unlock(&dcache_lock); 148 } 149 return 0; 150} 151 152struct file_operations dcache_dir_ops = { 153 open: dcache_dir_open, 154 release: dcache_dir_close, 155 llseek: dcache_dir_lseek, 156 read: generic_read_dir, 157 readdir: dcache_readdir, 158 fsync: dcache_dir_fsync, 159}; 160 161/* 162 * Traditional linux readdir() handling.. 163 * 164 * "count=1" is a special case, meaning that the buffer is one 165 * dirent-structure in size and that the code can't handle more 166 * anyway. Thus the special "fillonedir()" function for that 167 * case (the low-level handlers don't need to care about this). 168 */ 169#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) 170#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) 171 172#ifndef __ia64__ 173 174struct old_linux_dirent { 175 unsigned long d_ino; 176 unsigned long d_offset; 177 unsigned short d_namlen; 178 char d_name[1]; 179}; 180 181struct readdir_callback { 182 struct old_linux_dirent * dirent; 183 int count; 184}; 185 186static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, 187 ino_t ino, unsigned int d_type) 188{ 189 struct readdir_callback * buf = (struct readdir_callback *) __buf; 190 struct old_linux_dirent * dirent; 191 192 if (buf->count) 193 return -EINVAL; 194 buf->count++; 195 dirent = buf->dirent; 196 put_user(ino, &dirent->d_ino); 197 put_user(offset, &dirent->d_offset); 198 put_user(namlen, &dirent->d_namlen); 199 copy_to_user(dirent->d_name, name, namlen); 200 put_user(0, dirent->d_name + namlen); 201 return 0; 202} 203 204asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) 205{ 206 int error; 207 struct file * file; 208 struct readdir_callback buf; 209 210 error = -EBADF; 211 file = fget(fd); 212 if (!file) 213 goto out; 214 215 buf.count = 0; 216 buf.dirent = dirent; 217 218 error = vfs_readdir(file, fillonedir, &buf); 219 if (error >= 0) 220 error = buf.count; 221 222 fput(file); 223out: 224 return error; 225} 226 227#endif /* !__ia64__ */ 228 229/* 230 * New, all-improved, singing, dancing, iBCS2-compliant getdents() 231 * interface. 232 */ 233struct linux_dirent { 234 unsigned long d_ino; 235 unsigned long d_off; 236 unsigned short d_reclen; 237 char d_name[1]; 238}; 239 240struct getdents_callback { 241 struct linux_dirent * current_dir; 242 struct linux_dirent * previous; 243 int count; 244 int error; 245}; 246 247static int filldir(void * __buf, const char * name, int namlen, loff_t offset, 248 ino_t ino, unsigned int d_type) 249{ 250 struct linux_dirent * dirent; 251 struct getdents_callback * buf = (struct getdents_callback *) __buf; 252 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); 253 254 buf->error = -EINVAL; /* only used if we fail.. */ 255 if (reclen > buf->count) 256 return -EINVAL; 257 dirent = buf->previous; 258 if (dirent) 259 put_user(offset, &dirent->d_off); 260 dirent = buf->current_dir; 261 buf->previous = dirent; 262 put_user(ino, &dirent->d_ino); 263 put_user(reclen, &dirent->d_reclen); 264 copy_to_user(dirent->d_name, name, namlen); 265 put_user(0, dirent->d_name + namlen); 266 ((char *) dirent) += reclen; 267 buf->current_dir = dirent; 268 buf->count -= reclen; 269 return 0; 270} 271 272asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count) 273{ 274 struct file * file; 275 struct linux_dirent * lastdirent; 276 struct getdents_callback buf; 277 int error; 278 279 error = -EBADF; 280 file = fget(fd); 281 if (!file) 282 goto out; 283 284 buf.current_dir = (struct linux_dirent *) dirent; 285 buf.previous = NULL; 286 buf.count = count; 287 buf.error = 0; 288 289 error = vfs_readdir(file, filldir, &buf); 290 if (error < 0) 291 goto out_putf; 292 error = buf.error; 293 lastdirent = buf.previous; 294 if (lastdirent) { 295 put_user(file->f_pos, &lastdirent->d_off); 296 error = count - buf.count; 297 } 298 299out_putf: 300 fput(file); 301out: 302 return error; 303} 304 305/* 306 * And even better one including d_type field and 64bit d_ino and d_off. 307 */ 308struct linux_dirent64 { 309 u64 d_ino; 310 s64 d_off; 311 unsigned short d_reclen; 312 unsigned char d_type; 313 char d_name[0]; 314}; 315 316#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) 317 318struct getdents_callback64 { 319 struct linux_dirent64 * current_dir; 320 struct linux_dirent64 * previous; 321 int count; 322 int error; 323}; 324 325static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, 326 ino_t ino, unsigned int d_type) 327{ 328 struct linux_dirent64 * dirent, d; 329 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; 330 int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); 331 332 buf->error = -EINVAL; /* only used if we fail.. */ 333 if (reclen > buf->count) 334 return -EINVAL; 335 dirent = buf->previous; 336 if (dirent) { 337 d.d_off = offset; 338 copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off)); 339 } 340 dirent = buf->current_dir; 341 buf->previous = dirent; 342 memset(&d, 0, NAME_OFFSET(&d)); 343 d.d_ino = ino; 344 d.d_reclen = reclen; 345 d.d_type = d_type; 346 copy_to_user(dirent, &d, NAME_OFFSET(&d)); 347 copy_to_user(dirent->d_name, name, namlen); 348 put_user(0, dirent->d_name + namlen); 349 ((char *) dirent) += reclen; 350 buf->current_dir = dirent; 351 buf->count -= reclen; 352 return 0; 353} 354 355asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count) 356{ 357 struct file * file; 358 struct linux_dirent64 * lastdirent; 359 struct getdents_callback64 buf; 360 int error; 361 362 error = -EBADF; 363 file = fget(fd); 364 if (!file) 365 goto out; 366 367 buf.current_dir = (struct linux_dirent64 *) dirent; 368 buf.previous = NULL; 369 buf.count = count; 370 buf.error = 0; 371 372 error = vfs_readdir(file, filldir64, &buf); 373 if (error < 0) 374 goto out_putf; 375 error = buf.error; 376 lastdirent = buf.previous; 377 if (lastdirent) { 378 struct linux_dirent64 d; 379 d.d_off = file->f_pos; 380 copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off)); 381 error = count - buf.count; 382 } 383 384out_putf: 385 fput(file); 386out: 387 return error; 388} 389