static int remove_file(struct dentry *parent, char *name) { struct dentry *tmp; int ret; tmp = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(tmp)) { ret = PTR_ERR(tmp); goto bail; } spin_lock(&dcache_lock); spin_lock(&tmp->d_lock); if (!(d_unhashed(tmp) && tmp->d_inode)) { dget_locked(tmp); __d_drop(tmp); spin_unlock(&tmp->d_lock); spin_unlock(&dcache_lock); simple_unlink(parent->d_inode, tmp); } else { spin_unlock(&tmp->d_lock); spin_unlock(&dcache_lock); } ret = 0; bail: /* * We don't expect clients to care about the return value, but * it's there if they need it. */ return ret; }
/* this should be provided by each filesystem in an nfsd_operations interface as * iget isn't really the right interface */ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation) { /* iget isn't really right if the inode is currently unallocated!! * This should really all be done inside each filesystem * * ext2fs' read_inode has been strengthed to return a bad_inode if the inode * had been deleted. * * Currently we don't know the generation for parent directory, so a generation * of 0 means "accept any" */ struct inode *inode; struct list_head *lp; struct dentry *result; if (ino == 0) return ERR_PTR(-ESTALE); inode = iget(sb, ino); if (inode == NULL) return ERR_PTR(-ENOMEM); if (is_bad_inode(inode) || (generation && inode->i_generation != generation) ) { /* we didn't find the right inode.. */ dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n", inode->i_ino, inode->i_nlink, atomic_read(&inode->i_count), inode->i_generation, generation); iput(inode); return ERR_PTR(-ESTALE); } /* now to find a dentry. * If possible, get a well-connected one */ spin_lock(&dcache_lock); for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { result = list_entry(lp,struct dentry, d_alias); if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { dget_locked(result); result->d_vfs_flags |= DCACHE_REFERENCED; spin_unlock(&dcache_lock); iput(inode); return result; } } spin_unlock(&dcache_lock); result = d_alloc_root(inode); if (result == NULL) { iput(inode); return ERR_PTR(-ENOMEM); } result->d_flags |= DCACHE_NFSD_DISCONNECTED; return result; }
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ino_t dir_ino) { struct dentry *dentry, *d; struct inode *inode; au_gen_t sigen; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); dentry = NULL; inode = ilookup(sb, ino); if (!inode) goto out; dentry = ERR_PTR(-ESTALE); sigen = au_sigen(sb); if (unlikely(is_bad_inode(inode) || IS_DEADDIR(inode) || sigen != au_iigen(inode))) goto out_iput; dentry = NULL; if (!dir_ino || S_ISDIR(inode->i_mode)) dentry = d_find_alias(inode); else { spin_lock(&dcache_lock); list_for_each_entry(d, &inode->i_dentry, d_alias) if (!au_test_anon(d) && d->d_parent->d_inode->i_ino == dir_ino) { dentry = dget_locked(d); break; } spin_unlock(&dcache_lock); } if (unlikely(dentry && sigen != au_digen(dentry))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_iput: iput(inode); out: AuTraceErrPtr(dentry); return dentry; }
void sysfs_remove_dir(struct kobject * kobj) { struct list_head * node; struct dentry * dentry = dget(kobj->dentry); if (!dentry) return; pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); down(&dentry->d_inode->i_sem); spin_lock(&dcache_lock); node = dentry->d_subdirs.next; while (node != &dentry->d_subdirs) { struct dentry * d = list_entry(node,struct dentry,d_child); list_del_init(node); pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count)); if (d->d_inode) { d = dget_locked(d); pr_debug("removing"); /** * Unlink and unhash. */ spin_unlock(&dcache_lock); d_delete(d); simple_unlink(dentry->d_inode,d); dput(d); spin_lock(&dcache_lock); } pr_debug(" done\n"); node = dentry->d_subdirs.next; } list_del_init(&dentry->d_child); spin_unlock(&dcache_lock); up(&dentry->d_inode->i_sem); remove_dir(dentry); /** * Drop reference from dget() on entrance. */ dput(dentry); }
static int remove_file(struct dentry *parent, char *name) { struct dentry *tmp; int ret; tmp = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(tmp)) { ret = PTR_ERR(tmp); goto bail; } #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)) spin_lock(&dcache_lock); #endif spin_lock(&tmp->d_lock); if (!(d_unhashed(tmp) && tmp->d_inode)) { #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)) dget_locked(tmp); #else dget_dlock(tmp); #endif __d_drop(tmp); spin_unlock(&tmp->d_lock); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)) spin_unlock(&dcache_lock); #endif simple_unlink(parent->d_inode, tmp); } else { spin_unlock(&tmp->d_lock); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)) spin_unlock(&dcache_lock); #endif } ret = 0; bail: /* * We don't expect clients to care about the return value, but * it's there if they need it. */ return ret; }
/** * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent * @sd: target sysfs_dirent * * Drop dentry for @sd. @sd must have been unlinked from its * parent on entry to this function such that it can't be looked * up anymore. * * @sd->s_dentry which is protected with sysfs_assoc_lock points * to the currently associated dentry but we're not holding a * reference to it and racing with dput(). Grab dcache_lock and * verify dentry before dropping it. If @sd->s_dentry is NULL or * dput() beats us, no need to bother. */ static void sysfs_drop_dentry(struct sysfs_dirent *sd) { struct dentry *dentry = NULL; struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_assoc_lock is held. */ spin_lock(&sysfs_assoc_lock); spin_lock(&dcache_lock); /* drop dentry if it's there and dput() didn't kill it yet */ if (sd->s_dentry && sd->s_dentry->d_inode) { dentry = dget_locked(sd->s_dentry); spin_lock(&dentry->d_lock); __d_drop(dentry); spin_unlock(&dentry->d_lock); } spin_unlock(&dcache_lock); spin_unlock(&sysfs_assoc_lock); /* dentries for shadowed inodes are pinned, unpin */ if (dentry && sysfs_is_shadowed_inode(dentry->d_inode)) dput(dentry); dput(dentry); /* adjust nlink and update timestamp */ inode = ilookup(sysfs_sb, sd->s_ino); if (inode) { mutex_lock(&inode->i_mutex); inode->i_ctime = CURRENT_TIME; drop_nlink(inode); if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); iput(inode); } }
int osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) { int code; struct dentry *dentry; struct list_head *cur, *head; /* First, see if we can evict the inode from the dcache */ if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 && avc->opens == 0) { *slept = 1; ReleaseWriteLock(&afs_xvcache); AFS_GUNLOCK(); afs_linux_lock_dcache(); head = &(AFSTOV(avc))->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); if (d_unhashed(dentry)) continue; dget_locked(dentry); afs_linux_unlock_dcache(); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); afs_linux_lock_dcache(); goto restart; } afs_linux_unlock_dcache(); inuse: AFS_GLOCK(); ObtainWriteLock(&afs_xvcache, 733); }
/* Drop dentry if it is not used already, unhash otherwise. Should be called with dcache lock held! Returns: 1 if dentry was dropped, 0 if unhashed. */ int ll_drop_dentry(struct dentry *dentry) { lock_dentry(dentry); if (atomic_read(&dentry->d_count) == 0) { CDEBUG(D_DENTRY, "deleting dentry %.*s (%p) parent %p " "inode %p\n", dentry->d_name.len, dentry->d_name.name, dentry, dentry->d_parent, dentry->d_inode); dget_locked(dentry); __d_drop(dentry); unlock_dentry(dentry); spin_unlock(&dcache_lock); cfs_spin_unlock(&ll_lookup_lock); dput(dentry); cfs_spin_lock(&ll_lookup_lock); spin_lock(&dcache_lock); return 1; } /* disconected dentry can not be find without lookup, because we * not need his to unhash or mark invalid. */ if (dentry->d_flags & DCACHE_DISCONNECTED) { unlock_dentry(dentry); RETURN (0); } if (!(dentry->d_flags & DCACHE_LUSTRE_INVALID)) { CDEBUG(D_DENTRY, "unhashing dentry %.*s (%p) parent %p " "inode %p refc %d\n", dentry->d_name.len, dentry->d_name.name, dentry, dentry->d_parent, dentry->d_inode, atomic_read(&dentry->d_count)); /* actually we don't unhash the dentry, rather just * mark it inaccessible for to __d_lookup(). otherwise * sys_getcwd() could return -ENOENT -bzzz */ dentry->d_flags |= DCACHE_LUSTRE_INVALID; if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)) __d_drop(dentry); } unlock_dentry(dentry); return 0; }
static void sel_remove_entries(struct dentry *de) { struct list_head *node; spin_lock(&dcache_lock); node = de->d_subdirs.next; while (node != &de->d_subdirs) { struct dentry *d = list_entry(node, struct dentry, d_u.d_child); list_del_init(node); if (d->d_inode) { d = dget_locked(d); spin_unlock(&dcache_lock); d_delete(d); simple_unlink(de->d_inode, d); dput(d); spin_lock(&dcache_lock); } node = de->d_subdirs.next; } spin_unlock(&dcache_lock); }
/* * dget, but require that fpos and parent matches what the dentry contains. * dentry is not known to be a valid pointer at entry. */ struct dentry * smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) { struct dentry *dent = dentry; struct list_head *next; if (d_validate(dent, parent)) { if (dent->d_name.len <= SMB_MAXNAMELEN && (unsigned long)dent->d_fsdata == fpos) { if (!dent->d_inode) { dput(dent); dent = NULL; } return dent; } dput(dent); } /* If a pointer is invalid, we search the dentry. */ spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { dent = list_entry(next, struct dentry, d_u.d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); else dent = NULL; goto out_unlock; } next = next->next; } dent = NULL; out_unlock: spin_unlock(&dcache_lock); return dent; }
static int au_dpages_append(struct au_dcsub_pages *dpages, struct dentry *dentry, gfp_t gfp) { int err, sz; struct au_dpage *dpage; void *p; dpage = dpages->dpages + dpages->ndpage - 1; sz = PAGE_SIZE / sizeof(dentry); if (unlikely(dpage->ndentry >= sz)) { AuLabel(new dpage); err = -ENOMEM; sz = dpages->ndpage * sizeof(*dpages->dpages); p = au_kzrealloc(dpages->dpages, sz, sz + sizeof(*dpages->dpages), gfp); if (unlikely(!p)) goto out; dpages->dpages = p; dpage = dpages->dpages + dpages->ndpage; p = (void *)__get_free_page(gfp); if (unlikely(!p)) goto out; dpage->ndentry = 0; dpage->dentries = p; dpages->ndpage++; } /* d_count can be zero */ dpage->dentries[dpage->ndentry++] = dget_locked(dentry); return 0; /* success */ out: return err; }
/** * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for * @nd: lookup nameidata * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the * dentry @dent. * * In more detail, the dentry @dent specifies which inode to look for by * supplying the name of the inode in @dent->d_name.name. ntfs_lookup() * converts the name to Unicode and walks the contents of the directory inode * @dir_ino looking for the converted Unicode name. If the name is found in the * directory, the corresponding inode is loaded by calling ntfs_iget() on its * inode number and the inode is associated with the dentry @dent via a call to * d_splice_alias(). * * If the name is not found in the directory, a NULL inode is inserted into the * dentry @dent via a call to d_add(). The dentry is then termed a negative * dentry. * * Only if an actual error occurs, do we return an error via ERR_PTR(). * * In order to handle the case insensitivity issues of NTFS with regards to the * dcache and the dcache requiring only one dentry per directory, we deal with * dentry aliases that only differ in case in ->ntfs_lookup() while maintaining * a case sensitive dcache. This means that we get the full benefit of dcache * speed when the file/directory is looked up with the same case as returned by * ->ntfs_readdir() but that a lookup for any other case (or for the short file * name) will not find anything in dcache and will enter ->ntfs_lookup() * instead, where we search the directory for a fully matching file name * (including case) and if that is not found, we search for a file name that * matches with different case and if that has non-POSIX semantics we return * that. We actually do only one search (case sensitive) and keep tabs on * whether we have found a case insensitive match in the process. * * To simplify matters for us, we do not treat the short vs long filenames as * two hard links but instead if the lookup matches a short filename, we * return the dentry for the corresponding long filename instead. * * There are three cases we need to distinguish here: * * 1) @dent perfectly matches (i.e. including case) a directory entry with a * file name in the WIN32 or POSIX namespaces. In this case * ntfs_lookup_inode_by_name() will return with name set to NULL and we * just d_splice_alias() @dent. * 2) @dent matches (not including case) a directory entry with a file name in * the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return * with name set to point to a kmalloc()ed ntfs_name structure containing * the properly cased little endian Unicode name. We convert the name to the * current NLS code page, search if a dentry with this name already exists * and if so return that instead of @dent. At this point things are * complicated by the possibility of 'disconnected' dentries due to NFS * which we deal with appropriately (see the code comments). The VFS will * then destroy the old @dent and use the one we returned. If a dentry is * not found, we allocate a new one, d_splice_alias() it, and return it as * above. * 3) @dent matches either perfectly or not (i.e. we don't care about case) a * directory entry with a file name in the DOS namespace. In this case * ntfs_lookup_inode_by_name() will return with name set to point to a * kmalloc()ed ntfs_name structure containing the mft reference (cpu endian) * of the inode. We use the mft reference to read the inode and to find the * file name in the WIN32 namespace corresponding to the matched short file * name. We then convert the name to the current NLS code page, and proceed * searching for a dentry with this name, etc, as in case 2), above. * * Locking: Caller must hold i_mutex on the directory. */ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, struct nameidata *nd) { ntfs_volume *vol = NTFS_SB(dir_ino->i_sb); struct inode *dent_inode; ntfschar *uname; ntfs_name *name = NULL; MFT_REF mref; unsigned long dent_ino; int uname_len; ntfs_debug("Looking up %s in directory inode 0x%lx.", dent->d_name.name, dir_ino->i_ino); /* Convert the name of the dentry to Unicode. */ uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, &uname); if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(vol->sb, "Failed to convert name to " "Unicode."); return ERR_PTR(uname_len); } mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, &name); kmem_cache_free(ntfs_name_cache, uname); if (!IS_ERR_MREF(mref)) { dent_ino = MREF(mref); ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino); dent_inode = ntfs_iget(vol->sb, dent_ino); if (likely(!IS_ERR(dent_inode))) { /* Consistency check. */ if (is_bad_inode(dent_inode) || MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { /* Perfect WIN32/POSIX match. -- Case 1. */ if (!name) { ntfs_debug("Done. (Case 1.)"); return d_splice_alias(dent_inode, dent); } /* * We are too indented. Handle imperfect * matches and short file names further below. */ goto handle_name; } ntfs_error(vol->sb, "Found stale reference to inode " "0x%lx (reference sequence number = " "0x%x, inode sequence number = 0x%x), " "returning -EIO. Run chkdsk.", dent_ino, MSEQNO(mref), NTFS_I(dent_inode)->seq_no); iput(dent_inode); dent_inode = ERR_PTR(-EIO); } else ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with " "error code %li.", dent_ino, PTR_ERR(dent_inode)); kfree(name); /* Return the error code. */ return (struct dentry *)dent_inode; } /* It is guaranteed that @name is no longer allocated at this point. */ if (MREF_ERR(mref) == -ENOENT) { ntfs_debug("Entry was not found, adding negative dentry."); /* The dcache will handle negative entries. */ d_add(dent, NULL); ntfs_debug("Done."); return NULL; } ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error " "code %i.", -MREF_ERR(mref)); return ERR_PTR(MREF_ERR(mref)); // TODO: Consider moving this lot to a separate function! (AIA) handle_name: { struct dentry *real_dent, *new_dent; MFT_RECORD *m; ntfs_attr_search_ctx *ctx; ntfs_inode *ni = NTFS_I(dent_inode); int err; struct qstr nls_name; nls_name.name = NULL; if (name->type != FILE_NAME_DOS) { /* Case 2. */ ntfs_debug("Case 2."); nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&name->name, name->len, (unsigned char**)&nls_name.name, 0); kfree(name); } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ FILE_NAME_ATTR *fn; ntfs_debug("Case 3."); kfree(name); /* Find the WIN32 name corresponding to the matched DOS name. */ ni = NTFS_I(dent_inode); m = map_mft_record(ni); if (IS_ERR(m)) { err = PTR_ERR(m); m = NULL; ctx = NULL; goto err_out; } ctx = ntfs_attr_get_search_ctx(ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto err_out; } do { ATTR_RECORD *a; u32 val_len; err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_error(vol->sb, "Inode corrupt: No WIN32 " "namespace counterpart to DOS " "file name. Run chkdsk."); if (err == -ENOENT) err = -EIO; goto err_out; } /* Consistency checks. */ a = ctx->attr; if (a->non_resident || a->flags) goto eio_err_out; val_len = le32_to_cpu(a->data.resident.value_length); if (le16_to_cpu(a->data.resident.value_offset) + val_len > le32_to_cpu(a->length)) goto eio_err_out; fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu( ctx->attr->data.resident.value_offset)); if ((u32)(fn->file_name_length * sizeof(ntfschar) + sizeof(FILE_NAME_ATTR)) > val_len) goto eio_err_out; } while (fn->file_name_type != FILE_NAME_WIN32); /* Convert the found WIN32 name to current NLS code page. */ nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&fn->file_name, fn->file_name_length, (unsigned char**)&nls_name.name, 0); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); } m = NULL; ctx = NULL; /* Check if a conversion error occurred. */ if ((signed)nls_name.len < 0) { err = (signed)nls_name.len; goto err_out; } nls_name.hash = full_name_hash(nls_name.name, nls_name.len); /* * Note: No need for dent->d_lock lock as i_mutex is held on the * parent inode. */ /* Does a dentry matching the nls_name exist already? */ real_dent = d_lookup(dent->d_parent, &nls_name); /* If not, create it now. */ if (!real_dent) { real_dent = d_alloc(dent->d_parent, &nls_name); kfree(nls_name.name); if (!real_dent) { err = -ENOMEM; goto err_out; } new_dent = d_splice_alias(dent_inode, real_dent); if (new_dent) dput(real_dent); else new_dent = real_dent; ntfs_debug("Done. (Created new dentry.)"); return new_dent; } kfree(nls_name.name); /* Matching dentry exists, check if it is negative. */ if (real_dent->d_inode) { if (unlikely(real_dent->d_inode != dent_inode)) { /* This can happen because bad inodes are unhashed. */ BUG_ON(!is_bad_inode(dent_inode)); BUG_ON(!is_bad_inode(real_dent->d_inode)); } /* * Already have the inode and the dentry attached, decrement * the reference count to balance the ntfs_iget() we did * earlier on. We found the dentry using d_lookup() so it * cannot be disconnected and thus we do not need to worry * about any NFS/disconnectedness issues here. */ iput(dent_inode); ntfs_debug("Done. (Already had inode and dentry.)"); return real_dent; } /* * Negative dentry: instantiate it unless the inode is a directory and * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED), * in which case d_move() that in place of the found dentry. */ if (!S_ISDIR(dent_inode->i_mode)) { /* Not a directory; everything is easy. */ d_instantiate(real_dent, dent_inode); ntfs_debug("Done. (Already had negative file dentry.)"); return real_dent; } spin_lock(&dcache_lock); if (list_empty(&dent_inode->i_dentry)) { /* * Directory without a 'disconnected' dentry; we need to do * d_instantiate() by hand because it takes dcache_lock which * we already hold. */ list_add(&real_dent->d_alias, &dent_inode->i_dentry); real_dent->d_inode = dent_inode; spin_unlock(&dcache_lock); security_d_instantiate(real_dent, dent_inode); ntfs_debug("Done. (Already had negative directory dentry.)"); return real_dent; } /* * Directory with a 'disconnected' dentry; get a reference to the * 'disconnected' dentry. */ new_dent = list_entry(dent_inode->i_dentry.next, struct dentry, d_alias); dget_locked(new_dent); spin_unlock(&dcache_lock); /* Do security vodoo. */ security_d_instantiate(real_dent, dent_inode); /* Move new_dent in place of real_dent. */ d_move(new_dent, real_dent); /* Balance the ntfs_iget() we did above. */ iput(dent_inode); /* Throw away real_dent. */ dput(real_dent); /* Use new_dent as the actual dentry. */ ntfs_debug("Done. (Already had negative, disconnected directory " "dentry.)"); return new_dent; eio_err_out: ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); err = -EIO; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(ni); iput(dent_inode); ntfs_error(vol->sb, "Failed, returning error code %i.", err); return ERR_PTR(err); } }
/** * sysfs_get_dentry - get dentry for the given sysfs_dirent * @sd: sysfs_dirent of interest * * Get dentry for @sd. Dentry is looked up if currently not * present. This function climbs sysfs_dirent tree till it * reaches a sysfs_dirent with valid dentry attached and descends * down from there looking up dentry for each step. * * LOCKING: * Kernel thread context (may sleep) * * RETURNS: * Pointer to found dentry on success, ERR_PTR() value on error. */ struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) { struct sysfs_dirent *cur; struct dentry *parent_dentry, *dentry; int i, depth; /* Find the first parent which has valid s_dentry and get the * dentry. */ mutex_lock(&sysfs_mutex); restart0: spin_lock(&sysfs_assoc_lock); restart1: spin_lock(&dcache_lock); dentry = NULL; depth = 0; cur = sd; while (!cur->s_dentry || !cur->s_dentry->d_inode) { if (cur->s_flags & SYSFS_FLAG_REMOVED) { dentry = ERR_PTR(-ENOENT); depth = 0; break; } cur = cur->s_parent; depth++; } if (!IS_ERR(dentry)) dentry = dget_locked(cur->s_dentry); spin_unlock(&dcache_lock); spin_unlock(&sysfs_assoc_lock); /* from the found dentry, look up depth times */ while (depth--) { /* find and get depth'th ancestor */ for (cur = sd, i = 0; cur && i < depth; i++) cur = cur->s_parent; /* This can happen if tree structure was modified due * to move/rename. Restart. */ if (i != depth) { dput(dentry); goto restart0; } sysfs_get(cur); mutex_unlock(&sysfs_mutex); /* look it up */ parent_dentry = dentry; dentry = lookup_one_len_kern(cur->s_name, parent_dentry, strlen(cur->s_name)); dput(parent_dentry); if (IS_ERR(dentry)) { sysfs_put(cur); return dentry; } mutex_lock(&sysfs_mutex); spin_lock(&sysfs_assoc_lock); /* This, again, can happen if tree structure has * changed and we looked up the wrong thing. Restart. */ if (cur->s_dentry != dentry) { dput(dentry); sysfs_put(cur); goto restart1; } spin_unlock(&sysfs_assoc_lock); sysfs_put(cur); } mutex_unlock(&sysfs_mutex); return dentry; }
int osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) { int code; struct dentry *dentry; struct inode *inode = AFSTOV(avc); struct list_head *cur, *head; /* First, see if we can evict the inode from the dcache */ if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 && avc->opens == 0) { *slept = 1; ReleaseWriteLock(&afs_xvcache); AFS_GUNLOCK(); #if defined(HAVE_DCACHE_LOCK) spin_lock(&dcache_lock); head = &inode->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); if (d_unhashed(dentry)) continue; dget_locked(dentry); spin_unlock(&dcache_lock); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); spin_lock(&dcache_lock); goto restart; } spin_unlock(&dcache_lock); #else /* HAVE_DCACHE_LOCK */ spin_lock(&inode->i_lock); head = &inode->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); spin_lock(&dentry->d_lock); if (d_unhashed(dentry)) { spin_unlock(&dentry->d_lock); continue; } spin_unlock(&dentry->d_lock); dget(dentry); spin_unlock(&inode->i_lock); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); spin_lock(&inode->i_lock); goto restart; } spin_unlock(&inode->i_lock); #endif /* HAVE_DCACHE_LOCK */ inuse: AFS_GLOCK(); ObtainWriteLock(&afs_xvcache, 733); }
struct dentry * find_exported_dentry(struct super_block *sb, void *obj, void *parent, int (*acceptable)(void *context, struct dentry *de), void *context) { struct dentry *result = NULL; struct dentry *target_dir; int err; struct export_operations *nops = sb->s_export_op; struct list_head *le, *head; struct dentry *toput = NULL; int noprogress; /* * Attempt to find the inode. */ result = CALL(sb->s_export_op,get_dentry)(sb,obj); err = -ESTALE; if (result == NULL) goto err_out; if (IS_ERR(result)) { err = PTR_ERR(result); goto err_out; } if (S_ISDIR(result->d_inode->i_mode) && (result->d_flags & DCACHE_DISCONNECTED)) { /* it is an unconnected directory, we must connect it */ ; } else { if (acceptable(context, result)) return result; if (S_ISDIR(result->d_inode->i_mode)) { /* there is no other dentry, so fail */ goto err_result; } /* try any other aliases */ spin_lock(&dcache_lock); head = &result->d_inode->i_dentry; list_for_each(le, head) { struct dentry *dentry = list_entry(le, struct dentry, d_alias); dget_locked(dentry); spin_unlock(&dcache_lock); if (toput) dput(toput); toput = NULL; if (dentry != result && acceptable(context, dentry)) { dput(result); return dentry; } spin_lock(&dcache_lock); toput = dentry; } spin_unlock(&dcache_lock); if (toput) dput(toput); } /* It's a directory, or we are required to confirm the file's * location in the tree based on the parent information */ dprintk("find_exported_dentry: need to look harder for %s/%d\n",sb->s_id,*(int*)obj); if (S_ISDIR(result->d_inode->i_mode)) target_dir = dget(result); else { if (parent == NULL) goto err_result; target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent); if (IS_ERR(target_dir)) err = PTR_ERR(target_dir); if (target_dir == NULL || IS_ERR(target_dir)) goto err_result; } /* * Now we need to make sure that target_dir is properly connected. * It may already be, as the flag isn't always updated when connection * happens. * So, we walk up parent links until we find a connected directory, * or we run out of directories. Then we find the parent, find * the name of the child in that parent, and do a lookup. * This should connect the child into the parent * We then repeat. */ /* it is possible that a confused file system might not let us complete * the path to the root. For example, if get_parent returns a directory * in which we cannot find a name for the child. While this implies a * very sick filesystem we don't want it to cause knfsd to spin. Hence * the noprogress counter. If we go through the loop 10 times (2 is * probably enough) without getting anywhere, we just give up */ noprogress= 0; while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) { struct dentry *pd = target_dir; dget(pd); spin_lock(&pd->d_lock); while (!IS_ROOT(pd) && (pd->d_parent->d_flags&DCACHE_DISCONNECTED)) { struct dentry *parent = pd->d_parent; dget(parent); spin_unlock(&pd->d_lock); dput(pd); pd = parent; spin_lock(&pd->d_lock); } spin_unlock(&pd->d_lock); if (!IS_ROOT(pd)) { /* must have found a connected parent - great */ spin_lock(&pd->d_lock); pd->d_flags &= ~DCACHE_DISCONNECTED; spin_unlock(&pd->d_lock); noprogress = 0; } else if (pd == sb->s_root) { printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n"); spin_lock(&pd->d_lock); pd->d_flags &= ~DCACHE_DISCONNECTED; spin_unlock(&pd->d_lock); noprogress = 0; } else { /* we have hit the top of a disconnected path. Try * to find parent and connect * note: racing with some other process renaming a * directory isn't much of a problem here. If someone * renames the directory, it will end up properly * connected, which is what we want */ struct dentry *ppd; struct dentry *npd; char nbuf[NAME_MAX+1]; down(&pd->d_inode->i_sem); ppd = CALL(nops,get_parent)(pd); up(&pd->d_inode->i_sem); if (IS_ERR(ppd)) { err = PTR_ERR(ppd); dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n", pd->d_inode->i_ino, err); dput(pd); break; } dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino); err = CALL(nops,get_name)(ppd, nbuf, pd); if (err) { dput(ppd); dput(pd); if (err == -ENOENT) /* some race between get_parent and * get_name? just try again */ continue; break; } dprintk("find_exported_dentry: found name: %s\n", nbuf); down(&ppd->d_inode->i_sem); npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); up(&ppd->d_inode->i_sem); if (IS_ERR(npd)) { err = PTR_ERR(npd); dprintk("find_exported_dentry: lookup failed: %d\n", err); dput(ppd); dput(pd); break; } /* we didn't really want npd, we really wanted * a side-effect of the lookup. * hopefully, npd == pd, though it isn't really * a problem if it isn't */ if (npd == pd) noprogress = 0; else printk("find_exported_dentry: npd != pd\n"); dput(npd); dput(ppd); if (IS_ROOT(pd)) { /* something went wrong, we have to give up */ dput(pd); break; } } dput(pd); } if (target_dir->d_flags & DCACHE_DISCONNECTED) { /* something went wrong - oh-well */ if (!err) err = -ESTALE; goto err_target; } /* if we weren't after a directory, have one more step to go */ if (result != target_dir) { struct dentry *nresult; char nbuf[NAME_MAX+1]; err = CALL(nops,get_name)(target_dir, nbuf, result); if (!err) { down(&target_dir->d_inode->i_sem); nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); up(&target_dir->d_inode->i_sem); if (!IS_ERR(nresult)) { if (nresult->d_inode) { dput(result); result = nresult; } else dput(nresult); } } } dput(target_dir); /* now result is properly connected, it is our best bet */ if (acceptable(context, result)) return result; /* one last try of the aliases.. */ spin_lock(&dcache_lock); toput = NULL; head = &result->d_inode->i_dentry; list_for_each(le, head) { struct dentry *dentry = list_entry(le, struct dentry, d_alias); dget_locked(dentry); spin_unlock(&dcache_lock); if (toput) dput(toput); if (dentry != result && acceptable(context, dentry)) { dput(result); return dentry; } spin_lock(&dcache_lock); toput = dentry; } spin_unlock(&dcache_lock); if (toput) dput(toput); /* drat - I just cannot find anything acceptable */ dput(result); /* It might be justifiable to return ESTALE here, * but the filehandle at-least looks reasonable good * and it just be a permission problem, so returning * -EACCESS is safer */ return ERR_PTR(-EACCES); err_target: dput(target_dir); err_result: dput(result); err_out: return ERR_PTR(err); }