~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/readdir.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 16 int 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);
 32 out:
 33         return res;
 34 }
 35 
 36 /*
 37  * Directory is locked and all positive dentries in it are safe, since
 38  * for ramfs-type trees they can't go away without unlink() or rmdir(),
 39  * both impossible due to the lock on directory.
 40  */
 41 
 42 int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
 43 {
 44         int i;
 45         struct dentry *dentry = filp->f_dentry;
 46 
 47         i = filp->f_pos;
 48         switch (i) {
 49                 case 0:
 50                         if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
 51                                 break;
 52                         i++;
 53                         filp->f_pos++;
 54                         /* fallthrough */
 55                 case 1:
 56                         if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
 57                                 break;
 58                         i++;
 59                         filp->f_pos++;
 60                         /* fallthrough */
 61                 default: {
 62                         struct list_head *list;
 63                         int j = i-2;
 64 
 65                         spin_lock(&dcache_lock);
 66                         list = dentry->d_subdirs.next;
 67 
 68                         for (;;) {
 69                                 if (list == &dentry->d_subdirs) {
 70                                         spin_unlock(&dcache_lock);
 71                                         return 0;
 72                                 }
 73                                 if (!j)
 74                                         break;
 75                                 j--;
 76                                 list = list->next;
 77                         }
 78 
 79                         while(1) {
 80                                 struct dentry *de = list_entry(list, struct dentry, d_child);
 81 
 82                                 if (!list_empty(&de->d_hash) && de->d_inode) {
 83                                         spin_unlock(&dcache_lock);
 84                                         if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0)
 85                                                 break;
 86                                         spin_lock(&dcache_lock);
 87                                 }
 88                                 filp->f_pos++;
 89                                 list = list->next;
 90                                 if (list != &dentry->d_subdirs)
 91                                         continue;
 92                                 spin_unlock(&dcache_lock);
 93                                 break;
 94                         }
 95                 }
 96         }
 97         return 0;
 98 }
 99 
100 /*
101  * Traditional linux readdir() handling..
102  *
103  * "count=1" is a special case, meaning that the buffer is one
104  * dirent-structure in size and that the code can't handle more
105  * anyway. Thus the special "fillonedir()" function for that
106  * case (the low-level handlers don't need to care about this).
107  */
108 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
109 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
110 
111 #ifndef __ia64__
112 
113 struct old_linux_dirent {
114         unsigned long   d_ino;
115         unsigned long   d_offset;
116         unsigned short  d_namlen;
117         char            d_name[1];
118 };
119 
120 struct readdir_callback {
121         struct old_linux_dirent * dirent;
122         int count;
123 };
124 
125 static int fillonedir(void * __buf, const char * name, int namlen, off_t offset,
126                       ino_t ino, unsigned int d_type)
127 {
128         struct readdir_callback * buf = (struct readdir_callback *) __buf;
129         struct old_linux_dirent * dirent;
130 
131         if (buf->count)
132                 return -EINVAL;
133         buf->count++;
134         dirent = buf->dirent;
135         put_user(ino, &dirent->d_ino);
136         put_user(offset, &dirent->d_offset);
137         put_user(namlen, &dirent->d_namlen);
138         copy_to_user(dirent->d_name, name, namlen);
139         put_user(0, dirent->d_name + namlen);
140         return 0;
141 }
142 
143 asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
144 {
145         int error;
146         struct file * file;
147         struct readdir_callback buf;
148 
149         error = -EBADF;
150         file = fget(fd);
151         if (!file)
152                 goto out;
153 
154         buf.count = 0;
155         buf.dirent = dirent;
156 
157         error = vfs_readdir(file, fillonedir, &buf);
158         if (error >= 0)
159                 error = buf.count;
160 
161         fput(file);
162 out:
163         return error;
164 }
165 
166 #endif /* !__ia64__ */
167 
168 /*
169  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
170  * interface. 
171  */
172 struct linux_dirent {
173         unsigned long   d_ino;
174         unsigned long   d_off;
175         unsigned short  d_reclen;
176         char            d_name[1];
177 };
178 
179 struct getdents_callback {
180         struct linux_dirent * current_dir;
181         struct linux_dirent * previous;
182         int count;
183         int error;
184 };
185 
186 static int filldir(void * __buf, const char * name, int namlen, off_t offset,
187                    ino_t ino, unsigned int d_type)
188 {
189         struct linux_dirent * dirent;
190         struct getdents_callback * buf = (struct getdents_callback *) __buf;
191         int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
192 
193         buf->error = -EINVAL;   /* only used if we fail.. */
194         if (reclen > buf->count)
195                 return -EINVAL;
196         dirent = buf->previous;
197         if (dirent)
198                 put_user(offset, &dirent->d_off);
199         dirent = buf->current_dir;
200         buf->previous = dirent;
201         put_user(ino, &dirent->d_ino);
202         put_user(reclen, &dirent->d_reclen);
203         copy_to_user(dirent->d_name, name, namlen);
204         put_user(0, dirent->d_name + namlen);
205         ((char *) dirent) += reclen;
206         buf->current_dir = dirent;
207         buf->count -= reclen;
208         return 0;
209 }
210 
211 asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
212 {
213         struct file * file;
214         struct linux_dirent * lastdirent;
215         struct getdents_callback buf;
216         int error;
217 
218         error = -EBADF;
219         file = fget(fd);
220         if (!file)
221                 goto out;
222 
223         buf.current_dir = (struct linux_dirent *) dirent;
224         buf.previous = NULL;
225         buf.count = count;
226         buf.error = 0;
227 
228         error = vfs_readdir(file, filldir, &buf);
229         if (error < 0)
230                 goto out_putf;
231         error = buf.error;
232         lastdirent = buf.previous;
233         if (lastdirent) {
234                 put_user(file->f_pos, &lastdirent->d_off);
235                 error = count - buf.count;
236         }
237 
238 out_putf:
239         fput(file);
240 out:
241         return error;
242 }
243 
244 /*
245  * And even better one including d_type field and 64bit d_ino and d_off.
246  */
247 struct linux_dirent64 {
248         u64             d_ino;
249         s64             d_off;
250         unsigned short  d_reclen;
251         unsigned char   d_type;
252         char            d_name[0];
253 };
254 
255 #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
256 
257 struct getdents_callback64 {
258         struct linux_dirent64 * current_dir;
259         struct linux_dirent64 * previous;
260         int count;
261         int error;
262 };
263 
264 static int filldir64(void * __buf, const char * name, int namlen, off_t offset,
265                      ino_t ino, unsigned int d_type)
266 {
267         struct linux_dirent64 * dirent, d;
268         struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
269         int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
270 
271         buf->error = -EINVAL;   /* only used if we fail.. */
272         if (reclen > buf->count)
273                 return -EINVAL;
274         dirent = buf->previous;
275         if (dirent) {
276                 d.d_off = offset;
277                 copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
278         }
279         dirent = buf->current_dir;
280         buf->previous = dirent;
281         memset(&d, 0, NAME_OFFSET(&d));
282         d.d_ino = ino;
283         d.d_reclen = reclen;
284         d.d_type = d_type;
285         copy_to_user(dirent, &d, NAME_OFFSET(&d));
286         copy_to_user(dirent->d_name, name, namlen);
287         put_user(0, dirent->d_name + namlen);
288         ((char *) dirent) += reclen;
289         buf->current_dir = dirent;
290         buf->count -= reclen;
291         return 0;
292 }
293 
294 asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
295 {
296         struct file * file;
297         struct linux_dirent64 * lastdirent;
298         struct getdents_callback64 buf;
299         int error;
300 
301         error = -EBADF;
302         file = fget(fd);
303         if (!file)
304                 goto out;
305 
306         buf.current_dir = (struct linux_dirent64 *) dirent;
307         buf.previous = NULL;
308         buf.count = count;
309         buf.error = 0;
310 
311         error = vfs_readdir(file, filldir64, &buf);
312         if (error < 0)
313                 goto out_putf;
314         error = buf.error;
315         lastdirent = buf.previous;
316         if (lastdirent) {
317                 struct linux_dirent64 d;
318                 d.d_off = file->f_pos;
319                 copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
320                 error = count - buf.count;
321         }
322 
323 out_putf:
324         fput(file);
325 out:
326         return error;
327 }
328 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.