/* * Walk down the mount stack looking for an autofs mount that * has the requested mount type (ie. indirect, direct or offset). */ static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) { struct dentry *dentry; struct autofs_info *ino; unsigned int err; err = -ENOENT; /* Lookup the dentry name at the base of our mount point */ dentry = d_lookup(nd->path.dentry, &nd->last); if (!dentry) goto out; dput(nd->path.dentry); nd->path.dentry = dentry; /* And follow the mount stack looking for our autofs mount */ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ino = autofs4_dentry_ino(nd->path.dentry); if (ino && ino->sbi->type & type) { err = 0; break; } } out: return err; }
/* * Lookup or create a dentry from within the filesystem. * * We need to use this instead of lookup_dentry, as the * directory semaphore lock is already held. */ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len, int real) { struct dentry *result, *dentry; struct qstr qstr; qstr.name = name; qstr.len = len; qstr.hash = full_name_hash(name, len); result = d_lookup(parent, &qstr); if (!result) { result = ERR_PTR(-ENOMEM); dentry = d_alloc(parent, &qstr); if (dentry) { result = real ? UMSDOS_rlookup(parent->d_inode, dentry) : UMSDOS_lookup(parent->d_inode, dentry); if (result) goto out_fail; return dentry; } } out: return result; out_fail: dput(dentry); goto out; }
/* * Find the dentry that matches "name". If there isn't one, create one. If it's * a negative dentry or the uniqueid changed, then drop it and recreate it. */ static struct dentry * cifs_readdir_lookup(struct dentry *parent, struct qstr *name, struct cifs_fattr *fattr) { struct dentry *dentry, *alias; struct inode *inode; struct super_block *sb = parent->d_inode->i_sb; cFYI(1, "For %s", name->name); if (parent->d_op && parent->d_op->d_hash) parent->d_op->d_hash(parent, parent->d_inode, name); else name->hash = full_name_hash(name->name, name->len); dentry = d_lookup(parent, name); if (dentry) { inode = dentry->d_inode; /* update inode in place if i_ino didn't change */ if (inode && CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) { cifs_fattr_to_inode(inode, fattr); return dentry; } d_drop(dentry); dput(dentry); } /* * If we know that the inode will need to be revalidated immediately, * then don't create a new dentry for it. We'll end up doing an on * the wire call either way and this spares us an invalidation. */ if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) return NULL; dentry = d_alloc(parent, name); if (dentry == NULL) return NULL; inode = cifs_iget(sb, fattr); if (!inode) { dput(dentry); return NULL; } alias = d_materialise_unique(dentry, inode); if (alias != NULL) { dput(dentry); if (IS_ERR(alias)) return NULL; dentry = alias; } return dentry; }
/* * ------ samqfs_notify_dnlc_remove - * Look for a dnlc/dcache entry of pip/comp * and delete it from the dnlc/dcache. */ void samqfs_notify_dnlc_remove( sam_node_t *pip, /* parent inode */ char *comp) /* name to be deleted from the dcache */ { #ifdef sun (void) dnlc_remove(SAM_ITOV(pip), comp); #endif /* sun */ #ifdef linux struct inode *pli; int length; struct dentry *dir, *de; struct qstr qstr; if (pip == NULL) { return; } if (comp == NULL) { return; } pli = SAM_SITOLI(pip); length = strlen(comp); if ((length == 0) || (length > NAME_MAX)) { return; } /* * Find the dcache entry of the parent. */ dir = d_find_alias(pli); if (dir) { /* * Find the dcache entry of the component. */ qstr.name = comp; qstr.len = length; qstr.hash = rfs_full_name_hash(comp, length); de = d_lookup(dir, &qstr); if (de) { rfs_d_drop(de); dput(de); } dput(dir); } #endif /* linux */ }
/* Might check in the future if inode number changed so we can rehash inode */ static int construct_dentry(struct qstr *qstring, struct file *file, struct inode **ptmp_inode, struct dentry **pnew_dentry) { struct dentry *tmp_dentry; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int rc = 0; cFYI(1, ("For %s", qstring->name)); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; qstring->hash = full_name_hash(qstring->name, qstring->len); tmp_dentry = d_lookup(file->f_path.dentry, qstring); if (tmp_dentry) { cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode)); *ptmp_inode = tmp_dentry->d_inode; /* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/ if (*ptmp_inode == NULL) { *ptmp_inode = new_inode(file->f_path.dentry->d_sb); if (*ptmp_inode == NULL) return rc; rc = 1; } if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME) (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; } else { tmp_dentry = d_alloc(file->f_path.dentry, qstring); if (tmp_dentry == NULL) { cERROR(1, ("Failed allocating dentry")); *ptmp_inode = NULL; return rc; } *ptmp_inode = new_inode(file->f_path.dentry->d_sb); if (pTcon->nocase) tmp_dentry->d_op = &cifs_ci_dentry_ops; else tmp_dentry->d_op = &cifs_dentry_ops; if (*ptmp_inode == NULL) return rc; if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME) (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; rc = 2; } tmp_dentry->d_time = jiffies; *pnew_dentry = tmp_dentry; return rc; }
/* Might check in the future if inode number changed so we can rehash inode */ static int construct_dentry(struct qstr *qstring, struct file *file, struct inode **ptmp_inode, struct dentry **pnew_dentry, __u64 *inum) { struct dentry *tmp_dentry = NULL; struct super_block *sb = file->f_path.dentry->d_sb; int rc = 0; cFYI(1, ("For %s", qstring->name)); qstring->hash = full_name_hash(qstring->name, qstring->len); tmp_dentry = d_lookup(file->f_path.dentry, qstring); if (tmp_dentry) { /* BB: overwrite old name? i.e. tmp_dentry->d_name and * tmp_dentry->d_name.len?? */ cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode)); *ptmp_inode = tmp_dentry->d_inode; if (*ptmp_inode == NULL) { *ptmp_inode = cifs_new_inode(sb, inum); if (*ptmp_inode == NULL) return rc; rc = 1; } } else { tmp_dentry = d_alloc(file->f_path.dentry, qstring); if (tmp_dentry == NULL) { cERROR(1, ("Failed allocating dentry")); *ptmp_inode = NULL; return rc; } if (CIFS_SB(sb)->tcon->nocase) tmp_dentry->d_op = &cifs_ci_dentry_ops; else tmp_dentry->d_op = &cifs_dentry_ops; *ptmp_inode = cifs_new_inode(sb, inum); if (*ptmp_inode == NULL) return rc; rc = 2; } tmp_dentry->d_time = jiffies; *pnew_dentry = tmp_dentry; return rc; }
static struct dentry * cifs_readdir_lookup(struct dentry *parent, struct qstr *name, struct cifs_fattr *fattr) { struct dentry *dentry, *alias; struct inode *inode; struct super_block *sb = parent->d_inode->i_sb; cFYI(1, "For %s", name->name); if (parent->d_op && parent->d_op->d_hash) parent->d_op->d_hash(parent, parent->d_inode, name); else name->hash = full_name_hash(name->name, name->len); dentry = d_lookup(parent, name); if (dentry) { inode = dentry->d_inode; /* update inode in place if i_ino didn't change */ if (inode && CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) { cifs_fattr_to_inode(inode, fattr); return dentry; } d_drop(dentry); dput(dentry); } dentry = d_alloc(parent, name); if (dentry == NULL) return NULL; inode = cifs_iget(sb, fattr); if (!inode) { dput(dentry); return NULL; } alias = d_materialise_unique(dentry, inode); if (alias != NULL) { dput(dentry); if (IS_ERR(alias)) return NULL; dentry = alias; } return dentry; }
/* Might check in the future if inode number changed so we can rehash inode */ static int construct_dentry(struct qstr *qstring, struct file *file, struct inode **ptmp_inode, struct dentry **pnew_dentry) { struct dentry *tmp_dentry; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int rc = 0; cFYI(1, ("For %s", qstring->name)); cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; qstring->hash = full_name_hash(qstring->name, qstring->len); tmp_dentry = d_lookup(file->f_dentry, qstring); if (tmp_dentry) { cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode)); *ptmp_inode = tmp_dentry->d_inode; /* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/ if(*ptmp_inode == NULL) { *ptmp_inode = new_inode(file->f_dentry->d_sb); if(*ptmp_inode == NULL) return rc; rc = 1; d_instantiate(tmp_dentry, *ptmp_inode); } } else { tmp_dentry = d_alloc(file->f_dentry, qstring); if(tmp_dentry == NULL) { cERROR(1,("Failed allocating dentry")); *ptmp_inode = NULL; return rc; } *ptmp_inode = new_inode(file->f_dentry->d_sb); tmp_dentry->d_op = &cifs_dentry_ops; if(*ptmp_inode == NULL) return rc; rc = 1; d_instantiate(tmp_dentry, *ptmp_inode); d_rehash(tmp_dentry); } tmp_dentry->d_time = jiffies; *pnew_dentry = tmp_dentry; return rc; }
/** * \<\<private\>\> Checks whether a directory entry contains entry of * the specified name * * @param *self - pointer to this instance * @param *name - pointer to the name to be checked * @return 1 - if the entry of the specified name exists */ static int tcmi_ctlfs_entry_exists(struct tcmi_ctlfs_entry *self, const char *name) { struct dentry *dentry; struct qstr q; int exists = 0; q.name = name; q.len = strlen(name); q.hash = full_name_hash(q.name, q.len); dentry = d_lookup(self->dentry, &q); /* dentry already exists, decrement its reference counter since lookup incremented it */ if (dentry) { dput(dentry); exists = 1; } return exists; }
/* * Find the dentry that matches "name". If there isn't one, create one. If it's * a negative dentry or the uniqueid changed, then drop it and recreate it. */ static struct dentry * cifs_readdir_lookup(struct dentry *parent, struct qstr *name, struct cifs_fattr *fattr) { struct dentry *dentry, *alias; struct inode *inode; struct super_block *sb = parent->d_inode->i_sb; cFYI(1, "For %s", name->name); if (parent->d_op && parent->d_op->d_hash) parent->d_op->d_hash(parent, parent->d_inode, name); else name->hash = full_name_hash(name->name, name->len); dentry = d_lookup(parent, name); if (dentry) { /* FIXME: check for inode number changes? */ if (dentry->d_inode != NULL) return dentry; d_drop(dentry); dput(dentry); } dentry = d_alloc(parent, name); if (dentry == NULL) return NULL; inode = cifs_iget(sb, fattr); if (!inode) { dput(dentry); return NULL; } alias = d_materialise_unique(dentry, inode); if (alias != NULL) { dput(dentry); if (IS_ERR(alias)) return NULL; dentry = alias; } return dentry; }
/* * Find the dentry that matches "name". If there isn't one, create one. If it's * a negative dentry or the uniqueid changed, then drop it and recreate it. */ static struct dentry * cifs_readdir_lookup(struct dentry *parent, struct qstr *name, struct cifs_fattr *fattr) { struct dentry *dentry, *alias; struct inode *inode; struct super_block *sb = parent->d_inode->i_sb; cFYI(1, ("For %s", name->name)); dentry = d_lookup(parent, name); if (dentry) { /* FIXME: check for inode number changes? */ if (dentry->d_inode != NULL) return dentry; d_drop(dentry); dput(dentry); } dentry = d_alloc(parent, name); if (dentry == NULL) return NULL; inode = cifs_iget(sb, fattr); if (!inode) { dput(dentry); return NULL; } if (CIFS_SB(sb)->tcon->nocase) dentry->d_op = &cifs_ci_dentry_ops; else dentry->d_op = &cifs_dentry_ops; alias = d_materialise_unique(dentry, inode); if (alias != NULL) { dput(dentry); if (IS_ERR(alias)) return NULL; dentry = alias; } return dentry; }
/* D_NEWPROC -- Process a procedure declaration. The name of the procedure * is passed as the single argument. The input stream is left positioned * with the ( of the argument list as the next token (if present). INPUT is * called repeatedly to read the remainder of the declaration, which may span * several lines. The symbol table is cleared whenever a new procedure * declaration is started. */ void d_newproc ( char *name, /* procedure name */ int dtype /* procedure type (0 if subr) */ ) { register int token; char tokstr[SZ_TOKEN+1]; strncpy (procname, name, SZ_FNAME); proctype = dtype; nextch = sbuf; nsym = 0; /* Check for null argument list. */ if (d_gettok(tokstr,SZ_TOKEN) != '(') return; /* Process the argument list. */ while ((token = d_gettok(tokstr,SZ_TOKEN)) != ')') { if (isalpha(token)) { /* Enter argument name into the symbol table. */ if (d_lookup (tokstr) != NULL) { char lbuf[200]; sprintf (lbuf, "%s.%s multiply declared", procname, tokstr); xpp_warn (lbuf); } else d_enter (tokstr, UNDECL, S_ARGUMENT); } else if (token == '\n') { linenum[istkptr]++; continue; } else if (token == ',') { continue; } else error (XPP_SYNTAX, "bad syntax in procedure argument list"); } }
int main(int argc, char ** argv) { int i; char word[MAX_WORD_SIZE+1]; char answer[MAX_DESC_SIZE+1]; d_initialise(); for (i=1; i<argc; i++) d_read_from_file(argv[i]); scanf("%s",word); while(word[0] != '.') { if (d_lookup(word,answer)) printf("%s:\t%s\n", word, answer); else printf("%s:\t%s\n", word, "Not in dictionary"); scanf("%s",word); } }
static int proc_sys_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct ctl_table_header *head, struct ctl_table *table) { struct dentry *child, *dir = filp->f_path.dentry; struct inode *inode; struct qstr qname; ino_t ino = 0; unsigned type = DT_UNKNOWN; qname.name = table->procname; qname.len = strlen(table->procname); qname.hash = full_name_hash(qname.name, qname.len); child = d_lookup(dir, &qname); if (!child) { child = d_alloc(dir, &qname); if (child) { inode = proc_sys_make_inode(dir->d_sb, head, table); if (!inode) { dput(child); return -ENOMEM; } else { d_set_d_op(child, &proc_sys_dentry_operations); d_add(child, inode); } } else { return -ENOMEM; } } inode = child->d_inode; ino = inode->i_ino; type = inode->i_mode >> 12; dput(child); return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); }
/* * Walk down the mount stack looking for an autofs mount that * has the requested device number (aka. new_encode_dev(sb->s_dev). */ static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) { struct dentry *dentry; struct inode *inode; struct super_block *sb; dev_t s_dev; unsigned int err; err = -ENOENT; /* Lookup the dentry name at the base of our mount point */ dentry = d_lookup(nd->path.dentry, &nd->last); if (!dentry) goto out; dput(nd->path.dentry); nd->path.dentry = dentry; /* And follow the mount stack looking for our autofs mount */ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { inode = nd->path.dentry->d_inode; if (!inode) break; sb = inode->i_sb; s_dev = new_encode_dev(sb->s_dev); if (devno == s_dev) { if (sb->s_magic == AUTOFS_SUPER_MAGIC) { err = 0; break; } } } out: return err; }
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; mutex_lock(&parent->i_mutex); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_invalidate_attr(parent); fuse_invalidate_entry(entry); dput(entry); err = 0; unlock: mutex_unlock(&parent->i_mutex); iput(parent); return err; }
/* * Main driver function for wrapfs's lookup. * * Returns: NULL (ok), ERR_PTR if an error occurred. * Fills in lower_parent_path with <dentry,mnt> on success. */ static struct dentry *__wrapfs_lookup(struct dentry *dentry, int flags, struct path *lower_parent_path) { int err = 0; struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; const char *name; struct path lower_path; struct qstr this; /* must initialize dentry operations */ d_set_d_op(dentry, &wrapfs_dops); if (IS_ROOT(dentry)) goto out; name = dentry->d_name.name; /* now start the actual lookup procedure */ lower_dir_dentry = lower_parent_path->dentry; lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_path); /* no error: handle positive dentries */ if (!err) { wrapfs_set_lower_path(dentry, &lower_path); err = wrapfs_interpose(dentry, dentry->d_sb, &lower_path); if (err) /* path_put underlying path on error */ wrapfs_put_reset_lower_path(dentry); goto out; } /* * We don't consider ENOENT an error, and we want to return a * negative dentry. */ if (err && err != -ENOENT) goto out; /* instatiate a new negative dentry */ this.name = name; this.len = strlen(name); this.hash = full_name_hash(this.name, this.len); lower_dentry = d_lookup(lower_dir_dentry, &this); if (lower_dentry) goto setup_lower; lower_dentry = d_alloc(lower_dir_dentry, &this); if (!lower_dentry) { err = -ENOMEM; goto out; } d_add(lower_dentry, NULL); /* instantiate and hash */ setup_lower: lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); wrapfs_set_lower_path(dentry, &lower_path); /* * If the intent is to create a file, then don't return an error, so * the VFS will continue the process of making this negative dentry * into a positive one. */ if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) err = 0; out: return ERR_PTR(err); }
/* * Create dentry/inode for this file and add it to the dircache. */ int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry) { struct dentry *newdent, *dentry = filp->f_path.dentry; struct inode *newino, *inode = dentry->d_inode; struct smb_cache_control ctl = *ctrl; int valid = 0; int hashed = 0; ino_t ino = 0; qname->hash = full_name_hash(qname->name, qname->len); if (dentry->d_op && dentry->d_op->d_hash) if (dentry->d_op->d_hash(dentry, qname) != 0) goto end_advance; newdent = d_lookup(dentry, qname); if (!newdent) { newdent = d_alloc(dentry, qname); if (!newdent) goto end_advance; } else { hashed = 1; memcpy((char *) newdent->d_name.name, qname->name, newdent->d_name.len); } if (!newdent->d_inode) { smb_renew_times(newdent); entry->f_ino = iunique(inode->i_sb, 2); newino = smb_iget(inode->i_sb, entry); if (newino) { smb_new_dentry(newdent); d_instantiate(newdent, newino); if (!hashed) d_rehash(newdent); } } else smb_set_inode_attr(newdent->d_inode, entry); if (newdent->d_inode) { ino = newdent->d_inode->i_ino; newdent->d_fsdata = (void *) ctl.fpos; smb_new_dentry(newdent); } if (ctl.idx >= SMB_DIRCACHE_SIZE) { if (ctl.page) { kunmap(ctl.page); SetPageUptodate(ctl.page); unlock_page(ctl.page); page_cache_release(ctl.page); } ctl.cache = NULL; ctl.idx -= SMB_DIRCACHE_SIZE; ctl.ofs += 1; ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); if (ctl.page) ctl.cache = kmap(ctl.page); } if (ctl.cache) { ctl.cache->dentry[ctl.idx] = newdent; valid = 1; } dput(newdent); end_advance: if (!valid) ctl.valid = 0; if (!ctl.filled && (ctl.fpos == filp->f_pos)) { if (!ino) ino = find_inode_number(dentry, qname); if (!ino) ino = iunique(inode->i_sb, 2); ctl.filled = filldir(dirent, qname->name, qname->len, filp->f_pos, ino, DT_UNKNOWN); if (!ctl.filled) filp->f_pos += 1; } ctl.fpos += 1; ctl.idx += 1; *ctrl = ctl; return (ctl.valid || !ctl.filled); }
static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, u64 attr_version) { struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; struct dentry *parent = file->f_path.dentry; struct qstr name = QSTR_INIT(dirent->name, dirent->namelen); struct dentry *dentry; struct dentry *alias; struct inode *dir = d_inode(parent); struct fuse_conn *fc; struct inode *inode; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing. */ return 0; } if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent? */ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0; } if (invalid_nodeid(o->nodeid)) return -EIO; if (!fuse_valid_type(o->attr.mode)) return -EIO; fc = get_fuse_conn(dir); name.hash = full_name_hash(parent, name.name, name.len); dentry = d_lookup(parent, &name); if (!dentry) { retry: dentry = d_alloc_parallel(parent, &name, &wq); if (IS_ERR(dentry)) return PTR_ERR(dentry); } if (!d_in_lookup(dentry)) { struct fuse_inode *fi; inode = d_inode(dentry); if (!inode || get_node_id(inode) != o->nodeid || ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { d_invalidate(dentry); dput(dentry); goto retry; } if (is_bad_inode(inode)) { dput(dentry); return -EIO; } fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); forget_all_cached_acls(inode); fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), attr_version); /* * The other branch comes via fuse_iget() * which bumps nlookup inside */ } else { inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, entry_attr_timeout(o), attr_version); if (!inode) inode = ERR_PTR(-ENOMEM); alias = d_splice_alias(inode, dentry); d_lookup_done(dentry); if (alias) { dput(dentry); dentry = alias; } if (IS_ERR(dentry)) return PTR_ERR(dentry); } if (fc->readdirplus_auto) set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); fuse_change_entry_timeout(dentry, o); dput(dentry); return 0; }
struct file *replayfs_open_filp_by_id(struct vfsmount *vfs, struct replayfs_unique_id *id, loff_t version) { struct inode *req_inode; mm_segment_t old_fs; struct inode *inode; struct dentry *dentry; struct file *new_filp; struct dentry *new_dentry; struct qstr new_str; char *name; old_fs = get_fs(); set_fs(KERNEL_DS); debugk("%s %d: arguments ({%lld, %lld}, %lld)\n", __func__, __LINE__, id->log_num, id->sys_num, version); set_fs(old_fs); /* Get the root's inode */ inode = vfs->mnt_sb->s_root->d_inode; dentry = vfs->mnt_sb->s_root; /* Open the new file */ /* Instantiate a dentry for this file */ /* * NOTE: This dentry will only be used for this file, all other * accesses will point to the current version... */ /* Need to fudge the dentry creation... give it a fake name */ /* Make a new qstr to hold the fudged name */ /* Put name on the heap, so it doesn't overflow the stack... */ debugk("%s %d: About to call kmalloc\n", __func__, __LINE__); /*name = kmalloc(PAGE_SIZE, GFP_NOFS);*/ name = kmem_cache_alloc(replayfs_page_cache, GFP_NOFS); debugk("%s %d: kmalloc done\n", __func__, __LINE__); /* Now, append our prefix to the current name */ name[0] = '\0'; sprintf(name, "__%016llX_%016llX_%016llX", (unsigned long long)version, (unsigned long long)id->log_num, (unsigned long long)id->sys_num); new_str.len = strlen(name); new_str.name = name; new_str.hash = full_name_hash(new_str.name, new_str.len); debugk("%s %d: new name is %s\n", __func__, __LINE__, new_str.name); /* Give that name to the dentry */ /* See if the entry exists in the dentry cache */ new_dentry = d_lookup(dentry, &new_str); debugk("%s %d: new dentry is %p\n", __func__, __LINE__, new_dentry); if (new_dentry == NULL) { debugk("%s %d: about to call d_alloc\n", __func__, __LINE__); new_dentry = d_alloc(dentry, &new_str); debugk("%s %d: dalloc'd new dentry %p\n", __func__, __LINE__, new_dentry); if (new_dentry == NULL) { /*kfree(name);*/ kmem_cache_free(replayfs_page_cache, name); return ERR_PTR(-ENOMEM); } /* Now get the inode of this file */ debugk("%s %d: about to call replayfs_iget\n", __func__, __LINE__); req_inode = replayfs_iget(inode->i_sb, id, version); debugk("%s %d: done calling replayfs_iget\n", __func__, __LINE__); if (req_inode == NULL) { d_delete(new_dentry); new_filp = ERR_PTR(-ENOMEM); goto out; } inode_inc_link_count(req_inode); inode_inc_link_count(inode); debugk("%s %d: about to call d_add\n", __func__, __LINE__); d_add(new_dentry, req_inode); debugk("%s %d: added new dentry %p\n", __func__, __LINE__, new_dentry); } else { req_inode = new_dentry->d_inode; } /* We can free the memory used for naming now */ /*kfree(name);*/ kmem_cache_free(replayfs_page_cache, name); /* Get our new file pointer */ debugk("%s %d: calling dentry_open\n", __func__, __LINE__); new_filp = dentry_open(new_dentry, mntget(vfs), O_RDONLY); debugk("%s %d: called dentry_open, new_filp is %p\n", __func__, __LINE__, new_filp); if (IS_ERR(new_filp)) { d_delete(new_dentry); goto out; } else { } /* Call the actual fs open operation */ replayfs_file_open(req_inode, new_filp); out: /* Return our file descriptor */ return new_filp; }
/* * Get root dentry from superblock according to prefix path mount option. * Return dentry with refcount + 1 on success and NULL otherwise. */ static struct dentry * cifs_get_root(struct smb_vol *vol, struct super_block *sb) { int xid, rc; struct inode *inode; struct qstr name; struct dentry *dparent = NULL, *dchild = NULL, *alias; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); unsigned int i, full_len, len; char *full_path = NULL, *pstart; char sep; full_path = cifs_build_path_to_root(vol, cifs_sb, cifs_sb_master_tcon(cifs_sb)); if (full_path == NULL) return NULL; cFYI(1, "Get root dentry for %s", full_path); xid = GetXid(); sep = CIFS_DIR_SEP(cifs_sb); dparent = dget(sb->s_root); full_len = strlen(full_path); full_path[full_len] = sep; pstart = full_path + 1; for (i = 1, len = 0; i <= full_len; i++) { if (full_path[i] != sep || !len) { len++; continue; } full_path[i] = 0; cFYI(1, "get dentry for %s", pstart); name.name = pstart; name.len = len; name.hash = full_name_hash(pstart, len); dchild = d_lookup(dparent, &name); if (dchild == NULL) { cFYI(1, "not exists"); dchild = d_alloc(dparent, &name); if (dchild == NULL) { dput(dparent); dparent = NULL; goto out; } } cFYI(1, "get inode"); if (dchild->d_inode == NULL) { cFYI(1, "not exists"); inode = NULL; if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, xid, NULL); if (rc) { dput(dchild); dput(dparent); dparent = NULL; goto out; } alias = d_materialise_unique(dchild, inode); if (alias != NULL) { dput(dchild); if (IS_ERR(alias)) { dput(dparent); dparent = NULL; goto out; } dchild = alias; } } cFYI(1, "parent %p, child %p", dparent, dchild); dput(dparent); dparent = dchild; len = 0; pstart = full_path + i + 1; full_path[i] = sep; } out: _FreeXid(xid); kfree(full_path); return dparent; }
static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, u64 attr_version) { int err; struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; struct dentry *parent = file->f_path.dentry; struct qstr name = { .len = dirent->namelen, .name = dirent->name }; struct dentry *dentry; struct dentry *alias; struct inode *dir = parent->d_inode; struct fuse_conn *fc; struct inode *inode; if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing. */ return 0; } if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent? */ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0; } if (invalid_nodeid(o->nodeid)) return -EIO; if (!fuse_valid_type(o->attr.mode)) return -EIO; fc = get_fuse_conn(dir); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); if (dentry) { inode = dentry->d_inode; if (!inode) { d_drop(dentry); } else if (get_node_id(inode) != o->nodeid || ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { err = d_invalidate(dentry); if (err) goto out; } else if (is_bad_inode(inode)) { err = -EIO; goto out; } else { struct fuse_inode *fi; fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), attr_version); /* * The other branch to 'found' comes via fuse_iget() * which bumps nlookup inside */ goto found; } dput(dentry); } dentry = d_alloc(parent, &name); err = -ENOMEM; if (!dentry) goto out; dentry->d_op = &fuse_dentry_operations; inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, entry_attr_timeout(o), attr_version); if (!inode) goto out; alias = fuse_materialise_dentry(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; if (alias) { dput(dentry); dentry = alias; } found: fuse_change_entry_timeout(dentry, o); err = 0; out: dput(dentry); return err; } static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, void *dstbuf, filldir_t filldir, u64 attr_version) { struct fuse_direntplus *direntplus; struct fuse_dirent *dirent; size_t reclen; int over = 0; int ret; while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) { direntplus = (struct fuse_direntplus *) buf; dirent = &direntplus->dirent; reclen = FUSE_DIRENTPLUS_SIZE(direntplus); if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; if (memchr(dirent->name, '/', dirent->namelen) != NULL) return -EIO; if (!over) { /* We fill entries into dstbuf only as much as it can hold. But we still continue iterating over remaining entries to link them. If not, we need to send a FORGET for each of those which we did not link. */ over = filldir(dstbuf, dirent->name, dirent->namelen, file->f_pos, dirent->ino, dirent->type); file->f_pos = dirent->off; } buf += reclen; nbytes -= reclen; ret = fuse_direntplus_link(file, direntplus, attr_version); if (ret) fuse_force_forget(file, direntplus->entry_out.nodeid); } return 0; } static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) { int plus, err; size_t nbytes; struct page *page; struct inode *inode = file->f_path.dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; u64 attr_version = 0; if (is_bad_inode(inode)) return -EIO; req = fuse_get_req(fc, 1); if (IS_ERR(req)) return PTR_ERR(req); page = alloc_page(GFP_KERNEL); if (!page) { fuse_put_request(fc, req); return -ENOMEM; } plus = fuse_use_readdirplus(inode, file); req->out.argpages = 1; req->num_pages = 1; req->pages[0] = page; req->page_descs[0].length = PAGE_SIZE; if (plus) { attr_version = fuse_get_attr_version(fc); fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIRPLUS); } else { fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR); } fuse_request_send(fc, req); nbytes = req->out.args[0].size; err = req->out.h.error; fuse_put_request(fc, req); if (!err) { if (plus) { err = parse_dirplusfile(page_address(page), nbytes, file, dstbuf, filldir, attr_version); } else { err = parse_dirfile(page_address(page), nbytes, file, dstbuf, filldir); } } __free_page(page); fuse_invalidate_attr(inode); /* atime changed */ return err; }
long replayfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode; struct dentry *dentry; dentry = filp->f_dentry; inode = filp->f_dentry->d_inode; debugk("In %s!\n", __func__); debugk("%s %d: Cmd is %u, REPLAYFS_IOCTL_SET_VERSION is %u REPLAYFS_IOCTL_GET_VERSION is %u\n", __func__, __LINE__, cmd, REPLAYFS_IOCTL_SET_VERSION, REPLAYFS_IOCTL_GET_VERSION); switch (cmd) { /* Opens a new fd to point to the requested version of the file */ case REPLAYFS_IOCTL_SET_VERSION: do { unsigned long long req_version; long new_fd; struct inode *req_inode; debugk("%s %d: cmd is SET_VERSION\n", __func__, __LINE__); if (copy_from_user(&req_version, (unsigned long long __user *)arg, sizeof(unsigned long long))) { return -EFAULT; } /* They requested a version > current... we can't do that! */ if (req_version >= replayfs_file_log_size(REPLAYFS_I(inode)->file_log)) { return -EINVAL; } /* Open the new file */ new_fd = get_unused_fd(); if (new_fd >= 0) { struct file *new_filp; struct dentry *new_dentry; struct qstr new_str; char *name; int oldname_len; const char *oldname; oldname = dentry->d_name.name; oldname_len = dentry->d_name.len; /* Instantiate a dentry for this file */ /* * NOTE: This dentry will only be used for this file, all other * accesses will point to the current version... */ /* Need to fudge the dentry creation... give it a fake name */ /* Make a new qstr to hold the fudged name */ /* Put name on the heap, so it doesn't overflow the stack... */ name = kmalloc(PAGE_SIZE, GFP_NOFS); /* See if the current dentry is already a replayfs versioned file */ if (dentry->d_name.len > 17) { if (!memcmp(dentry->d_name.name, "__replayfsvers", 14)) { /* * The name is already replayfs versioned... remove the version * info */ int uscore_cnt; int offs; for (uscore_cnt = 0, offs=14; uscore_cnt < 2; offs++) { BUG_ON(offs == dentry->d_name.len); if (oldname[14] == '_') { uscore_cnt++; } } oldname_len -= offs; oldname += offs; } } /* Now, append our prefix to the current name */ name[0] = '\0'; sprintf(name, "__replayfsvers%llu__%*.s", req_version, oldname_len, oldname); new_str.len = strlen(name); new_str.name = name; new_str.hash = full_name_hash(new_str.name, new_str.len); debugk("%s %d: new name is %s", __func__, __LINE__, new_str.name); /* Name formua is: __replayfsvers%version%__%filename% */ /* Give that name to the dentry */ /* See if the entry exists in the dentry cache */ new_dentry = d_lookup(dentry->d_parent, &new_str); if (new_dentry == NULL) { new_dentry = d_alloc(dentry->d_parent, &new_str); if (new_dentry == NULL) { put_unused_fd(new_fd); kfree(name); return -ENOMEM; } /* Get the inode of this file at that version */ /* Now get the inode of this file */ req_inode = replayfs_iget(inode->i_sb, &REPLAYFS_I(inode)->id, req_version); if (req_inode == NULL) { new_fd = -ENOMEM; } req_inode->i_fop = inode->i_fop; req_inode->i_op = inode->i_op; req_inode->i_mapping->a_ops = inode->i_mapping->a_ops; inode_inc_link_count(req_inode); inode_inc_link_count(inode); debugk("%s %d: req_inode is %p\n", __func__, __LINE__, req_inode); d_add(new_dentry, req_inode); } else { req_inode = new_dentry->d_inode; } /* We can free the memory used for naming now */ kfree(name); /* Get our new file pointer */ new_filp = dentry_open(new_dentry, mntget(filp->f_vfsmnt), O_RDONLY); if (IS_ERR(new_filp)) { put_unused_fd(new_fd); new_fd = PTR_ERR(new_filp); } else { /* Install that file pointer to a fd, and return */ fd_install(new_fd, new_filp); /* Call the actual fs open operation */ replayfs_file_open(req_inode, new_filp); } } /* Return our file descriptor */ return new_fd; } while (0); case REPLAYFS_IOCTL_GET_SUBDIR: do { char fname[0x80]; struct page *dir_page; loff_t dir_pos; struct replayfs_dir *dir; struct dentry entry; int new_fd; /* Figure out if the file exists */ /* Get file name within directory */ /* Scan backwards for '/' or begin of path */ entry.d_name.len = strncpy_from_user(fname, (const char *)arg, 0x80); entry.d_name.name = fname; if (entry.d_name.len < 0) { /* Shouldn't happen, we should catch error cases and break early */ return -EFAULT; } /* * If the file doesn't exist, and the return is successful, we created the * file... */ dir = replayfs_dir_find_entry(filp->f_dentry->d_inode, &entry, &dir_page, &dir_pos); /* Okay, find the subdir */ if (dir == NULL) { return -ENOENT; } new_fd = get_unused_fd(); if (new_fd >= 0) { struct file *filp; filp = replayfs_open_filp_by_id(vfs_loc, &dir->header.id, 0); if (filp == NULL) { put_unused_fd(new_fd); return -EINVAL; } fd_install(new_fd, filp); replayfs_file_open(filp->f_dentry->d_inode, filp); } /* Return our file descriptor */ return new_fd; } while (0); break; case REPLAYFS_IOCTL_GET_VERSION: if (REPLAYFS_I(inode)->version == REPLAYFS_CURRENT_VERSION) { return replayfs_file_log_size(REPLAYFS_I(inode)->file_log)-1; } else { return REPLAYFS_I(inode)->version; } case REPLAYFS_IOCTL_MAX_VERSION: do { loff_t max_version; max_version = replayfs_file_log_size(REPLAYFS_I(inode)->file_log)-1; if (copy_to_user((unsigned long long __user *)arg, &max_version, sizeof(unsigned long long))) { return -EFAULT; } } while (0); return 0; default: return -ENOTTY; } }
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, enum autofs_notify notify) { struct autofs_wait_queue *wq; struct qstr qstr; char *name; int status, ret, type; /* In catatonic mode, we don't wait for nobody */ if (sbi->catatonic) return -ENOENT; if (!dentry->d_inode) { /* * A wait for a negative dentry is invalid for certain * cases. A direct or offset mount "always" has its mount * point directory created and so the request dentry must * be positive or the map key doesn't exist. The situation * is very similar for indirect mounts except only dentrys * in the root of the autofs file system may be negative. */ if (autofs_type_trigger(sbi->type)) return -ENOENT; else if (!IS_ROOT(dentry->d_parent)) return -ENOENT; } name = kmalloc(NAME_MAX + 1, GFP_KERNEL); if (!name) return -ENOMEM; /* If this is a direct mount request create a dummy name */ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) qstr.len = sprintf(name, "%p", dentry); else { qstr.len = autofs4_getpath(sbi, dentry, &name); if (!qstr.len) { kfree(name); return -ENOENT; } } qstr.name = name; qstr.hash = full_name_hash(name, qstr.len); if (mutex_lock_interruptible(&sbi->wq_mutex)) { kfree(qstr.name); return -EINTR; } ret = validate_request(&wq, sbi, &qstr, dentry, notify); if (ret <= 0) { if (ret == 0) mutex_unlock(&sbi->wq_mutex); kfree(qstr.name); return ret; } if (!wq) { /* Create a new wait queue */ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); if (!wq) { kfree(qstr.name); mutex_unlock(&sbi->wq_mutex); return -ENOMEM; } wq->wait_queue_token = autofs4_next_wait_queue; if (++autofs4_next_wait_queue == 0) autofs4_next_wait_queue = 1; wq->next = sbi->queues; sbi->queues = wq; init_waitqueue_head(&wq->queue); memcpy(&wq->name, &qstr, sizeof(struct qstr)); wq->dev = autofs4_get_dev(sbi); wq->ino = autofs4_get_ino(sbi); wq->uid = current_uid(); wq->gid = current_gid(); wq->pid = current->pid; wq->tgid = current->tgid; wq->status = -EINTR; /* Status return if interrupted */ wq->wait_ctr = 2; mutex_unlock(&sbi->wq_mutex); if (sbi->version < 5) { if (notify == NFY_MOUNT) type = autofs_ptype_missing; else type = autofs_ptype_expire_multi; } else { if (notify == NFY_MOUNT) type = autofs_type_trigger(sbi->type) ? autofs_ptype_missing_direct : autofs_ptype_missing_indirect; else type = autofs_type_trigger(sbi->type) ? autofs_ptype_expire_direct : autofs_ptype_expire_indirect; } DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, notify); /* autofs4_notify_daemon() may block */ autofs4_notify_daemon(sbi, wq, type); } else { wq->wait_ctr++; mutex_unlock(&sbi->wq_mutex); kfree(qstr.name); DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, notify); } /* * wq->name.name is NULL iff the lock is already released * or the mount has been made catatonic. */ if (wq->name.name) { /* Block all but "shutdown" signals while waiting */ sigset_t oldset; unsigned long irqflags; spin_lock_irqsave(¤t->sighand->siglock, irqflags); oldset = current->blocked; siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); wait_event_interruptible(wq->queue, wq->name.name == NULL); spin_lock_irqsave(¤t->sighand->siglock, irqflags); current->blocked = oldset; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); } else { DPRINTK("skipped sleeping"); } status = wq->status; /* * For direct and offset mounts we need to track the requester's * uid and gid in the dentry info struct. This is so it can be * supplied, on request, by the misc device ioctl interface. * This is needed during daemon resatart when reconnecting * to existing, active, autofs mounts. The uid and gid (and * related string values) may be used for macro substitution * in autofs mount maps. */ if (!status) { struct autofs_info *ino; struct dentry *de = NULL; /* direct mount or browsable map */ ino = autofs4_dentry_ino(dentry); if (!ino) { /* If not lookup actual dentry used */ de = d_lookup(dentry->d_parent, &dentry->d_name); if (de) ino = autofs4_dentry_ino(de); } /* Set mount requester */ if (ino) { spin_lock(&sbi->fs_lock); ino->uid = wq->uid; ino->gid = wq->gid; spin_unlock(&sbi->fs_lock); } if (de) dput(de); } /* Are we the last process to need status? */ mutex_lock(&sbi->wq_mutex); if (!--wq->wait_ctr) kfree(wq); mutex_unlock(&sbi->wq_mutex); return status; }
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; inode_lock(parent); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; name->hash = full_name_hash(dir, name->name, name->len); entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_dir_changed(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (d_is_dir(entry)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } d_inode(entry)->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(d_inode(entry)); err = 0; badentry: inode_unlock(d_inode(entry)); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: inode_unlock(parent); iput(parent); return err; }
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) { struct rpc_message msg = { .rpc_argp = &data->args, .rpc_resp = &data->res, .rpc_cred = data->cred, }; struct rpc_task_setup task_setup_data = { .rpc_message = &msg, .callback_ops = &nfs_unlink_ops, .callback_data = data, .workqueue = nfsiod_workqueue, .flags = RPC_TASK_ASYNC, }; struct rpc_task *task; struct dentry *alias; alias = d_lookup(parent, &data->args.name); if (alias != NULL) { int ret; void *devname_garbage = NULL; /* * Hey, we raced with lookup... See if we need to transfer * the sillyrename information to the aliased dentry. */ nfs_free_dname(data); ret = nfs_copy_dname(alias, data); spin_lock(&alias->d_lock); if (ret == 0 && alias->d_inode != NULL && !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { devname_garbage = alias->d_fsdata; alias->d_fsdata = data; alias->d_flags |= DCACHE_NFSFS_RENAMED; ret = 1; } else ret = 0; spin_unlock(&alias->d_lock); nfs_dec_sillycount(dir); dput(alias); /* * If we'd displaced old cached devname, free it. At that * point dentry is definitely not a root, so we won't need * that anymore. */ kfree(devname_garbage); return ret; } data->dir = igrab(dir); if (!data->dir) { nfs_dec_sillycount(dir); return 0; } nfs_sb_active(dir->i_sb); data->args.fh = NFS_FH(dir); nfs_fattr_init(data->res.dir_attr); NFS_PROTO(dir)->unlink_setup(&msg, dir); task_setup_data.rpc_client = NFS_CLIENT(dir); task = rpc_run_task(&task_setup_data); if (!IS_ERR(task)) rpc_put_task_async(task); return 1; } static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) { struct dentry *parent; struct inode *dir; int ret = 0; parent = dget_parent(dentry); if (parent == NULL) goto out_free; dir = parent->d_inode; /* Non-exclusive lock protects against concurrent lookup() calls */ spin_lock(&dir->i_lock); if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) { /* Deferred delete */ hlist_add_head(&data->list, &NFS_I(dir)->silly_list); spin_unlock(&dir->i_lock); ret = 1; goto out_dput; } spin_unlock(&dir->i_lock); ret = nfs_do_call_unlink(parent, dir, data); out_dput: dput(parent); out_free: return ret; } void nfs_block_sillyrename(struct dentry *dentry) { struct nfs_inode *nfsi = NFS_I(dentry->d_inode); wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1); } void nfs_unblock_sillyrename(struct dentry *dentry) { struct inode *dir = dentry->d_inode; struct nfs_inode *nfsi = NFS_I(dir); struct nfs_unlinkdata *data; atomic_inc(&nfsi->silly_count); spin_lock(&dir->i_lock); while (!hlist_empty(&nfsi->silly_list)) { if (!atomic_inc_not_zero(&nfsi->silly_count)) break; data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list); hlist_del(&data->list); spin_unlock(&dir->i_lock); if (nfs_do_call_unlink(dentry, dir, data) == 0) nfs_free_unlinkdata(data); spin_lock(&dir->i_lock); } spin_unlock(&dir->i_lock); } /** * nfs_async_unlink - asynchronous unlinking of a file * @dir: parent directory of dentry * @dentry: dentry to unlink */ static int nfs_async_unlink(struct inode *dir, struct dentry *dentry) { struct nfs_unlinkdata *data; int status = -ENOMEM; void *devname_garbage = NULL; data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) goto out; data->cred = rpc_lookup_cred(); if (IS_ERR(data->cred)) { status = PTR_ERR(data->cred); goto out_free; } data->res.dir_attr = &data->dir_attr; status = -EBUSY; spin_lock(&dentry->d_lock); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out_unlock; dentry->d_flags |= DCACHE_NFSFS_RENAMED; devname_garbage = dentry->d_fsdata; dentry->d_fsdata = data; spin_unlock(&dentry->d_lock); /* * If we'd displaced old cached devname, free it. At that * point dentry is definitely not a root, so we won't need * that anymore. */ if (devname_garbage) kfree(devname_garbage); return 0; out_unlock: spin_unlock(&dentry->d_lock); put_rpccred(data->cred); out_free: kfree(data); out: return status; } /** * nfs_complete_unlink - Initialize completion of the sillydelete * @dentry: dentry to delete * @inode: inode * * Since we're most likely to be called by dentry_iput(), we * only use the dentry to find the sillydelete. We then copy the name * into the qstr. */ void nfs_complete_unlink(struct dentry *dentry, struct inode *inode) { struct nfs_unlinkdata *data = NULL; spin_lock(&dentry->d_lock); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; data = dentry->d_fsdata; dentry->d_fsdata = NULL; } spin_unlock(&dentry->d_lock); if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) nfs_free_unlinkdata(data); } /* Cancel a queued async unlink. Called when a sillyrename run fails. */ static void nfs_cancel_async_unlink(struct dentry *dentry) { spin_lock(&dentry->d_lock); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { struct nfs_unlinkdata *data = dentry->d_fsdata; dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; dentry->d_fsdata = NULL; spin_unlock(&dentry->d_lock); nfs_free_unlinkdata(data); return; } spin_unlock(&dentry->d_lock); }
/** * 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); } }
static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, u64 attr_version) { int err; struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; struct dentry *parent = file->f_path.dentry; struct qstr name = QSTR_INIT(dirent->name, dirent->namelen); struct dentry *dentry; struct dentry *alias; struct inode *dir = parent->d_inode; struct fuse_conn *fc; struct inode *inode; if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing. */ return 0; } if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent? */ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0; } if (invalid_nodeid(o->nodeid)) return -EIO; if (!fuse_valid_type(o->attr.mode)) return -EIO; fc = get_fuse_conn(dir); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); if (dentry) { inode = dentry->d_inode; if (!inode) { d_drop(dentry); } else if (get_node_id(inode) != o->nodeid || ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { err = d_invalidate(dentry); if (err) goto out; } else if (is_bad_inode(inode)) { err = -EIO; goto out; } else { struct fuse_inode *fi; fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), attr_version); /* * The other branch to 'found' comes via fuse_iget() * which bumps nlookup inside */ goto found; } dput(dentry); } dentry = d_alloc(parent, &name); err = -ENOMEM; if (!dentry) goto out; inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, entry_attr_timeout(o), attr_version); if (!inode) goto out; alias = fuse_materialise_dentry(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; if (alias) { dput(dentry); dentry = alias; } found: fuse_change_entry_timeout(dentry, o); err = 0; out: dput(dentry); return err; }
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; mutex_lock(&parent->i_mutex); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_invalidate_attr(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && entry->d_inode) { mutex_lock(&entry->d_inode->i_mutex); if (get_node_id(entry->d_inode) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (S_ISDIR(entry->d_inode->i_mode)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } entry->d_inode->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(entry->d_inode); err = 0; badentry: mutex_unlock(&entry->d_inode->i_mutex); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: mutex_unlock(&parent->i_mutex); iput(parent); return err; }
/* * Main driver function for sdcardfskk's lookup. * * Returns: NULL (ok), ERR_PTR if an error occurred. * Fills in lower_parent_path with <dentry,mnt> on success. */ static struct dentry *__sdcardfskk_lookup(struct dentry *dentry, struct nameidata *nd, struct path *lower_parent_path) { int err = 0; struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; const char *name; struct nameidata lower_nd; struct path lower_path; struct qstr this; struct sdcardfskk_sb_info *sbi; sbi = SDCARDFSKK_SB(dentry->d_sb); /* must initialize dentry operations */ d_set_d_op(dentry, &sdcardfskk_ci_dops); if (IS_ROOT(dentry)) goto out; name = dentry->d_name.name; /* now start the actual lookup procedure */ lower_dir_dentry = lower_parent_path->dentry; lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ if (sbi->options.lower_fs == LOWER_FS_EXT4) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, LOOKUP_CASE_INSENSITIVE, &lower_nd); } else if (sbi->options.lower_fs == LOWER_FS_FAT) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_nd); } /* no error: handle positive dentries */ if (!err) { /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with * the inode of the graft path */ if(need_graft_path_kitkat(dentry)) { /* setup_obb_dentry_kitkat() * The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path * returns: -ERRNO if error (0: no error) */ err = setup_obb_dentry_kitkat(dentry, &lower_nd.path); if(err) { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as * a lookup fail. */ printk(KERN_INFO "sdcardfskk: base obbpath is not available\n"); sdcardfskk_put_reset_orig_path(dentry); goto out; } } sdcardfskk_set_lower_path(dentry, &lower_nd.path); err = sdcardfskk_interpose(dentry, dentry->d_sb, &lower_nd.path); if (err) /* path_put underlying path on error */ sdcardfskk_put_reset_lower_path(dentry); goto out; } /* * We don't consider ENOENT an error, and we want to return a * negative dentry. */ if (err && err != -ENOENT) goto out; /* instatiate a new negative dentry */ this.name = name; this.len = strlen(name); this.hash = full_name_hash(this.name, this.len); lower_dentry = d_lookup(lower_dir_dentry, &this); if (lower_dentry) goto setup_lower; lower_dentry = d_alloc(lower_dir_dentry, &this); if (!lower_dentry) { err = -ENOMEM; goto out; } d_add(lower_dentry, NULL); /* instantiate and hash */ setup_lower: lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); sdcardfskk_set_lower_path(dentry, &lower_path); /* * If the intent is to create a file, then don't return an error, so * the VFS will continue the process of making this negative dentry * into a positive one. */ if (nd) { if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) err = 0; } else err = 0; out: return ERR_PTR(err); }