OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [readdir.c] - Blame information for rev 1275

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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
int 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
 
45
int dcache_dir_close(struct inode *inode, struct file *file)
46
{
47
        dput(file->private_data);
48
        return 0;
49
}
50
 
51
loff_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
                        list_del(&cursor->d_child);
73
                        p = file->f_dentry->d_subdirs.next;
74
                        while (n && p != &file->f_dentry->d_subdirs) {
75
                                struct dentry *next;
76
                                next = list_entry(p, struct dentry, d_child);
77
                                if (!list_empty(&next->d_hash) && next->d_inode)
78
                                        n--;
79
                                p = p->next;
80
                        }
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
 
89
int 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
 
100
int 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
        UPDATE_ATIME(dentry->d_inode);
150
        return 0;
151
}
152
 
153
struct file_operations dcache_dir_ops = {
154
        open:           dcache_dir_open,
155
        release:        dcache_dir_close,
156
        llseek:         dcache_dir_lseek,
157
        read:           generic_read_dir,
158
        readdir:        dcache_readdir,
159
        fsync:          dcache_dir_fsync,
160
};
161
 
162
/*
163
 * Traditional linux readdir() handling..
164
 *
165
 * "count=1" is a special case, meaning that the buffer is one
166
 * dirent-structure in size and that the code can't handle more
167
 * anyway. Thus the special "fillonedir()" function for that
168
 * case (the low-level handlers don't need to care about this).
169
 */
170
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
171
#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
172
 
173
#ifndef __ia64__
174
 
175
struct old_linux_dirent {
176
        unsigned long   d_ino;
177
        unsigned long   d_offset;
178
        unsigned short  d_namlen;
179
        char            d_name[1];
180
};
181
 
182
struct readdir_callback {
183
        struct old_linux_dirent * dirent;
184
        int count;
185
};
186
 
187
static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
188
                      ino_t ino, unsigned int d_type)
189
{
190
        struct readdir_callback * buf = (struct readdir_callback *) __buf;
191
        struct old_linux_dirent * dirent;
192
 
193
        if (buf->count)
194
                return -EINVAL;
195
        buf->count++;
196
        dirent = buf->dirent;
197
        put_user(ino, &dirent->d_ino);
198
        put_user(offset, &dirent->d_offset);
199
        put_user(namlen, &dirent->d_namlen);
200
        copy_to_user(dirent->d_name, name, namlen);
201
        put_user(0, dirent->d_name + namlen);
202
        return 0;
203
}
204
 
205
asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
206
{
207
        int error;
208
        struct file * file;
209
        struct readdir_callback buf;
210
 
211
        error = -EBADF;
212
        file = fget(fd);
213
        if (!file)
214
                goto out;
215
 
216
        buf.count = 0;
217
        buf.dirent = dirent;
218
 
219
        error = vfs_readdir(file, fillonedir, &buf);
220
        if (error >= 0)
221
                error = buf.count;
222
 
223
        fput(file);
224
out:
225
        return error;
226
}
227
 
228
#endif /* !__ia64__ */
229
 
230
/*
231
 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
232
 * interface.
233
 */
234
struct linux_dirent {
235
        unsigned long   d_ino;
236
        unsigned long   d_off;
237
        unsigned short  d_reclen;
238
        char            d_name[1];
239
};
240
 
241
struct getdents_callback {
242
        struct linux_dirent * current_dir;
243
        struct linux_dirent * previous;
244
        int count;
245
        int error;
246
};
247
 
248
static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
249
                   ino_t ino, unsigned int d_type)
250
{
251
        struct linux_dirent * dirent;
252
        struct getdents_callback * buf = (struct getdents_callback *) __buf;
253
        int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
254
 
255
        buf->error = -EINVAL;   /* only used if we fail.. */
256
        if (reclen > buf->count)
257
                return -EINVAL;
258
        dirent = buf->previous;
259
        if (dirent)
260
                put_user(offset, &dirent->d_off);
261
        dirent = buf->current_dir;
262
        buf->previous = dirent;
263
        put_user(ino, &dirent->d_ino);
264
        put_user(reclen, &dirent->d_reclen);
265
        copy_to_user(dirent->d_name, name, namlen);
266
        put_user(0, dirent->d_name + namlen);
267
        ((char *) dirent) += reclen;
268
        buf->current_dir = dirent;
269
        buf->count -= reclen;
270
        return 0;
271
}
272
 
273
asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
274
{
275
        struct file * file;
276
        struct linux_dirent * lastdirent;
277
        struct getdents_callback buf;
278
        int error;
279
 
280
        error = -EBADF;
281
        file = fget(fd);
282
        if (!file)
283
                goto out;
284
 
285
        buf.current_dir = (struct linux_dirent *) dirent;
286
        buf.previous = NULL;
287
        buf.count = count;
288
        buf.error = 0;
289
 
290
        error = vfs_readdir(file, filldir, &buf);
291
        if (error < 0)
292
                goto out_putf;
293
        error = buf.error;
294
        lastdirent = buf.previous;
295
        if (lastdirent) {
296
                put_user(file->f_pos, &lastdirent->d_off);
297
                error = count - buf.count;
298
        }
299
 
300
out_putf:
301
        fput(file);
302
out:
303
        return error;
304
}
305
 
306
/*
307
 * And even better one including d_type field and 64bit d_ino and d_off.
308
 */
309
struct linux_dirent64 {
310
        u64             d_ino;
311
        s64             d_off;
312
        unsigned short  d_reclen;
313
        unsigned char   d_type;
314
        char            d_name[0];
315
};
316
 
317
#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
318
 
319
struct getdents_callback64 {
320
        struct linux_dirent64 * current_dir;
321
        struct linux_dirent64 * previous;
322
        int count;
323
        int error;
324
};
325
 
326
static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
327
                     ino_t ino, unsigned int d_type)
328
{
329
        struct linux_dirent64 * dirent, d;
330
        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
331
        int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
332
 
333
        buf->error = -EINVAL;   /* only used if we fail.. */
334
        if (reclen > buf->count)
335
                return -EINVAL;
336
        dirent = buf->previous;
337
        if (dirent) {
338
                d.d_off = offset;
339
                copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
340
        }
341
        dirent = buf->current_dir;
342
        buf->previous = dirent;
343
        memset(&d, 0, NAME_OFFSET(&d));
344
        d.d_ino = ino;
345
        d.d_reclen = reclen;
346
        d.d_type = d_type;
347
        copy_to_user(dirent, &d, NAME_OFFSET(&d));
348
        copy_to_user(dirent->d_name, name, namlen);
349
        put_user(0, dirent->d_name + namlen);
350
        ((char *) dirent) += reclen;
351
        buf->current_dir = dirent;
352
        buf->count -= reclen;
353
        return 0;
354
}
355
 
356
asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
357
{
358
        struct file * file;
359
        struct linux_dirent64 * lastdirent;
360
        struct getdents_callback64 buf;
361
        int error;
362
 
363
        error = -EBADF;
364
        file = fget(fd);
365
        if (!file)
366
                goto out;
367
 
368
        buf.current_dir = (struct linux_dirent64 *) dirent;
369
        buf.previous = NULL;
370
        buf.count = count;
371
        buf.error = 0;
372
 
373
        error = vfs_readdir(file, filldir64, &buf);
374
        if (error < 0)
375
                goto out_putf;
376
        error = buf.error;
377
        lastdirent = buf.previous;
378
        if (lastdirent) {
379
                struct linux_dirent64 d;
380
                d.d_off = file->f_pos;
381
                copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
382
                error = count - buf.count;
383
        }
384
 
385
out_putf:
386
        fput(file);
387
out:
388
        return error;
389
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.