1 /* 2 * linux/fs/readdir.c 3 * 4 * Copyright (C) 1995 Linus Torvalds 5 */ 6 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 #include <linux/time.h> 10 #include <linux/mm.h> 11 #include <linux/errno.h> 12 #include <linux/stat.h> 13 #include <linux/file.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_path.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 mutex_lock(&inode->i_mutex); 34 res = -ENOENT; 35 if (!IS_DEADDIR(inode)) { 36 res = file->f_op->readdir(file, buf, filler); 37 file_accessed(file); 38 } 39 mutex_unlock(&inode->i_mutex); 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 56 #ifdef __ARCH_WANT_OLD_READDIR 57 58 struct old_linux_dirent { 59 unsigned long d_ino; 60 unsigned long d_offset; 61 unsigned short d_namlen; 62 char d_name[1]; 63 }; 64 65 struct readdir_callback { 66 struct old_linux_dirent __user * dirent; 67 int result; 68 }; 69 70 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, 71 u64 ino, unsigned int d_type) 72 { 73 struct readdir_callback * buf = (struct readdir_callback *) __buf; 74 struct old_linux_dirent __user * dirent; 75 unsigned long d_ino; 76 77 if (buf->result) 78 return -EINVAL; 79 d_ino = ino; 80 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) 81 return -EOVERFLOW; 82 buf->result++; 83 dirent = buf->dirent; 84 if (!access_ok(VERIFY_WRITE, dirent, 85 (unsigned long)(dirent->d_name + namlen + 1) - 86 (unsigned long)dirent)) 87 goto efault; 88 if ( __put_user(d_ino, &dirent->d_ino) || 89 __put_user(offset, &dirent->d_offset) || 90 __put_user(namlen, &dirent->d_namlen) || 91 __copy_to_user(dirent->d_name, name, namlen) || 92 __put_user(0, dirent->d_name + namlen)) 93 goto efault; 94 return 0; 95 efault: 96 buf->result = -EFAULT; 97 return -EFAULT; 98 } 99 100 asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * dirent, unsigned int count) 101 { 102 int error; 103 struct file * file; 104 struct readdir_callback buf; 105 106 error = -EBADF; 107 file = fget(fd); 108 if (!file) 109 goto out; 110 111 buf.result = 0; 112 buf.dirent = dirent; 113 114 error = vfs_readdir(file, fillonedir, &buf); 115 if (error >= 0) 116 error = buf.result; 117 118 fput(file); 119 out: 120 return error; 121 } 122 123 #endif /* __ARCH_WANT_OLD_READDIR */ 124 125 /* 126 * New, all-improved, singing, dancing, iBCS2-compliant getdents() 127 * interface. 128 */ 129 struct linux_dirent { 130 unsigned long d_ino; 131 unsigned long d_off; 132 unsigned short d_reclen; 133 char d_name[1]; 134 }; 135 136 struct getdents_callback { 137 struct linux_dirent __user * current_dir; 138 struct linux_dirent __user * previous; 139 int count; 140 int error; 141 }; 142 143 static int filldir(void * __buf, const char * name, int namlen, loff_t offset, 144 u64 ino, unsigned int d_type) 145 { 146 struct linux_dirent __user * dirent; 147 struct getdents_callback * buf = (struct getdents_callback *) __buf; 148 unsigned long d_ino; 149 int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(long)); 150 151 buf->error = -EINVAL; /* only used if we fail.. */ 152 if (reclen > buf->count) 153 return -EINVAL; 154 d_ino = ino; 155 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) 156 return -EOVERFLOW; 157 dirent = buf->previous; 158 if (dirent) { 159 if (__put_user(offset, &dirent->d_off)) 160 goto efault; 161 } 162 dirent = buf->current_dir; 163 if (__put_user(d_ino, &dirent->d_ino)) 164 goto efault; 165 if (__put_user(reclen, &dirent->d_reclen)) 166 goto efault; 167 if (copy_to_user(dirent->d_name, name, namlen)) 168 goto efault; 169 if (__put_user(0, dirent->d_name + namlen)) 170 goto efault; 171 if (__put_user(d_type, (char __user *) dirent + reclen - 1)) 172 goto efault; 173 buf->previous = dirent; 174 dirent = (void __user *)dirent + reclen; 175 buf->current_dir = dirent; 176 buf->count -= reclen; 177 return 0; 178 efault: 179 buf->error = -EFAULT; 180 return -EFAULT; 181 } 182 183 asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * dirent, unsigned int count) 184 { 185 struct file * file; 186 struct linux_dirent __user * lastdirent; 187 struct getdents_callback buf; 188 int error; 189 190 error = -EFAULT; 191 if (!access_ok(VERIFY_WRITE, dirent, count)) 192 goto out; 193 194 error = -EBADF; 195 file = fget(fd); 196 if (!file) 197 goto out; 198 199 buf.current_dir = dirent; 200 buf.previous = NULL; 201 buf.count = count; 202 buf.error = 0; 203 204 error = vfs_readdir(file, filldir, &buf); 205 if (error < 0) 206 goto out_putf; 207 error = buf.error; 208 lastdirent = buf.previous; 209 if (lastdirent) { 210 if (put_user(file->f_pos, &lastdirent->d_off)) 211 error = -EFAULT; 212 else 213 error = count - buf.count; 214 } 215 216 out_putf: 217 fput(file); 218 out: 219 return error; 220 } 221 222 struct getdents_callback64 { 223 struct linux_dirent64 __user * current_dir; 224 struct linux_dirent64 __user * previous; 225 int count; 226 int error; 227 }; 228 229 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, 230 u64 ino, unsigned int d_type) 231 { 232 struct linux_dirent64 __user *dirent; 233 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; 234 int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64)); 235 236 buf->error = -EINVAL; /* only used if we fail.. */ 237 if (reclen > buf->count) 238 return -EINVAL; 239 dirent = buf->previous; 240 if (dirent) { 241 if (__put_user(offset, &dirent->d_off)) 242 goto efault; 243 } 244 dirent = buf->current_dir; 245 if (__put_user(ino, &dirent->d_ino)) 246 goto efault; 247 if (__put_user(0, &dirent->d_off)) 248 goto efault; 249 if (__put_user(reclen, &dirent->d_reclen)) 250 goto efault; 251 if (__put_user(d_type, &dirent->d_type)) 252 goto efault; 253 if (copy_to_user(dirent->d_name, name, namlen)) 254 goto efault; 255 if (__put_user(0, dirent->d_name + namlen)) 256 goto efault; 257 buf->previous = dirent; 258 dirent = (void __user *)dirent + reclen; 259 buf->current_dir = dirent; 260 buf->count -= reclen; 261 return 0; 262 efault: 263 buf->error = -EFAULT; 264 return -EFAULT; 265 } 266 267 asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * dirent, unsigned int count) 268 { 269 struct file * file; 270 struct linux_dirent64 __user * lastdirent; 271 struct getdents_callback64 buf; 272 int error; 273 274 error = -EFAULT; 275 if (!access_ok(VERIFY_WRITE, dirent, count)) 276 goto out; 277 278 error = -EBADF; 279 file = fget(fd); 280 if (!file) 281 goto out; 282 283 buf.current_dir = dirent; 284 buf.previous = NULL; 285 buf.count = count; 286 buf.error = 0; 287 288 error = vfs_readdir(file, filldir64, &buf); 289 if (error < 0) 290 goto out_putf; 291 error = buf.error; 292 lastdirent = buf.previous; 293 if (lastdirent) { 294 typeof(lastdirent->d_off) d_off = file->f_pos; 295 error = -EFAULT; 296 if (__put_user(d_off, &lastdirent->d_off)) 297 goto out_putf; 298 error = count - buf.count; 299 } 300 301 out_putf: 302 fput(file); 303 out: 304 return error; 305 } 306