1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
3 *
4 * Original version: Copyright (C) 1996 P. Braam and M. Callahan
5 * Rewritten for Linux 2.1. Copyright (C) 1997 Carnegie Mellon University
6 * d_fsdata and NFS compatiblity fixes Copyright (C) 2001 Tacit Networks, Inc.
7 *
8 * This file is part of InterMezzo, http://www.inter-mezzo.org.
9 *
10 * InterMezzo is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General Public
12 * License as published by the Free Software Foundation.
13 *
14 * InterMezzo is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with InterMezzo; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 * Directory operations for InterMezzo filesystem
24 */
25
26 /* inode dentry alias list walking code adapted from linux/fs/dcache.c
27 *
28 * fs/dcache.c
29 *
30 * (C) 1997 Thomas Schoebel-Theuer,
31 * with heavy changes by Linus Torvalds
32 */
33
34 #define __NO_VERSION__
35 #include <linux/types.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/fs.h>
39 #include <linux/stat.h>
40 #include <linux/errno.h>
41 #include <linux/locks.h>
42 #include <linux/slab.h>
43 #include <asm/segment.h>
44 #include <asm/uaccess.h>
45 #include <linux/string.h>
46 #include <linux/smp_lock.h>
47 #include <linux/vmalloc.h>
48
49 #include <linux/intermezzo_fs.h>
50
51 kmem_cache_t * presto_dentry_slab;
52
53 /* called when a cache lookup succeeds */
presto_d_revalidate(struct dentry * de,int flag)54 static int presto_d_revalidate(struct dentry *de, int flag)
55 {
56 struct inode *inode = de->d_inode;
57 struct presto_file_set * root_fset;
58
59 ENTRY;
60 if (!inode) {
61 EXIT;
62 return 0;
63 }
64
65 if (is_bad_inode(inode)) {
66 EXIT;
67 return 0;
68 }
69
70 if (!presto_d2d(de)) {
71 presto_set_dd(de);
72 }
73
74 if (!presto_d2d(de)) {
75 EXIT;
76 return 0;
77 }
78
79 root_fset = presto_d2d(de->d_inode->i_sb->s_root)->dd_fset;
80 if (root_fset->fset_flags & FSET_FLAT_BRANCH &&
81 (presto_d2d(de)->dd_fset != root_fset )) {
82 presto_d2d(de)->dd_fset = root_fset;
83 }
84
85 EXIT;
86 return 1;
87
88 #if 0
89 /* The following is needed for metadata on demand. */
90 if ( S_ISDIR(inode->i_mode) ) {
91 EXIT;
92 return (presto_chk(de, PRESTO_DATA) &&
93 (presto_chk(de, PRESTO_ATTR)));
94 } else {
95 EXIT;
96 return presto_chk(de, PRESTO_ATTR);
97 }
98 #endif
99 }
100
presto_d_release(struct dentry * dentry)101 static void presto_d_release(struct dentry *dentry)
102 {
103 if (!presto_d2d(dentry)) {
104 /* This can happen for dentries from NFSd */
105 return;
106 }
107 presto_d2d(dentry)->dd_count--;
108
109 if (!presto_d2d(dentry)->dd_count) {
110 kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
111 dentry->d_fsdata = NULL;
112 }
113 }
114
115 struct dentry_operations presto_dentry_ops =
116 {
117 .d_revalidate = presto_d_revalidate,
118 .d_release = presto_d_release
119 };
120
presto_is_dentry_ROOT(struct dentry * dentry)121 static inline int presto_is_dentry_ROOT (struct dentry *dentry)
122 {
123 return(dentry_name_cmp(dentry,"ROOT") &&
124 !dentry_name_cmp(dentry->d_parent,".intermezzo"));
125 }
126
presto_try_find_fset(struct dentry * dentry,int * is_under_d_intermezzo)127 static struct presto_file_set* presto_try_find_fset(struct dentry* dentry,
128 int *is_under_d_intermezzo)
129 {
130 struct dentry* temp_dentry;
131 struct presto_dentry_data *d_data;
132 int found_root=0;
133
134 ENTRY;
135 CDEBUG(D_FSDATA, "finding fileset for %p:%s\n", dentry,
136 dentry->d_name.name);
137
138 *is_under_d_intermezzo = 0;
139
140 /* walk up through the branch to get the fileset */
141 /* The dentry we are passed presumably does not have the correct
142 * fset information. However, we still want to start walking up
143 * the branch from this dentry to get our found_root and
144 * is_under_d_intermezzo decisions correct
145 */
146 for (temp_dentry = dentry ; ; temp_dentry = temp_dentry->d_parent) {
147 CDEBUG(D_FSDATA, "--->dentry %p:%*s\n", temp_dentry,
148 temp_dentry->d_name.len,temp_dentry->d_name.name);
149 if (presto_is_dentry_ROOT(temp_dentry))
150 found_root = 1;
151 if (!found_root &&
152 dentry_name_cmp(temp_dentry, ".intermezzo")) {
153 *is_under_d_intermezzo = 1;
154 }
155 d_data = presto_d2d(temp_dentry);
156 if (d_data) {
157 /* If we found a "ROOT" dentry while walking up the
158 * branch, we will journal regardless of whether
159 * we are under .intermezzo or not.
160 * If we are already under d_intermezzo don't reverse
161 * the decision here...even if we found a "ROOT"
162 * dentry above .intermezzo (if we were ever to
163 * modify the directory structure).
164 */
165 if (!*is_under_d_intermezzo)
166 *is_under_d_intermezzo = !found_root &&
167 (d_data->dd_flags & PRESTO_DONT_JOURNAL);
168 EXIT;
169 return d_data->dd_fset;
170 }
171 if (temp_dentry->d_parent == temp_dentry) {
172 break;
173 }
174 }
175 EXIT;
176 return NULL;
177 }
178
179 /* Only call this function on positive dentries */
presto_try_find_alias_with_dd(struct dentry * dentry)180 static struct presto_dentry_data* presto_try_find_alias_with_dd (
181 struct dentry* dentry)
182 {
183 struct inode *inode=dentry->d_inode;
184 struct list_head *head, *next, *tmp;
185 struct dentry *tmp_dentry;
186
187 /* Search through the alias list for dentries with d_fsdata */
188 spin_lock(&dcache_lock);
189 head = &inode->i_dentry;
190 next = inode->i_dentry.next;
191 while (next != head) {
192 tmp = next;
193 next = tmp->next;
194 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
195 if (!presto_d2d(tmp_dentry)) {
196 spin_unlock(&dcache_lock);
197 return presto_d2d(tmp_dentry);
198 }
199 }
200 spin_unlock(&dcache_lock);
201 return NULL;
202 }
203
204 /* Only call this function on positive dentries */
presto_set_alias_dd(struct dentry * dentry,struct presto_dentry_data * dd)205 static void presto_set_alias_dd (struct dentry *dentry,
206 struct presto_dentry_data* dd)
207 {
208 struct inode *inode=dentry->d_inode;
209 struct list_head *head, *next, *tmp;
210 struct dentry *tmp_dentry;
211
212 /* Set d_fsdata for this dentry */
213 dd->dd_count++;
214 dentry->d_fsdata = dd;
215
216 /* Now set d_fsdata for all dentries in the alias list. */
217 spin_lock(&dcache_lock);
218 head = &inode->i_dentry;
219 next = inode->i_dentry.next;
220 while (next != head) {
221 tmp = next;
222 next = tmp->next;
223 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
224 if (!presto_d2d(tmp_dentry)) {
225 dd->dd_count++;
226 tmp_dentry->d_fsdata = dd;
227 }
228 }
229 spin_unlock(&dcache_lock);
230 return;
231 }
232
izo_alloc_ddata(void)233 inline struct presto_dentry_data *izo_alloc_ddata(void)
234 {
235 struct presto_dentry_data *dd;
236
237 dd = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
238 if (dd == NULL) {
239 CERROR("IZO: out of memory trying to allocate presto_dentry_data\n");
240 return NULL;
241 }
242 memset(dd, 0, sizeof(*dd));
243 dd->dd_count = 1;
244
245 return dd;
246 }
247
248 /* This uses the BKL! */
presto_set_dd(struct dentry * dentry)249 int presto_set_dd(struct dentry * dentry)
250 {
251 struct presto_file_set *fset = NULL;
252 struct presto_dentry_data *dd;
253 int is_under_d_izo;
254 int error=0;
255
256 ENTRY;
257
258 if (!dentry)
259 BUG();
260
261 lock_kernel();
262
263 /* Did we lose a race? */
264 if (dentry->d_fsdata) {
265 CERROR("dentry %p already has d_fsdata set\n", dentry);
266 if (dentry->d_inode)
267 CERROR(" inode: %ld\n", dentry->d_inode->i_ino);
268 EXIT;
269 goto out_unlock;
270 }
271
272 if (dentry->d_inode != NULL) {
273 /* NFSd runs find_fh_dentry which instantiates disconnected
274 * dentries which are then connected without a lookup().
275 * So it is possible to have connected dentries that do not
276 * have d_fsdata set. So we walk the list trying to find
277 * an alias which has its d_fsdata set and then use that
278 * for all the other dentries as well.
279 * - SHP,Vinny.
280 */
281
282 /* If there is an alias with d_fsdata use it. */
283 if ((dd = presto_try_find_alias_with_dd (dentry))) {
284 presto_set_alias_dd (dentry, dd);
285 EXIT;
286 goto out_unlock;
287 }
288 } else {
289 /* Negative dentry */
290 CDEBUG(D_FSDATA,"negative dentry %p: %*s\n", dentry,
291 dentry->d_name.len, dentry->d_name.name);
292 }
293
294 /* No pre-existing d_fsdata, we need to construct one.
295 * First, we must walk up the tree to find the fileset
296 * If a fileset can't be found, we leave a null fsdata
297 * and return EROFS to indicate that we can't journal
298 * updates.
299 */
300 fset = presto_try_find_fset (dentry, &is_under_d_izo);
301 if (!fset) {
302 #ifdef PRESTO_NO_NFS
303 CERROR("No fileset for dentry %p: %*s\n", dentry,
304 dentry->d_name.len, dentry->d_name.name);
305 #endif
306 error = -EROFS;
307 EXIT;
308 goto out_unlock;
309 }
310
311 dentry->d_fsdata = izo_alloc_ddata();
312 if (!presto_d2d(dentry)) {
313 CERROR ("InterMezzo: out of memory allocating d_fsdata\n");
314 error = -ENOMEM;
315 goto out_unlock;
316 }
317 presto_d2d(dentry)->dd_fset = fset;
318 if (is_under_d_izo)
319 presto_d2d(dentry)->dd_flags |= PRESTO_DONT_JOURNAL;
320 EXIT;
321
322 out_unlock:
323 CDEBUG(D_FSDATA,"presto_set_dd dentry %p: %*s, d_fsdata %p\n",
324 dentry, dentry->d_name.len, dentry->d_name.name,
325 dentry->d_fsdata);
326 unlock_kernel();
327
328 if (fset) {
329 filter_setup_dentry_ops(fset->fset_cache->cache_filter,
330 dentry->d_op, &presto_dentry_ops);
331 dentry->d_op = filter_c2udops(fset->fset_cache->cache_filter);
332 }
333
334 return error;
335 }
336
presto_init_ddata_cache(void)337 int presto_init_ddata_cache(void)
338 {
339 ENTRY;
340 presto_dentry_slab =
341 kmem_cache_create("presto_cache",
342 sizeof(struct presto_dentry_data), 0,
343 SLAB_HWCACHE_ALIGN, NULL,
344 NULL);
345 EXIT;
346 return (presto_dentry_slab != NULL);
347 }
348
presto_cleanup_ddata_cache(void)349 void presto_cleanup_ddata_cache(void)
350 {
351 kmem_cache_destroy(presto_dentry_slab);
352 }
353