/* * 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); }
/* support routines */ static int coda_venus_readdir(struct file *coda_file, void *buf, filldir_t filldir) { int result = 0; /* # of entries returned */ struct coda_file_info *cfi; struct coda_inode_info *cii; struct file *host_file; struct dentry *de; struct venus_dirent *vdir; unsigned long vdir_size = (unsigned long)(&((struct venus_dirent *)0)->d_name); unsigned int type; struct qstr name; ino_t ino; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; de = coda_file->f_path.dentry; cii = ITOC(de->d_inode); vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; if (coda_file->f_pos == 0) { ret = filldir(buf, ".", 1, 0, de->d_inode->i_ino, DT_DIR); if (ret < 0) goto out; result++; coda_file->f_pos++; } if (coda_file->f_pos == 1) { ret = filldir(buf, "..", 2, 1, de->d_parent->d_inode->i_ino, DT_DIR); if (ret < 0) goto out; result++; coda_file->f_pos++; } while (1) { /* read entries from the directory file */ ret = kernel_read(host_file, coda_file->f_pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { printk(KERN_ERR "coda readdir: read dir %s failed %d\n", coda_f2s(&cii->c_fid), ret); break; } if (ret == 0) break; /* end of directory file reached */ /* catch truncated reads */ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { printk(KERN_ERR "coda readdir: short read on %s\n", coda_f2s(&cii->c_fid)); ret = -EBADF; break; } /* validate whether the directory file actually makes sense */ if (vdir->d_reclen < vdir_size + vdir->d_namlen) { printk(KERN_ERR "coda readdir: invalid dir %s\n", coda_f2s(&cii->c_fid)); ret = -EBADF; break; } name.len = vdir->d_namlen; name.name = vdir->d_name; /* Make sure we skip '.' and '..', we already got those */ if (name.name[0] == '.' && (name.len == 1 || (vdir->d_name[1] == '.' && name.len == 2))) vdir->d_fileno = name.len = 0; /* skip null entries */ if (vdir->d_fileno && name.len) { /* try to look up this entry in the dcache, that way * userspace doesn't have to worry about breaking * getcwd by having mismatched inode numbers for * internal volume mountpoints. */ ino = find_inode_number(de, &name); if (!ino) ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); ret = filldir(buf, name.name, name.len, coda_file->f_pos, ino, type); /* failure means no space for filling in this round */ if (ret < 0) break; result++; } /* we'll always have progress because d_reclen is unsigned and * we've already established it is non-zero. */ coda_file->f_pos += vdir->d_reclen; } out: kfree(vdir); return result ? result : ret; }
/* support routines */ static int coda_venus_readdir(struct file *filp, filldir_t filldir, void *dirent, struct dentry *dir) { int result = 0; /* # of entries returned */ struct venus_dirent *vdir; unsigned long vdir_size = (unsigned long)(&((struct venus_dirent *)0)->d_name); unsigned int type; struct qstr name; ino_t ino; int ret, i; vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; i = filp->f_pos; switch(i) { case 0: ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR); if (ret < 0) break; result++; filp->f_pos++; /* fallthrough */ case 1: ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR); if (ret < 0) break; result++; filp->f_pos++; /* fallthrough */ default: while (1) { /* read entries from the directory file */ ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { printk("coda_venus_readdir: read dir failed %d\n", ret); break; } if (ret == 0) break; /* end of directory file reached */ /* catch truncated reads */ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { printk("coda_venus_readdir: short read: %ld\n", filp->f_path.dentry->d_inode->i_ino); ret = -EBADF; break; } /* validate whether the directory file actually makes sense */ if (vdir->d_reclen < vdir_size + vdir->d_namlen) { printk("coda_venus_readdir: Invalid dir: %ld\n", filp->f_path.dentry->d_inode->i_ino); ret = -EBADF; break; } name.len = vdir->d_namlen; name.name = vdir->d_name; /* Make sure we skip '.' and '..', we already got those */ if (name.name[0] == '.' && (name.len == 1 || (vdir->d_name[1] == '.' && name.len == 2))) vdir->d_fileno = name.len = 0; /* skip null entries */ if (vdir->d_fileno && name.len) { /* try to look up this entry in the dcache, that way * userspace doesn't have to worry about breaking * getcwd by having mismatched inode numbers for * internal volume mountpoints. */ ino = find_inode_number(dir, &name); if (!ino) ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); ret = filldir(dirent, name.name, name.len, filp->f_pos, ino, type); /* failure means no space for filling in this round */ if (ret < 0) break; result++; } /* we'll always have progress because d_reclen is unsigned and * we've already established it is non-zero. */ filp->f_pos += vdir->d_reclen; } } kfree(vdir); return result ? result : ret; }
static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_inode; struct cache_head *cachep = NULL; int result; VERBOSE("reading %s/%s, f_pos=%d\n", DENTRY_PATH(dentry), (int) filp->f_pos); result = 0; switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) goto out; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino) < 0) goto out; filp->f_pos = 2; } /* * Make sure our inode is up-to-date. */ result = smb_revalidate_inode(dentry); if (result) goto out; /* * Get the cache pointer ... */ result = -EIO; cachep = smb_get_dircache(dentry); if (!cachep) goto out; /* * Make sure the cache is up-to-date. * * To detect changes on the server we refill on each "new" access. * * Directory mtime would be nice to use for finding changes, * unfortunately some servers (NT4) doesn't update on local changes. */ if (!cachep->valid || filp->f_pos == 2) { result = smb_refill_dircache(cachep, dentry); if (result) goto out; } result = 0; while (1) { struct cache_dirent this_dirent, *entry = &this_dirent; if (!smb_find_in_cache(cachep, filp->f_pos, entry)) break; /* * Check whether to look up the inode number. */ if (!entry->ino) { struct qstr qname; /* N.B. Make cache_dirent name a qstr! */ qname.name = entry->name; qname.len = entry->len; entry->ino = find_inode_number(dentry, &qname); if (!entry->ino) entry->ino = smb_invent_inos(1); } if (filldir(dirent, entry->name, entry->len, filp->f_pos, entry->ino) < 0) break; filp->f_pos += 1; } /* * Release the dircache. */ out: if (cachep) { smb_free_dircache(cachep); } return result; }
static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_inode; struct cache_head *cachep; int result; #ifdef SMBFS_DEBUG_VERBOSE printk("smb_readdir: reading %s/%s, f_pos=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos); #endif /* * Make sure our inode is up-to-date. */ result = smb_revalidate_inode(dentry); if (result) goto out; /* * Get the cache pointer ... */ result = -EIO; cachep = smb_get_dircache(dentry); if (!cachep) goto out; /* * Make sure the cache is up-to-date. */ if (!cachep->valid) { result = smb_refill_dircache(cachep, dentry); if (result) goto out_free; } result = 0; switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) goto out_free; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino) < 0) goto out_free; filp->f_pos = 2; } while (1) { struct cache_dirent this_dirent, *entry = &this_dirent; if (!smb_find_in_cache(cachep, filp->f_pos, entry)) break; /* * Check whether to look up the inode number. */ if (!entry->ino) { struct qstr qname; /* N.B. Make cache_dirent name a qstr! */ qname.name = entry->name; qname.len = entry->len; entry->ino = find_inode_number(dentry, &qname); if (!entry->ino) entry->ino = smb_invent_inos(1); } if (filldir(dirent, entry->name, entry->len, filp->f_pos, entry->ino) < 0) break; filp->f_pos += 1; } /* * Release the dircache. */ out_free: smb_free_dircache(cachep); out: return result; }
static int coda_venus_readdir(struct file *coda_file, void *buf, filldir_t filldir) { int result = 0; struct coda_file_info *cfi; struct coda_inode_info *cii; struct file *host_file; struct dentry *de; struct venus_dirent *vdir; unsigned long vdir_size = offsetof(struct venus_dirent, d_name); unsigned int type; struct qstr name; ino_t ino; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; de = coda_file->f_path.dentry; cii = ITOC(de->d_inode); vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; if (coda_file->f_pos == 0) { ret = filldir(buf, ".", 1, 0, de->d_inode->i_ino, DT_DIR); if (ret < 0) goto out; result++; coda_file->f_pos++; } if (coda_file->f_pos == 1) { ret = filldir(buf, "..", 2, 1, parent_ino(de), DT_DIR); if (ret < 0) goto out; result++; coda_file->f_pos++; } while (1) { ret = kernel_read(host_file, coda_file->f_pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { printk(KERN_ERR "coda readdir: read dir %s failed %d\n", coda_f2s(&cii->c_fid), ret); break; } if (ret == 0) break; if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { printk(KERN_ERR "coda readdir: short read on %s\n", coda_f2s(&cii->c_fid)); ret = -EBADF; break; } if (vdir->d_reclen < vdir_size + vdir->d_namlen) { printk(KERN_ERR "coda readdir: invalid dir %s\n", coda_f2s(&cii->c_fid)); ret = -EBADF; break; } name.len = vdir->d_namlen; name.name = vdir->d_name; if (name.name[0] == '.' && (name.len == 1 || (vdir->d_name[1] == '.' && name.len == 2))) vdir->d_fileno = name.len = 0; if (vdir->d_fileno && name.len) { ino = find_inode_number(de, &name); if (!ino) ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); ret = filldir(buf, name.name, name.len, coda_file->f_pos, ino, type); if (ret < 0) break; result++; } coda_file->f_pos += vdir->d_reclen; } out: kfree(vdir); return result ? result : ret; }