1 /* 2 * linux/fs/readdir.c 3 * 4 * Copyright (C) 1995 Linus Torvalds 5 */ 6 7 #include <linux/module.h> 8 #include <linux/time.h> 9 #include <linux/mm.h> 10 #include <linux/errno.h> 11 #include <linux/stat.h> 12 #include <linux/file.h> 13 #include <linux/smp_lock.h> 14 #include <linux/fs.h> 15 #include <linux/dirent.h> 16 #include <linux/security.h> 17 #include <linux/syscalls.h> 18 #include <linux/unistd.h> 19 20 #include <asm/uaccess.h> 21 22 int vfs_readdir(struct file *file, filldir_t filler, void *buf) 23 { 24 struct inode *inode = file->f_dentry->d_inode; 25 int res = -ENOTDIR; 26 if (!file->f_op || !file->f_op->readdir) 27 goto out; 28 29 res = security_file_permission(file, MAY_READ); 30 if (res) 31 goto out; 32 33 down(&inode->i_sem); 34 res = -ENOENT; 35 if (!IS_DEADDIR(inode)) { 36 res = file->f_op->readdir(file, buf, filler); 37 file_accessed(file); 38 } 39 up(&inode->i_sem); 40 out: 41 return res; 42 } 43 44 EXPORT_SYMBOL(vfs_readdir); 45 46 /* 47 * Traditional linux readdir() handling.. 48 * 49 * "count=1" is a special case, meaning that the buffer is one 50 * dirent-structure in size and that the code can't handle more 51 * anyway. Thus the special "fillonedir()" function for that 52 * case (the low-level handlers don't need to care about this). 53 */ 54 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) 55 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) 56 57 #ifdef __ARCH_WANT_OLD_READDIR 58 59 struct old_linux_dirent { 60 unsigned long d_ino; 61 unsigned long d_offset; 62 unsigned short d_namlen; 63 char d_name[1]; 64 }; 65 66 struct readdir_callback { 67 struct old_linux_dirent __user * dirent; 68 int result; 69 }; 70 71 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, 72 ino_t ino, unsigned int d_type) 73 { 74 struct readdir_callback * buf = (struct readdir_callback *) __buf; 75 struct old_linux_dirent __user * dirent; 76 77 if (buf->result) 78 return -EINVAL; 79 buf->result++; 80 dirent = buf->dirent; 81 if (!access_ok(VERIFY_WRITE, dirent, 82 (unsigned long)(dirent->d_name + namlen + 1) - 83 (unsigned long)dirent)) 84 goto efault; 85 if ( __put_user(ino, &dirent->d_ino) || 86 __put_user(offset, &dirent->d_offset) || 87 __put_user(namlen, &dirent->d_namlen) || 88 __copy_to_user(dirent->d_name, name, namlen) || 89 __put_user(0, dirent->d_name + namlen)) 90 goto efault; 91 return 0; 92 efault: 93 buf->result = -EFAULT; 94 return -EFAULT; 95 } 96 97 asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * dirent, unsigned int count) 98 { 99 int error; 100 struct file * file; 101 struct readdir_callback buf; 102 103 error = -EBADF; 104 file = fget(fd); 105 if (!file) 106 goto out; 107 108 buf.result = 0; 109 buf.dirent = dirent; 110 111 error = vfs_readdir(file, fillonedir, &buf); 112 if (error >= 0) 113 error = buf.result; 114 115 fput(file); 116 out: 117 return error; 118 } 119 120 #endif /* __ARCH_WANT_OLD_READDIR */ 121 122 /* 123 * New, all-improved, singing, dancing, iBCS2-compliant getdents() 124 * interface. 125 */ 126 struct linux_dirent { 127 unsigned long d_ino; 128 unsigned long d_off; 129 unsigned short d_reclen; 130 char d_name[1]; 131 }; 132 133 struct getdents_callback { 134 struct linux_dirent __user * current_dir; 135 struct linux_dirent __user * previous; 136 int count; 137 int error; 138 }; 139 140 static int filldir(void * __buf, const char * name, int namlen, loff_t offset, 141 ino_t ino, unsigned int d_type) 142 { 143 struct linux_dirent __user * dirent; 144 struct getdents_callback * buf = (struct getdents_callback *) __buf; 145 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 2); 146 147 buf->error = -EINVAL; /* only used if we fail.. */ 148 if (reclen > buf->count) 149 return -EINVAL; 150 dirent = buf->previous; 151 if (dirent) { 152 if (__put_user(offset, &dirent->d_off)) 153 goto efault; 154 } 155 dirent = buf->current_dir; 156 if (__put_user(ino, &dirent->d_ino)) 157 goto efault; 158 if (__put_user(reclen, &dirent->d_reclen)) 159 goto efault; 160 if (copy_to_user(dirent->d_name, name, namlen)) 161 goto efault; 162 if (__put_user(0, dirent->d_name + namlen)) 163 goto efault; 164 if (__put_user(d_type, (char __user *) dirent + reclen - 1)) 165 goto efault; 166 buf->previous = dirent; 167 dirent = (void __user *)dirent + reclen; 168 buf->current_dir = dirent; 169 buf->count -= reclen; 170 return 0; 171 efault: 172 buf->error = -EFAULT; 173 return -EFAULT; 174 } 175 176 asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * dirent, unsigned int count) 177 { 178 struct file * file; 179 struct linux_dirent __user * lastdirent; 180 struct getdents_callback buf; 181 int error; 182 183 error = -EFAULT; 184 if (!access_ok(VERIFY_WRITE, dirent, count)) 185 goto out; 186 187 error = -EBADF; 188 file = fget(fd); 189 if (!file) 190 goto out; 191 192 buf.current_dir = dirent; 193 buf.previous = NULL; 194 buf.count = count; 195 buf.error = 0; 196 197 error = vfs_readdir(file, filldir, &buf); 198 if (error < 0) 199 goto out_putf; 200 error = buf.error; 201 lastdirent = buf.previous; 202 if (lastdirent) { 203 if (put_user(file->f_pos, &lastdirent->d_off)) 204 error = -EFAULT; 205 else 206 error = count - buf.count; 207 } 208 209 out_putf: 210 fput(file); 211 out: 212 return error; 213 } 214 215 #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) 216 217 struct getdents_callback64 { 218 struct linux_dirent64 __user * current_dir; 219 struct linux_dirent64 __user * previous; 220 int count; 221 int error; 222 }; 223 224 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, 225 ino_t ino, unsigned int d_type) 226 { 227 struct linux_dirent64 __user *dirent; 228 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; 229 int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); 230 231 buf->error = -EINVAL; /* only used if we fail.. */ 232 if (reclen > buf->count) 233 return -EINVAL; 234 dirent = buf->previous; 235 if (dirent) { 236 if (__put_user(offset, &dirent->d_off)) 237 goto efault; 238 } 239 dirent = buf->current_dir; 240 if (__put_user(ino, &dirent->d_ino)) 241 goto efault; 242 if (__put_user(0, &dirent->d_off)) 243 goto efault; 244 if (__put_user(reclen, &dirent->d_reclen)) 245 goto efault; 246 if (__put_user(d_type, &dirent->d_type)) 247 goto efault; 248 if (copy_to_user(dirent->d_name, name, namlen)) 249 goto efault; 250 if (__put_user(0, dirent->d_name + namlen)) 251 goto efault; 252 buf->previous = dirent; 253 dirent = (void __user *)dirent + reclen; 254 buf->current_dir = dirent; 255 buf->count -= reclen; 256 return 0; 257 efault: 258 buf->error = -EFAULT; 259 return -EFAULT; 260 } 261 262 asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * dirent, unsigned int count) 263 { 264 struct file * file; 265 struct linux_dirent64 __user * lastdirent; 266 struct getdents_callback64 buf; 267 int error; 268 269 error = -EFAULT; 270 if (!access_ok(VERIFY_WRITE, dirent, count)) 271 goto out; 272 273 error = -EBADF; 274 file = fget(fd); 275 if (!file) 276 goto out; 277 278 buf.current_dir = dirent; 279 buf.previous = NULL; 280 buf.count = count; 281 buf.error = 0; 282 283 error = vfs_readdir(file, filldir64, &buf); 284 if (error < 0) 285 goto out_putf; 286 error = buf.error; 287 lastdirent = buf.previous; 288 if (lastdirent) { 289 typeof(lastdirent->d_off) d_off = file->f_pos; 290 error = -EFAULT; 291 if (__put_user(d_off, &lastdirent->d_off)) 292 goto out_putf; 293 error = count - buf.count; 294 } 295 296 out_putf: 297 fput(file); 298 out: 299 return error; 300 } 301