void coda_replace_fid(struct inode *inode, struct CodaFid *oldfid, struct CodaFid *newfid) { struct coda_inode_info *cii; unsigned long hash = coda_f2i(newfid); cii = ITOC(inode); BUG_ON(!coda_fideq(&cii->c_fid, oldfid)); /* replace fid and rehash inode */ /* XXX we probably need to hold some lock here! */ remove_inode_hash(inode); cii->c_fid = *newfid; inode->i_ino = hash; __insert_inode_hash(inode, hash); }
void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, struct ViceFid *newfid) { struct coda_inode_info *cii; cii = ITOC(inode); if (!coda_fideq(&cii->c_fid, oldfid)) BUG(); /* replace fid and rehash inode */ /* XXX we probably need to hold some lock here! */ remove_inode_hash(inode); cii->c_fid = *newfid; inode->i_ino = coda_f2i(newfid); insert_inode_hash(inode); }
int coda_release(struct inode *coda_inode, struct file *coda_file) { unsigned short flags = (coda_file->f_flags) & (~O_EXCL); unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_file_info *cfi; struct coda_inode_info *cii; struct inode *host_inode; int err = 0; lock_kernel(); coda_vfs_stat.release++; if (!use_coda_close) { err = venus_release(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags); if (err == -EOPNOTSUPP) { use_coda_close = 1; err = 0; } } cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); if (use_coda_close) err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, coda_file->f_uid); host_inode = cfi->cfi_container->f_dentry->d_inode; cii = ITOC(coda_inode); /* did we mmap this file? */ if (coda_inode->i_mapping == &host_inode->i_data) { cii->c_mapcount -= cfi->cfi_mapcount; if (!cii->c_mapcount) coda_inode->i_mapping = &coda_inode->i_data; } fput(cfi->cfi_container); kfree(coda_file->private_data); coda_file->private_data = NULL; unlock_kernel(); return err; }
/* all filling in of inodes postponed until lookup */ static void coda_read_inode(struct inode *inode) { struct coda_sb_info *sbi = coda_sbp(inode->i_sb); struct coda_inode_info *cii; ENTRY; if (!sbi) BUG(); cii = ITOC(inode); if (cii->c_magic == CODA_CNODE_MAGIC) { printk("coda_read_inode: initialized inode"); return; } memset(cii, 0, sizeof(struct coda_inode_info)); list_add(&cii->c_cilist, &sbi->sbi_cihead); cii->c_magic = CODA_CNODE_MAGIC; }
/* * This is called when we want to check if the inode has * changed on the server. Coda makes this easy since the * cache manager Venus issues a downcall to the kernel when this * happens */ int coda_revalidate_inode(struct dentry *dentry) { struct coda_vattr attr; int error; int old_mode; ino_t old_ino; struct inode *inode = dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); if (!cii->c_flags) return 0; if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) { error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); if (error) return -EIO; /* this inode may be lost if: - it's ino changed - type changes must be permitted for repair and missing mount points. */ old_mode = inode->i_mode; old_ino = inode->i_ino; coda_vattr_to_iattr(inode, &attr); if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) { printk("Coda: inode %ld, fid %s changed type!\n", inode->i_ino, coda_f2s(&(cii->c_fid))); } /* the following can happen when a local fid is replaced with a global one, here we lose and declare the inode bad */ if (inode->i_ino != old_ino) return -EIO; coda_flag_inode_children(inode, C_FLUSH); spin_lock(&cii->c_lock); cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); spin_unlock(&cii->c_lock); } return 0; }
static int coda_pioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long user_data) { struct nameidata nd; int error; struct PioctlData data; struct inode *target_inode = NULL; struct coda_inode_info *cnp; /* get the Pioctl data arguments from user space */ if (copy_from_user(&data, (void __user *)user_data, sizeof(data))) { return -EINVAL; } /* * Look up the pathname. Note that the pathname is in * user memory, and namei takes care of this */ if ( data.follow ) { error = user_path_walk(data.path, &nd); } else { error = user_path_walk_link(data.path, &nd); } if ( error ) { return error; } else { target_inode = nd.dentry->d_inode; } /* return if it is not a Coda inode */ if ( target_inode->i_sb != inode->i_sb ) { path_release(&nd); return -EINVAL; } /* now proceed to make the upcall */ cnp = ITOC(target_inode); error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); path_release(&nd); return error; }
int coda_release(struct inode *i, struct file *f) { unsigned short flags = (f->f_flags) & (~O_EXCL); unsigned short cflags = coda_flags_to_cflags(flags); struct coda_inode_info *cii; struct file *cfile; int err = 0; lock_kernel(); coda_vfs_stat.release++; if (!use_coda_close) { err = venus_release(i->i_sb, coda_i2f(i), cflags); if (err == -EOPNOTSUPP) { use_coda_close = 1; err = 0; } } if (use_coda_close) err = venus_close(i->i_sb, coda_i2f(i), cflags, (struct coda_cred *)f->private_data); cii = ITOC(i); cfile = cii->c_container; if (!cfile) BUG(); if (--cii->c_contcount) { unlock_kernel(); return err; } i->i_mapping = &i->i_data; fput(cfile); cii->c_container = NULL; if (f->private_data) { kfree(f->private_data); f->private_data = NULL; } unlock_kernel(); return err; }
int coda_flush(struct file *file) { unsigned short flags = (file->f_flags) & (~O_EXCL); unsigned short cflags; struct coda_inode_info *cii; struct file *cfile; struct inode *cinode, *inode; int err = 0, fcnt; coda_vfs_stat.flush++; /* No need to make an upcall when we have not made any modifications * to the file */ if ((file->f_flags & O_ACCMODE) == O_RDONLY) return 0; if (use_coda_close) return 0; fcnt = file_count(file); if (fcnt > 1) return 0; cflags = coda_flags_to_cflags(flags); inode = file->f_dentry->d_inode; cii = ITOC(inode); cfile = cii->c_container; if (!cfile) BUG(); cinode = cfile->f_dentry->d_inode; CDEBUG(D_FILE, "FLUSH coda (file %p ct %d)\n", file, fcnt); err = venus_store(inode->i_sb, coda_i2f(inode), cflags, (struct coda_cred *)file->private_data); if (err == -EOPNOTSUPP) { use_coda_close = 1; err = 0; } CDEBUG(D_FILE, "coda_flush: result: %d\n", err); return err; }
static void coda_clear_inode(struct inode *inode) { struct coda_inode_info *cii = ITOC(inode); CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", inode->i_ino, atomic_read(&inode->i_count)); CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); if (cii->c_container) BUG(); list_del_init(&cii->c_cilist); inode->i_mapping = &inode->i_data; coda_cache_clear_inode(inode); #if 0 cii_free(inode->u.generic_ip); inode->u.generic_ip = NULL; #endif }
static long coda_pioctl(struct file *filp, unsigned int cmd, unsigned long user_data) { struct path path; int error; struct PioctlData data; struct inode *inode = filp->f_dentry->d_inode; struct inode *target_inode = NULL; struct coda_inode_info *cnp; /* get the Pioctl data arguments from user space */ if (copy_from_user(&data, (void __user *)user_data, sizeof(data))) return -EINVAL; /* * Look up the pathname. Note that the pathname is in * user memory, and namei takes care of this */ if (data.follow) error = user_path(data.path, &path); else error = user_lpath(data.path, &path); if (error) return error; target_inode = path.dentry->d_inode; /* return if it is not a Coda inode */ if (target_inode->i_sb != inode->i_sb) { error = -EINVAL; goto out; } /* now proceed to make the upcall */ cnp = ITOC(target_inode); error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); out: path_put(&path); return error; }
static ssize_t coda_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); struct file *cfile; ssize_t ret; cfile = cii->c_container; if (!cfile) BUG(); if (!cfile->f_op || !cfile->f_op->read) return -EINVAL; down(&inode->i_sem); ret = cfile->f_op->read(cfile, buf, count, ppos); UPDATE_ATIME(inode); up(&inode->i_sem); return ret; }
/* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, int flags) { struct inode *inode = de->d_inode; struct coda_inode_info *cii; if (!inode) return 1; lock_kernel(); if (coda_isroot(inode)) goto out; if (is_bad_inode(inode)) goto bad; cii = ITOC(de->d_inode); if (!(cii->c_flags & (C_PURGE | C_FLUSH))) goto out; shrink_dcache_parent(de); /* propagate for a flush */ if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); if (atomic_read(&de->d_count) > 1) { /* pretend it's valid, but don't change the flags */ CDEBUG(D_DOWNCALL, "BOOM for: ino %ld, %s\n", de->d_inode->i_ino, coda_f2s(&cii->c_fid)); goto out; } /* clear the flags. */ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); bad: unlock_kernel(); return 0; out: unlock_kernel(); return 1; }
static ssize_t coda_file_write(struct file *coda_file, const char *buff, size_t count, loff_t *ppos) { struct coda_inode_info *cnp; struct inode *coda_inode = coda_file->f_dentry->d_inode; struct inode *cont_inode = NULL; struct file cont_file; struct dentry cont_dentry; int result = 0; ENTRY; coda_vfs_stat.file_write++; cnp = ITOC(coda_inode); cont_inode = cnp->c_ovp; if ( cont_inode == NULL ) { printk("coda_file_write: cached inode is 0!\n"); return -1; } coda_prepare_openfile(coda_inode, coda_file, cont_inode, &cont_file, &cont_dentry); if (!cont_file.f_op || !cont_file.f_op->write) { printk("coda_file_write: container file has no file ops.\n"); return -1; } down(&cont_inode->i_sem); result = cont_file.f_op->write(&cont_file , buff, count, &(cont_file.f_pos)); up(&cont_inode->i_sem); coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); if (result) cnp->c_flags |= C_VATTR; return result; }
static int coda_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); struct file *cfile; int ret; cfile = cii->c_container; if (!cfile) BUG(); if (!cfile->f_op || !cfile->f_op->mmap) return -ENODEV; down(&inode->i_sem); ret = cfile->f_op->mmap(cfile, vma); UPDATE_ATIME(inode); up(&inode->i_sem); return ret; }
int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) { struct coda_inode_info *cnp; struct inode *coda_inode = coda_dentry->d_inode; struct inode *cont_inode = NULL; struct file cont_file; struct dentry cont_dentry; int result = 0; ENTRY; coda_vfs_stat.fsync++; if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || S_ISLNK(coda_inode->i_mode))) return -EINVAL; cnp = ITOC(coda_inode); cont_inode = cnp->c_ovp; if ( cont_inode == NULL ) { printk("coda_file_write: cached inode is 0!\n"); return -1; } coda_prepare_openfile(coda_inode, coda_file, cont_inode, &cont_file, &cont_dentry); down(&cont_inode->i_sem); result = file_fsync(&cont_file ,&cont_dentry); if ( result == 0 ) { result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid)); } up(&cont_inode->i_sem); coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); return result; }
static ssize_t coda_file_read(struct file *coda_file, char *buff, size_t count, loff_t *ppos) { struct coda_inode_info *cnp; struct inode *coda_inode = coda_file->f_dentry->d_inode; struct inode *cont_inode = NULL; struct file cont_file; struct dentry cont_dentry; int result = 0; ENTRY; coda_vfs_stat.file_read++; cnp = ITOC(coda_inode); cont_inode = cnp->c_ovp; if ( cont_inode == NULL ) { printk("coda_file_read: cached inode is 0!\n"); return -1; } coda_prepare_openfile(coda_inode, coda_file, cont_inode, &cont_file, &cont_dentry); if (!cont_file.f_op || ! cont_file.f_op->read) { printk( "container file has no read in file operations.\n"); return -1; } result = cont_file.f_op->read(&cont_file , buff, count, &(cont_file.f_pos)); CDEBUG(D_FILE, "ops at %p result %d, count %ld, position: %d\n", cont_file.f_op, result, (long)count, (int)cont_file.f_pos); coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); return result; }
static int coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma) { struct coda_file_info *cfi; struct coda_inode_info *cii; struct file *host_file; struct inode *coda_inode, *host_inode; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (!host_file->f_op || !host_file->f_op->mmap) return -ENODEV; coda_inode = coda_file->f_path.dentry->d_inode; host_inode = host_file->f_path.dentry->d_inode; cii = ITOC(coda_inode); spin_lock(&cii->c_lock); coda_file->f_mapping = host_file->f_mapping; if (coda_inode->i_mapping == &coda_inode->i_data) coda_inode->i_mapping = host_inode->i_mapping; /* only allow additional mmaps as long as userspace isn't changing * the container file on us! */ else if (coda_inode->i_mapping != host_inode->i_mapping) { spin_unlock(&cii->c_lock); return -EBUSY; } /* keep track of how often the coda_inode/host_file has been mmapped */ cii->c_mapcount++; cfi->cfi_mapcount++; spin_unlock(&cii->c_lock); return host_file->f_op->mmap(host_file, vma); }
/* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) { struct inode *inode; struct coda_inode_info *cii; if (nd->flags & LOOKUP_RCU) return -ECHILD; inode = de->d_inode; if (!inode || coda_isroot(inode)) goto out; if (is_bad_inode(inode)) goto bad; cii = ITOC(de->d_inode); if (!(cii->c_flags & (C_PURGE | C_FLUSH))) goto out; shrink_dcache_parent(de); /* propagate for a flush */ if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); if (de->d_count > 1) /* pretend it's valid, but don't change the flags */ goto out; /* clear the flags. */ spin_lock(&cii->c_lock); cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); spin_unlock(&cii->c_lock); bad: return 0; out: return 1; }
/* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) { struct inode *inode = de->d_inode; struct coda_inode_info *cii; if (!inode) return 1; lock_kernel(); if (coda_isroot(inode)) goto out; if (is_bad_inode(inode)) goto bad; cii = ITOC(de->d_inode); if (!(cii->c_flags & (C_PURGE | C_FLUSH))) goto out; shrink_dcache_parent(de); /* propagate for a flush */ if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); if (atomic_read(&de->d_count) > 1) /* pretend it's valid, but don't change the flags */ goto out; /* clear the flags. */ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); bad: unlock_kernel(); return 0; out: unlock_kernel(); return 1; }
int coda_revalidate_inode(struct dentry *dentry) { struct coda_vattr attr; int error; int old_mode; ino_t old_ino; struct inode *inode = dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); if (!cii->c_flags) return 0; if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) { error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); if (error) return -EIO; old_mode = inode->i_mode; old_ino = inode->i_ino; coda_vattr_to_iattr(inode, &attr); if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) { printk("Coda: inode %ld, fid %s changed type!\n", inode->i_ino, coda_f2s(&(cii->c_fid))); } if (inode->i_ino != old_ino) return -EIO; coda_flag_inode_children(inode, C_FLUSH); spin_lock(&cii->c_lock); cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); spin_unlock(&cii->c_lock); } return 0; }
int coda_release(struct inode *coda_inode, struct file *coda_file) { unsigned short flags = (coda_file->f_flags) & (~O_EXCL); unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_file_info *cfi; struct coda_inode_info *cii; struct inode *host_inode; int err = 0; lock_kernel(); cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, coda_file->f_cred->fsuid); host_inode = cfi->cfi_container->f_path.dentry->d_inode; cii = ITOC(coda_inode); /* did we mmap this file? */ if (coda_inode->i_mapping == &host_inode->i_data) { cii->c_mapcount -= cfi->cfi_mapcount; if (!cii->c_mapcount) coda_inode->i_mapping = &coda_inode->i_data; } fput(cfi->cfi_container); kfree(coda_file->private_data); coda_file->private_data = NULL; unlock_kernel(); /* VFS fput ignores the return value from file_operations->release, so * there is no use returning an error here */ return 0; }
struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, struct coda_vattr * attr) { struct inode *inode; struct coda_inode_info *cii; unsigned long hash = coda_f2i(fid); inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid); if (!inode) return ERR_PTR(-ENOMEM); if (inode->i_state & I_NEW) { cii = ITOC(inode); /* we still need to set i_ino for things like stat(2) */ inode->i_ino = hash; cii->c_mapcount = 0; unlock_new_inode(inode); } /* always replace the attributes, type might have changed */ coda_fill_inode(inode, attr); return inode; }
/* convert a fid to an inode. */ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) { ino_t nr; struct inode *inode; struct coda_inode_info *cii; if ( !sb ) { printk("coda_fid_to_inode: no sb!\n"); return NULL; } CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); nr = coda_f2i(fid); inode = iget4(sb, nr, coda_inocmp, fid); if ( !inode ) { printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n", sb, (long)nr); return NULL; } cii = ITOC(inode); /* The inode could already be purged due to memory pressure */ if (coda_isnullfid(&cii->c_fid)) { inode->i_nlink = 0; iput(inode); return NULL; } /* we shouldn't see inode collisions anymore */ if ( !coda_fideq(fid, &cii->c_fid) ) BUG(); CDEBUG(D_INODE, "found %ld\n", inode->i_ino); return inode; }
static int coda_symlink_filler(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; int error; struct coda_inode_info *cii; unsigned int len = PAGE_SIZE; char *p = kmap(page); cii = ITOC(inode); error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len); if (error) goto fail; SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; fail: SetPageError(page); kunmap(page); unlock_page(page); return error; }
static int coda_test_inode(struct inode *inode, void *data) { struct CodaFid *fid = (struct CodaFid *)data; struct coda_inode_info *cii = ITOC(inode); return coda_fideq(&cii->c_fid, fid); }
static int coda_set_inode(struct inode *inode, void *data) { struct CodaFid *fid = (struct CodaFid *)data; ITOC(inode)->c_fid = *fid; return 0; }
static int coda_test_inode(struct inode *inode, void *data) { struct CodaFid *fid = (struct CodaFid *)data; return coda_fideq(&(ITOC(inode)->c_fid), fid); }
static void coda_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(coda_inode_cachep, ITOC(inode)); }
/* support routines */ static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx) { struct coda_file_info *cfi; struct coda_inode_info *cii; struct file *host_file; 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; cii = ITOC(file_inode(coda_file)); vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; if (!dir_emit_dots(coda_file, ctx)) goto out; while (1) { /* read entries from the directory file */ ret = kernel_read(host_file, ctx->pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { pr_err("%s: read dir %s failed %d\n", __func__, 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) { pr_err("%s: short read on %s\n", __func__, 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) { pr_err("%s: invalid dir %s\n", __func__, 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 || (name.name[1] == '.' && name.len == 2))) vdir->d_fileno = name.len = 0; /* skip null entries */ if (vdir->d_fileno && name.len) { ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); if (!dir_emit(ctx, name.name, name.len, ino, type)) break; } /* we'll always have progress because d_reclen is unsigned and * we've already established it is non-zero. */ ctx->pos += vdir->d_reclen; } out: kfree(vdir); return 0; }
/* 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; }