1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
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.
8 * This file is part of InterMezzo, http://www.inter-mezzo.org.
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.
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.
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.
23 * Directory operations for InterMezzo filesystem
26 /* inode dentry alias list walking code adapted from linux/fs/dcache.c
30 * (C) 1997 Thomas Schoebel-Theuer,
31 * with heavy changes by Linus Torvalds
34 #include <linux/types.h>
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
38 #include <linux/stat.h>
39 #include <linux/errno.h>
40 #include <linux/slab.h>
41 #include <asm/segment.h>
42 #include <asm/uaccess.h>
43 #include <linux/string.h>
44 #include <linux/vmalloc.h>
46 #include "intermezzo_fs.h"
48 kmem_cache_t * presto_dentry_slab;
50 /* called when a cache lookup succeeds */
51 static int presto_d_revalidate(struct dentry *de, struct nameidata *nd)
53 struct inode *inode = de->d_inode;
54 struct presto_file_set * root_fset;
62 if (is_bad_inode(inode)) {
67 if (!presto_d2d(de)) {
71 if (!presto_d2d(de)) {
76 root_fset = presto_d2d(de->d_inode->i_sb->s_root)->dd_fset;
77 if (root_fset->fset_flags & FSET_FLAT_BRANCH &&
78 (presto_d2d(de)->dd_fset != root_fset )) {
79 presto_d2d(de)->dd_fset = root_fset;
86 /* The following is needed for metadata on demand. */
87 if ( S_ISDIR(inode->i_mode) ) {
89 return (presto_chk(de, PRESTO_DATA) &&
90 (presto_chk(de, PRESTO_ATTR)));
93 return presto_chk(de, PRESTO_ATTR);
98 static void presto_d_release(struct dentry *dentry)
100 if (!presto_d2d(dentry)) {
101 /* This can happen for dentries from NFSd */
104 presto_d2d(dentry)->dd_count--;
106 if (!presto_d2d(dentry)->dd_count) {
107 kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
108 dentry->d_fsdata = NULL;
112 struct dentry_operations presto_dentry_ops =
114 .d_revalidate = presto_d_revalidate,
115 .d_release = presto_d_release
118 static inline int presto_is_dentry_ROOT (struct dentry *dentry)
120 return(dentry_name_cmp(dentry,"ROOT") &&
121 !dentry_name_cmp(dentry->d_parent,".intermezzo"));
124 static struct presto_file_set* presto_try_find_fset(struct dentry* dentry,
125 int *is_under_d_intermezzo)
127 struct dentry* temp_dentry;
128 struct presto_dentry_data *d_data;
132 CDEBUG(D_FSDATA, "finding fileset for %p:%s\n", dentry,
133 dentry->d_name.name);
135 *is_under_d_intermezzo = 0;
137 /* walk up through the branch to get the fileset */
138 /* The dentry we are passed presumably does not have the correct
139 * fset information. However, we still want to start walking up
140 * the branch from this dentry to get our found_root and
141 * is_under_d_intermezzo decisions correct
143 for (temp_dentry = dentry ; ; temp_dentry = temp_dentry->d_parent) {
144 CDEBUG(D_FSDATA, "--->dentry %p:%*s\n", temp_dentry,
145 temp_dentry->d_name.len,temp_dentry->d_name.name);
146 if (presto_is_dentry_ROOT(temp_dentry))
149 dentry_name_cmp(temp_dentry, ".intermezzo")) {
150 *is_under_d_intermezzo = 1;
152 d_data = presto_d2d(temp_dentry);
154 /* If we found a "ROOT" dentry while walking up the
155 * branch, we will journal regardless of whether
156 * we are under .intermezzo or not.
157 * If we are already under d_intermezzo don't reverse
158 * the decision here...even if we found a "ROOT"
159 * dentry above .intermezzo (if we were ever to
160 * modify the directory structure).
162 if (!*is_under_d_intermezzo)
163 *is_under_d_intermezzo = !found_root &&
164 (d_data->dd_flags & PRESTO_DONT_JOURNAL);
166 return d_data->dd_fset;
168 if (temp_dentry->d_parent == temp_dentry) {
176 /* Only call this function on positive dentries */
177 static struct presto_dentry_data* presto_try_find_alias_with_dd (
178 struct dentry* dentry)
180 struct inode *inode=dentry->d_inode;
181 struct list_head *head, *next, *tmp;
182 struct dentry *tmp_dentry;
184 /* Search through the alias list for dentries with d_fsdata */
185 spin_lock(&dcache_lock);
186 head = &inode->i_dentry;
187 next = inode->i_dentry.next;
188 while (next != head) {
191 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
192 if (!presto_d2d(tmp_dentry)) {
193 spin_unlock(&dcache_lock);
194 return presto_d2d(tmp_dentry);
197 spin_unlock(&dcache_lock);
201 /* Only call this function on positive dentries */
202 static void presto_set_alias_dd (struct dentry *dentry,
203 struct presto_dentry_data* dd)
205 struct inode *inode=dentry->d_inode;
206 struct list_head *head, *next, *tmp;
207 struct dentry *tmp_dentry;
209 /* Set d_fsdata for this dentry */
211 dentry->d_fsdata = dd;
213 /* Now set d_fsdata for all dentries in the alias list. */
214 spin_lock(&dcache_lock);
215 head = &inode->i_dentry;
216 next = inode->i_dentry.next;
217 while (next != head) {
220 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
221 if (!presto_d2d(tmp_dentry)) {
223 tmp_dentry->d_fsdata = dd;
226 spin_unlock(&dcache_lock);
230 inline struct presto_dentry_data *izo_alloc_ddata(void)
232 struct presto_dentry_data *dd;
234 dd = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
236 CERROR("IZO: out of memory trying to allocate presto_dentry_data\n");
239 memset(dd, 0, sizeof(*dd));
245 /* This uses the BKL! */
246 int presto_set_dd(struct dentry * dentry)
248 struct presto_file_set *fset;
249 struct presto_dentry_data *dd;
260 /* Did we lose a race? */
261 if (dentry->d_fsdata) {
262 CERROR("dentry %p already has d_fsdata set\n", dentry);
264 CERROR(" inode: %ld\n", dentry->d_inode->i_ino);
269 if (dentry->d_inode != NULL) {
270 /* NFSd runs find_fh_dentry which instantiates disconnected
271 * dentries which are then connected without a lookup().
272 * So it is possible to have connected dentries that do not
273 * have d_fsdata set. So we walk the list trying to find
274 * an alias which has its d_fsdata set and then use that
275 * for all the other dentries as well.
279 /* If there is an alias with d_fsdata use it. */
280 if ((dd = presto_try_find_alias_with_dd (dentry))) {
281 presto_set_alias_dd (dentry, dd);
286 /* Negative dentry */
287 CDEBUG(D_FSDATA,"negative dentry %p: %*s\n", dentry,
288 dentry->d_name.len, dentry->d_name.name);
291 /* No pre-existing d_fsdata, we need to construct one.
292 * First, we must walk up the tree to find the fileset
293 * If a fileset can't be found, we leave a null fsdata
294 * and return EROFS to indicate that we can't journal
297 fset = presto_try_find_fset (dentry, &is_under_d_izo);
300 CERROR("No fileset for dentry %p: %*s\n", dentry,
301 dentry->d_name.len, dentry->d_name.name);
308 dentry->d_fsdata = izo_alloc_ddata();
309 if (!presto_d2d(dentry)) {
310 CERROR ("InterMezzo: out of memory allocating d_fsdata\n");
314 presto_d2d(dentry)->dd_fset = fset;
316 presto_d2d(dentry)->dd_flags |= PRESTO_DONT_JOURNAL;
320 CDEBUG(D_FSDATA,"presto_set_dd dentry %p: %*s, d_fsdata %p\n",
321 dentry, dentry->d_name.len, dentry->d_name.name,
327 int presto_init_ddata_cache(void)
331 kmem_cache_create("presto_cache",
332 sizeof(struct presto_dentry_data), 0,
333 SLAB_HWCACHE_ALIGN, NULL,
336 return (presto_dentry_slab != NULL);
339 void presto_cleanup_ddata_cache(void)
341 kmem_cache_destroy(presto_dentry_slab);