| 1 | /* |
|---|
| 2 | * (C) 2001 Clemson University and The University of Chicago |
|---|
| 3 | * |
|---|
| 4 | * See COPYING in top-level directory. |
|---|
| 5 | */ |
|---|
| 6 | |
|---|
| 7 | /** \file |
|---|
| 8 | * \ingroup pvfs2linux |
|---|
| 9 | * |
|---|
| 10 | * Implementation of dentry (directory cache) functions. |
|---|
| 11 | */ |
|---|
| 12 | |
|---|
| 13 | #include "pvfs2-kernel.h" |
|---|
| 14 | #include "pvfs2-internal.h" |
|---|
| 15 | |
|---|
| 16 | static void __attribute__ ((unused)) print_dentry(struct dentry *entry, int ret); |
|---|
| 17 | |
|---|
| 18 | /* should return 1 if dentry can still be trusted, else 0 */ |
|---|
| 19 | static int pvfs2_d_revalidate_common(struct dentry* dentry) |
|---|
| 20 | { |
|---|
| 21 | int ret = 0; |
|---|
| 22 | struct inode *inode; |
|---|
| 23 | struct inode *parent_inode = NULL; |
|---|
| 24 | pvfs2_kernel_op_t *new_op = NULL; |
|---|
| 25 | pvfs2_inode_t *parent = NULL; |
|---|
| 26 | |
|---|
| 27 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: called on dentry %p.\n", |
|---|
| 28 | __func__, dentry); |
|---|
| 29 | |
|---|
| 30 | /* find inode from dentry */ |
|---|
| 31 | if(!dentry || !dentry->d_inode) |
|---|
| 32 | { |
|---|
| 33 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: inode not valid.\n", __func__); |
|---|
| 34 | goto invalid_exit; |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: inode valid.\n", __func__); |
|---|
| 38 | inode = dentry->d_inode; |
|---|
| 39 | |
|---|
| 40 | /* find parent inode */ |
|---|
| 41 | if(!dentry || !dentry->d_parent) |
|---|
| 42 | { |
|---|
| 43 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: parent not found.\n", __func__); |
|---|
| 44 | goto invalid_exit; |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: parent found.\n", __func__); |
|---|
| 48 | parent_inode = dentry->d_parent->d_inode; |
|---|
| 49 | |
|---|
| 50 | /* first perform a lookup to make sure that the object not only |
|---|
| 51 | * exists, but is still in the expected place in the name space |
|---|
| 52 | */ |
|---|
| 53 | if (!is_root_handle(inode)) |
|---|
| 54 | { |
|---|
| 55 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__); |
|---|
| 56 | new_op = op_alloc(PVFS2_VFS_OP_LOOKUP); |
|---|
| 57 | if (!new_op) |
|---|
| 58 | { |
|---|
| 59 | goto invalid_exit; |
|---|
| 60 | } |
|---|
| 61 | new_op->upcall.req.lookup.sym_follow = PVFS2_LOOKUP_LINK_NO_FOLLOW; |
|---|
| 62 | parent = PVFS2_I(parent_inode); |
|---|
| 63 | if (parent && parent->refn.handle != PVFS_HANDLE_NULL && |
|---|
| 64 | parent->refn.fs_id != PVFS_FS_ID_NULL) |
|---|
| 65 | { |
|---|
| 66 | new_op->upcall.req.lookup.parent_refn = parent->refn; |
|---|
| 67 | } |
|---|
| 68 | else |
|---|
| 69 | { |
|---|
| 70 | #if defined(HAVE_IGET4_LOCKED) || defined(HAVE_IGET5_LOCKED) |
|---|
| 71 | gossip_lerr("Critical error: i_ino cannot be relied " |
|---|
| 72 | "upon when using iget5/iget4\n"); |
|---|
| 73 | op_release(new_op); |
|---|
| 74 | goto invalid_exit; |
|---|
| 75 | #endif |
|---|
| 76 | new_op->upcall.req.lookup.parent_refn.handle = |
|---|
| 77 | get_handle_from_ino(parent_inode); |
|---|
| 78 | new_op->upcall.req.lookup.parent_refn.fs_id = |
|---|
| 79 | PVFS2_SB(parent_inode->i_sb)->fs_id; |
|---|
| 80 | } |
|---|
| 81 | strncpy(new_op->upcall.req.lookup.d_name, |
|---|
| 82 | dentry->d_name.name, PVFS2_NAME_LEN); |
|---|
| 83 | |
|---|
| 84 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d interrupt flag [%d]\n", |
|---|
| 85 | __FILE__, __func__, __LINE__, get_interruptible_flag(parent_inode)); |
|---|
| 86 | |
|---|
| 87 | ret = service_operation( |
|---|
| 88 | new_op, "pvfs2_lookup", |
|---|
| 89 | get_interruptible_flag(parent_inode)); |
|---|
| 90 | |
|---|
| 91 | if((new_op->downcall.status != 0) || |
|---|
| 92 | !match_handle(new_op->downcall.resp.lookup.refn.handle, inode)) |
|---|
| 93 | { |
|---|
| 94 | gossip_debug( |
|---|
| 95 | GOSSIP_DCACHE_DEBUG, |
|---|
| 96 | "%s:%s:%d lookup failure |%s| or no match |%s|.\n", |
|---|
| 97 | __FILE__, __func__, __LINE__, |
|---|
| 98 | (new_op->downcall.status != 0) ? "true" : "false", |
|---|
| 99 | (!match_handle(new_op->downcall.resp.lookup.refn.handle, inode)) ? "true" : "false"); |
|---|
| 100 | op_release(new_op); |
|---|
| 101 | |
|---|
| 102 | /* Avoid calling make_bad_inode() in this situation. On 2.4 |
|---|
| 103 | * (RHEL3) kernels, it can cause bogus permission denied errors |
|---|
| 104 | * on path elements after interrupt signals. On later 2.6 |
|---|
| 105 | * kernels this causes a kernel oops rather than a permission |
|---|
| 106 | * error. |
|---|
| 107 | */ |
|---|
| 108 | #if 0 |
|---|
| 109 | /* mark the inode as bad so that d_delete will be aggressive |
|---|
| 110 | * about dropping the dentry |
|---|
| 111 | */ |
|---|
| 112 | pvfs2_make_bad_inode(inode); |
|---|
| 113 | #endif |
|---|
| 114 | gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d setting revalidate_failed = 1\n", __FILE__, __func__, __LINE__); |
|---|
| 115 | /* set a flag that we can detect later in d_delete() */ |
|---|
| 116 | PVFS2_I(inode)->revalidate_failed = 1; |
|---|
| 117 | d_drop(dentry); |
|---|
| 118 | |
|---|
| 119 | goto invalid_exit; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | op_release(new_op); |
|---|
| 123 | } |
|---|
| 124 | else |
|---|
| 125 | { |
|---|
| 126 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 127 | "%s: root handle, lookup skipped.\n", __func__); |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | /* now perform getattr */ |
|---|
| 131 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 132 | "%s: doing getattr: inode: %p, handle: %llu)\n", |
|---|
| 133 | __func__, inode, llu(get_handle_from_ino(inode))); |
|---|
| 134 | ret = pvfs2_inode_getattr(inode, PVFS_ATTR_SYS_ALL_NOHINT); |
|---|
| 135 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 136 | "%s: getattr %s (ret = %d), returning %s for dentry i_count=%d\n", |
|---|
| 137 | __func__, |
|---|
| 138 | (ret == 0 ? "succeeded" : "failed"), |
|---|
| 139 | ret, |
|---|
| 140 | (ret == 0 ? "valid" : "INVALID"), |
|---|
| 141 | atomic_read(&inode->i_count)); |
|---|
| 142 | if(ret != 0) |
|---|
| 143 | { |
|---|
| 144 | goto invalid_exit; |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | /* dentry is valid! */ |
|---|
| 148 | return 1; |
|---|
| 149 | |
|---|
| 150 | invalid_exit: |
|---|
| 151 | return 0; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | static int pvfs2_d_delete ( |
|---|
| 155 | #ifdef HAVE_D_DELETE_CONST |
|---|
| 156 | const |
|---|
| 157 | #endif /* HAVE_D_DELETE_CONST */ |
|---|
| 158 | struct dentry * dentry |
|---|
| 159 | ) |
|---|
| 160 | { |
|---|
| 161 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 162 | "%s: called on dentry %p.\n", __func__, dentry); |
|---|
| 163 | #if 0 |
|---|
| 164 | if(dentry->d_inode && is_bad_inode(dentry->d_inode)) |
|---|
| 165 | #endif |
|---|
| 166 | if(dentry->d_inode && PVFS2_I(dentry->d_inode)->revalidate_failed == 1) |
|---|
| 167 | { |
|---|
| 168 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 169 | "%s: returning 1 (bad inode).\n", __func__); |
|---|
| 170 | return 1; |
|---|
| 171 | } |
|---|
| 172 | else |
|---|
| 173 | { |
|---|
| 174 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 175 | "%s: returning 0 (inode looks ok).\n", __func__); |
|---|
| 176 | return 0; |
|---|
| 177 | } |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | /* should return 1 if dentry can still be trusted, else 0 */ |
|---|
| 181 | #ifdef PVFS2_LINUX_KERNEL_2_4 |
|---|
| 182 | static int pvfs2_d_revalidate( |
|---|
| 183 | struct dentry *dentry, |
|---|
| 184 | int flags) |
|---|
| 185 | { |
|---|
| 186 | return(pvfs2_d_revalidate_common(dentry)); |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | #else |
|---|
| 190 | |
|---|
| 191 | /** Verify that dentry is valid. |
|---|
| 192 | */ |
|---|
| 193 | static int pvfs2_d_revalidate( |
|---|
| 194 | struct dentry *dentry, |
|---|
| 195 | struct nameidata *nd) |
|---|
| 196 | { |
|---|
| 197 | |
|---|
| 198 | if (nd && (nd->flags & LOOKUP_FOLLOW) && |
|---|
| 199 | ((!nd->flags) & (LOOKUP_CREATE)) ) |
|---|
| 200 | { |
|---|
| 201 | gossip_debug(GOSSIP_DCACHE_DEBUG, |
|---|
| 202 | "\n%s: Trusting intent; skipping getattr\n", __func__); |
|---|
| 203 | return 1; |
|---|
| 204 | } |
|---|
| 205 | return(pvfs2_d_revalidate_common(dentry)); |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | #endif /* PVFS2_LINUX_KERNEL_2_4 */ |
|---|
| 209 | |
|---|
| 210 | /* |
|---|
| 211 | to propagate an error, return a value < 0, as this causes |
|---|
| 212 | link_path_walk to pass our error up |
|---|
| 213 | */ |
|---|
| 214 | static int pvfs2_d_hash( |
|---|
| 215 | #ifdef HAVE_THREE_PARAM_D_HASH |
|---|
| 216 | const struct dentry *parent, |
|---|
| 217 | const struct inode *inode, |
|---|
| 218 | struct qstr *hash |
|---|
| 219 | #else |
|---|
| 220 | struct dentry *parent, |
|---|
| 221 | struct qstr *hash |
|---|
| 222 | #endif /* HAVE_THREE_PARAM_D_HASH */ |
|---|
| 223 | ) |
|---|
| 224 | { |
|---|
| 225 | /* gossip_debug(GOSSIP_DCACHE_DEBUG, "pvfs2: pvfs2_d_hash called " */ |
|---|
| 226 | /* "(name: %s | len: %d | hash: %d)\n", */ |
|---|
| 227 | /* hash->name, hash->len, hash->hash); */ |
|---|
| 228 | return 0; |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | #ifdef HAVE_SEVEN_PARAM_D_COMPARE |
|---|
| 232 | static int pvfs2_d_compare( |
|---|
| 233 | const struct dentry *parent, |
|---|
| 234 | const struct inode * pinode, |
|---|
| 235 | const struct dentry *dentry, |
|---|
| 236 | const struct inode *inode, |
|---|
| 237 | unsigned int len, |
|---|
| 238 | const char *str, |
|---|
| 239 | const struct qstr *name) |
|---|
| 240 | { |
|---|
| 241 | int i = 0; |
|---|
| 242 | gossip_debug(GOSSIP_DCACHE_DEBUG, "pvfs2_d_compare: " |
|---|
| 243 | "called on parent %p\n (name1: %s| name2: %s)\n", |
|---|
| 244 | parent, str, name->name); |
|---|
| 245 | |
|---|
| 246 | if( len != name->len ) |
|---|
| 247 | return 1; |
|---|
| 248 | |
|---|
| 249 | for( i=0; i < len; i++ ) |
|---|
| 250 | { |
|---|
| 251 | if( str[i] != name->name[i] ) |
|---|
| 252 | return 1; |
|---|
| 253 | } |
|---|
| 254 | return 0; |
|---|
| 255 | } |
|---|
| 256 | #else |
|---|
| 257 | static int pvfs2_d_compare( |
|---|
| 258 | struct dentry *parent, |
|---|
| 259 | struct qstr *d_name, |
|---|
| 260 | struct qstr *name) |
|---|
| 261 | { |
|---|
| 262 | gossip_debug(GOSSIP_DCACHE_DEBUG, "pvfs2_d_compare: called on parent %p\n (name1: %s| " |
|---|
| 263 | "name2: %s)\n", parent, d_name->name, name->name); |
|---|
| 264 | |
|---|
| 265 | /* if we have a match, return 0 (normally called from __d_lookup) */ |
|---|
| 266 | return !((d_name->len == name->len) && |
|---|
| 267 | (d_name->hash == name->hash) && |
|---|
| 268 | (memcmp(d_name->name, name->name, d_name->len) == 0)); |
|---|
| 269 | } |
|---|
| 270 | #endif /* HAVE_SEVEN_PARAM_D_COMPARE */ |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | /** PVFS2 implementation of VFS dentry operations */ |
|---|
| 274 | struct dentry_operations pvfs2_dentry_operations = |
|---|
| 275 | { |
|---|
| 276 | .d_revalidate = pvfs2_d_revalidate, |
|---|
| 277 | .d_hash = pvfs2_d_hash, |
|---|
| 278 | .d_compare = pvfs2_d_compare, |
|---|
| 279 | .d_delete = pvfs2_d_delete, |
|---|
| 280 | }; |
|---|
| 281 | |
|---|
| 282 | /* print_dentry() |
|---|
| 283 | * |
|---|
| 284 | * Available for debugging purposes. Please remove the unused attribute |
|---|
| 285 | * before invoking |
|---|
| 286 | */ |
|---|
| 287 | static void __attribute__ ((unused)) print_dentry(struct dentry *entry, int ret) |
|---|
| 288 | { |
|---|
| 289 | unsigned int local_count = 0; |
|---|
| 290 | if(!entry) |
|---|
| 291 | { |
|---|
| 292 | printk("--- dentry %p: no entry, ret: %d\n", entry, ret); |
|---|
| 293 | return; |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | if(!entry->d_inode) |
|---|
| 297 | { |
|---|
| 298 | printk("--- dentry %p: no d_inode, ret: %d\n", entry, ret); |
|---|
| 299 | return; |
|---|
| 300 | } |
|---|
| 301 | |
|---|
| 302 | if(!entry->d_parent) |
|---|
| 303 | { |
|---|
| 304 | printk("--- dentry %p: no d_parent, ret: %d\n", entry, ret); |
|---|
| 305 | return; |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | #ifdef HAVE_DENTRY_D_COUNT_ATOMIC |
|---|
| 309 | local_count = atomic_read(&entry->d_count); |
|---|
| 310 | #else |
|---|
| 311 | spin_lock(&entry->d_lock); |
|---|
| 312 | local_count = entry->d_count; |
|---|
| 313 | spin_unlock(&entry->d_lock); |
|---|
| 314 | #endif /* HAVE_DENTRY_D_COUNT_ATOMIC */ |
|---|
| 315 | |
|---|
| 316 | printk("--- dentry %p: d_count: %d, name: %s, parent: %p, parent name: %s, ret: %d\n", |
|---|
| 317 | entry, |
|---|
| 318 | local_count, |
|---|
| 319 | entry->d_name.name, |
|---|
| 320 | entry->d_parent, |
|---|
| 321 | entry->d_parent->d_name.name, |
|---|
| 322 | ret); |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | /* |
|---|
| 326 | * Local variables: |
|---|
| 327 | * c-indent-level: 4 |
|---|
| 328 | * c-basic-offset: 4 |
|---|
| 329 | * End: |
|---|
| 330 | * |
|---|
| 331 | * vim: ts=8 sts=4 sw=4 expandtab |
|---|
| 332 | */ |
|---|