Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / tux / directory.c
1 /*
2  * TUX - Integrated Application Protocols Layer and Object Cache
3  *
4  * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
5  *
6  * directory.c: directory listing support
7  */
8
9 #define __KERNEL_SYSCALLS__
10 #include <net/tux.h>
11
12 /****************************************************************
13  *      This program is free software; you can redistribute it and/or modify
14  *      it under the terms of the GNU General Public License as published by
15  *      the Free Software Foundation; either version 2, or (at your option)
16  *      any later version.
17  *
18  *      This program is distributed in the hope that it will be useful,
19  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *      GNU General Public License for more details.
22  *
23  *      You should have received a copy of the GNU General Public License
24  *      along with this program; if not, write to the Free Software
25  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *
27  ****************************************************************/
28
29 char * tux_print_path (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt, char *buf, unsigned int max_len)
30 {
31         char *res;
32         struct dentry *cwd, *root;
33         struct vfsmount *cwd_mnt, *rootmnt;
34
35         cwd = dget(dentry);
36         cwd_mnt = mntget(mnt);
37         root = dget(req->docroot_dentry);
38         rootmnt = mntget(req->docroot_mnt);
39
40         spin_lock(&dcache_lock);
41         res = __d_path(cwd, cwd_mnt, root, rootmnt, buf, max_len);
42         spin_unlock(&dcache_lock);
43
44         dput(cwd);
45         mntput(cwd_mnt);
46         dput(root);
47         mntput(rootmnt);
48
49         return res;
50 }
51
52 /*
53  * There are filesystems that do not fill in ->d_type correctly.
54  * Determine file-type.
55  */
56 static int get_d_type (struct dentry *dentry)
57 {
58         unsigned int mode = dentry->d_inode->i_mode;
59
60         if (S_ISREG(mode))
61                 return DT_REG;
62         if (S_ISDIR(mode))
63                 return DT_DIR;
64         if (S_ISLNK(mode))
65                 return DT_LNK;
66         if (S_ISFIFO(mode))
67                 return DT_FIFO;
68         if (S_ISSOCK(mode))
69                 return DT_SOCK;
70         if (S_ISCHR(mode))
71                 return DT_CHR;
72         if (S_ISBLK(mode))
73                 return DT_BLK;
74         return 0;
75 }
76
77 static void do_dir_line (tux_req_t *req, int cachemiss)
78 {
79         struct linux_dirent64 *dirp, *dirp0;
80         char string0[MAX_OBJECTNAME_LEN+200], *tmp;
81         int len, curroff, total, str_len = 0;
82         int err, flag = cachemiss ? 0 : LOOKUP_ATOMIC;
83         struct nameidata base = { };
84         struct dentry *dentry = NULL;
85         struct inode *inode = NULL;
86         struct vfsmount *mnt = NULL;
87
88         if (req->proto->check_req_err(req, cachemiss))
89                 return;
90
91         tmp = NULL;
92         dirp0 = req->dirp0;
93         curroff = req->curroff;
94         total = req->total;
95
96         dirp = (struct linux_dirent64 *)((char *)dirp0 + curroff);
97         if (!dirp->d_name || !dirp->d_name[0])
98                 goto next_dir;
99         /*
100          * Hide .xxxxx files:
101          */
102         if (dirp->d_name[0] == '.')
103                 goto next_dir;
104         Dprintk("<%s T:%d (off:%Ld) (len:%d)>\n", dirp->d_name, dirp->d_type, dirp->d_off, dirp->d_reclen);
105         if (tux_hide_unreadable) {
106                 switch (dirp->d_type) {
107                         default:
108                                 goto next_dir;
109                         case DT_UNKNOWN:
110                         case DT_REG:
111                         case DT_DIR:
112                         case DT_LNK:
113                         /* valid entries - fall through. */
114                                 ;
115                 }
116         }
117         len = strlen(dirp->d_name);
118         if (len >= MAX_OBJECTNAME_LEN) {
119                 dirp->d_name[MAX_OBJECTNAME_LEN] = 0;
120                 len = MAX_OBJECTNAME_LEN-1;
121         }
122
123         if (!req->dentry)
124                 TUX_BUG();
125
126         base.flags = flag;
127         base.last_type = LAST_ROOT;
128         base.dentry = dget(req->dentry);
129         base.mnt = mntget(req->cwd_mnt);
130
131         switch_docroot(req);
132         err = path_walk(dirp->d_name, &base);
133
134         Dprintk("path_walk() returned %d.\n", err);
135
136         if (err) {
137                 if (err == -EWOULDBLOCKIO) {
138                         add_tux_atom(req, do_dir_line);
139                         queue_cachemiss(req);
140                         return;
141                 }
142                 goto next_dir;
143         }
144
145         dentry = base.dentry;
146         mnt = base.mnt;
147         if (!dentry)
148                 TUX_BUG();
149         if (IS_ERR(dentry))
150                 TUX_BUG();
151         inode = dentry->d_inode;
152         if (!inode)
153                 TUX_BUG();
154         if (!dirp->d_type)
155                 dirp->d_type = get_d_type(dentry);
156         if (tux_hide_unreadable) {
157                 umode_t mode;
158
159                 mode = inode->i_mode;
160                 if (mode & tux_mode_forbidden)
161                         goto out_dput;
162                 if (!(mode & tux_mode_allowed))
163                         goto out_dput;
164
165                 err = permission(inode, MAY_READ, NULL);
166                 if (err)
167                         goto out_dput;
168                 if (dirp->d_type == DT_DIR) {
169                         err = permission(inode, MAY_EXEC, NULL);
170                         if (err)
171                                 goto out_dput;
172                 }
173         }
174
175         tmp = req->proto->print_dir_line(req, string0, dirp->d_name, len, dirp->d_type, dentry, inode);
176         if (tmp)
177                 str_len = tmp-string0;
178 out_dput:
179         dput(dentry);
180         mntput(mnt);
181 next_dir:
182         curroff += dirp->d_reclen;
183
184         if (tmp && (tmp != string0))
185                 Dprintk("writing line (len: %d): <%s>\n", strlen(string0), string0);
186
187         if (curroff < total) {
188                 req->dirp0 = dirp0;
189                 req->curroff = curroff;
190                 add_tux_atom(req, do_dir_line);
191         } else {
192                 kfree(dirp0);
193                 req->dirp0 = NULL;
194                 req->curroff = 0;
195                 // falls back to the list_directory atom
196         }
197         if (tmp && (tmp != string0))
198                 __send_async_message(req, string0, 200, str_len, 0);
199         else
200                 add_req_to_workqueue(req);
201 }
202
203 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
204 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
205 #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
206
207 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
208                      u64 ino, unsigned int d_type)
209 {
210         struct linux_dirent64 * dirent, d;
211         struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
212         int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
213         int err;
214
215         buf->error = -EINVAL;   /* only used if we fail.. */
216         if (reclen > buf->count)
217                 return -EINVAL;
218         dirent = buf->previous;
219         if (dirent) {
220                 d.d_off = offset;
221                 err = copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
222                 BUG_ON(err);
223         }
224         dirent = buf->current_dir;
225         buf->previous = dirent;
226         memset(&d, 0, NAME_OFFSET(&d));
227         d.d_ino = ino;
228         d.d_reclen = reclen;
229         d.d_type = d_type;
230         err = copy_to_user(dirent, &d, NAME_OFFSET(&d));
231         BUG_ON(err);
232         err = copy_to_user(dirent->d_name, name, namlen);
233         BUG_ON(err);
234         err = put_user(0, dirent->d_name + namlen);
235         BUG_ON(err);
236         dirent = (void *)dirent + reclen;
237         buf->current_dir = dirent;
238         buf->count -= reclen;
239         return 0;
240 }
241 #define DIRENT_SIZE 3000
242
243 void list_directory (tux_req_t *req, int cachemiss)
244 {
245         struct getdents_callback64 buf;
246         struct linux_dirent64 *dirp0;
247         mm_segment_t oldmm;
248         int total;
249
250         Dprintk("list_directory(%p, %d), dentry: %p.\n", req, cachemiss, req->dentry);
251         if (!req->cwd_dentry)
252                 TUX_BUG();
253
254         if (!cachemiss) {
255                 add_tux_atom(req, list_directory);
256                 queue_cachemiss(req);
257                 return;
258         }
259
260         dirp0 = tux_kmalloc(DIRENT_SIZE);
261
262         buf.current_dir = dirp0;
263         buf.previous = NULL;
264         buf.count = DIRENT_SIZE;
265         buf.error = 0;
266
267         oldmm = get_fs(); set_fs(KERNEL_DS);
268         set_fs(KERNEL_DS);
269         total = vfs_readdir(req->in_file, filldir64, &buf);
270         set_fs(oldmm);
271
272         if (buf.previous)
273                 total = DIRENT_SIZE - buf.count;
274
275         Dprintk("total: %d (buf.error: %d, buf.previous %p)\n",
276                 total, buf.error, buf.previous);
277
278         if (total < 0) {
279                 kfree(dirp0);
280                 req_err(req);
281                 add_req_to_workqueue(req);
282                 return;
283         }
284         if (!total) {
285                 kfree(dirp0);
286                 req->in_file->f_pos = 0;
287                 add_req_to_workqueue(req);
288                 return;
289         }
290
291         if (!req->cwd_dentry)
292                 TUX_BUG();
293         add_tux_atom(req, list_directory);
294
295         req->dirp0 = dirp0;
296         req->curroff = 0;
297         req->total = total;
298         add_tux_atom(req, do_dir_line);
299
300         add_req_to_workqueue(req);
301 }
302