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; 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) return -EBUSY; /* keep track of how often the coda_inode/host_file has been mmapped */ cii = ITOC(coda_inode); cii->c_mapcount++; cfi->cfi_mapcount++; return host_file->f_op->mmap(host_file, vma); }
/* file operations for directories */ int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) { struct dentry *coda_dentry = coda_file->f_dentry; struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG(); host_file = cfi->cfi_container; coda_vfs_stat.readdir++; down(&host_file->f_dentry->d_inode->i_sem); host_file->f_pos = coda_file->f_pos; if ( !host_file->f_op->readdir ) { /* Venus: we must read Venus dirents from the file */ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry); } else { /* potemkin case: we were handed a directory inode */ ret = vfs_readdir(host_file, filldir, dirent); } coda_file->f_pos = host_file->f_pos; up(&host_file->f_dentry->d_inode->i_sem); return ret; }
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; 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? */ spin_lock(&cii->c_lock); 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; } spin_unlock(&cii->c_lock); fput(cfi->cfi_container); kfree(coda_file->private_data); coda_file->private_data = NULL; /* VFS fput ignores the return value from file_operations->release, so * there is no use returning an error here */ return 0; }
int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) { struct file *host_file; struct dentry *host_dentry; struct inode *host_inode, *coda_inode = coda_dentry->d_inode; struct coda_file_info *cfi; int err = 0; if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || S_ISLNK(coda_inode->i_mode))) return -EINVAL; cfi = CODA_FTOC(coda_file); if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG(); host_file = cfi->cfi_container; coda_vfs_stat.fsync++; if (host_file->f_op && host_file->f_op->fsync) { host_dentry = host_file->f_dentry; host_inode = host_dentry->d_inode; down(&host_inode->i_sem); err = host_file->f_op->fsync(host_file, host_dentry, datasync); up(&host_inode->i_sem); } if ( !err && !datasync ) { lock_kernel(); err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); unlock_kernel(); } return err; }
static ssize_t coda_file_write(struct file *coda_file, const char *buf, size_t count, loff_t *ppos) { struct inode *coda_inode = coda_file->f_dentry->d_inode; struct coda_file_info *cfi; struct file *host_file; ssize_t ret; cfi = CODA_FTOC(coda_file); if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG(); host_file = cfi->cfi_container; if (!host_file->f_op || !host_file->f_op->write) return -EINVAL; down(&coda_inode->i_sem); ret = host_file->f_op->write(host_file, buf, count, ppos); coda_inode->i_size = host_file->f_dentry->d_inode->i_size; coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9; coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME; up(&coda_inode->i_sem); return ret; }
int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync) { struct file *host_file; struct inode *coda_inode = coda_file->f_path.dentry->d_inode; struct coda_file_info *cfi; int err; if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || S_ISLNK(coda_inode->i_mode))) return -EINVAL; err = filemap_write_and_wait_range(coda_inode->i_mapping, start, end); if (err) return err; mutex_lock(&coda_inode->i_mutex); cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; err = vfs_fsync(host_file, datasync); if (!err && !datasync) err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); mutex_unlock(&coda_inode->i_mutex); return err; }
static ssize_t coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos) { struct inode *host_inode, *coda_inode = coda_file->f_path.dentry->d_inode; struct coda_file_info *cfi; struct file *host_file; ssize_t ret; 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->write) return -EINVAL; host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&coda_inode->i_mutex); ret = host_file->f_op->write(host_file, buf, count, ppos); coda_inode->i_size = host_inode->i_size; coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9; coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME_SEC; mutex_unlock(&coda_inode->i_mutex); return ret; }
static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (!host_file->f_op) return -ENOTDIR; if (host_file->f_op->readdir) { struct inode *host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, buf, filldir); file_accessed(host_file); } coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); } else ret = coda_venus_readdir(coda_file, buf, filldir); return ret; }
static ssize_t coda_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *coda_file = iocb->ki_filp; struct coda_file_info *cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); return vfs_iter_read(cfi->cfi_container, to, &iocb->ki_pos); }
static ssize_t coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos) { struct coda_file_info *cfi; struct file *host_file; 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->read) return -EINVAL; return host_file->f_op->read(host_file, buf, count, ppos); }
static ssize_t coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, read_actor_t actor, void *target) { struct coda_file_info *cfi; struct file *host_file; 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->sendfile) return -EINVAL; return host_file->f_op->sendfile(host_file, ppos, count, actor, target); }
static ssize_t coda_file_splice_read(struct file *coda_file, loff_t *ppos, struct pipe_inode_info *pipe, size_t count, unsigned int flags) { struct coda_file_info *cfi; struct file *host_file; 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->splice_read) return -EINVAL; return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags); }
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); if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG(); if (use_coda_close) err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, &cfi->cfi_cred); 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; }
int coda_flush(struct file *coda_file, fl_owner_t id) { unsigned short flags = coda_file->f_flags & ~O_EXCL; unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_file_info *cfi; struct inode *coda_inode; int err = 0, fcnt; lock_kernel(); coda_vfs_stat.flush++; /* last close semantics */ fcnt = file_count(coda_file); if (fcnt > 1) goto out; /* No need to make an upcall when we have not made any modifications * to the file */ if ((coda_file->f_flags & O_ACCMODE) == O_RDONLY) goto out; if (use_coda_close) goto out; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); coda_inode = coda_file->f_dentry->d_inode; err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, coda_file->f_uid); if (err == -EOPNOTSUPP) { use_coda_close = 1; err = 0; } out: unlock_kernel(); return err; }
static ssize_t coda_file_splice_read(struct file *coda_file, loff_t *ppos, struct pipe_inode_info *pipe, size_t count, unsigned int flags) { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); struct coda_file_info *cfi; struct file *host_file; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; splice_read = host_file->f_op->splice_read; if (!splice_read) splice_read = default_file_splice_read; return splice_read(host_file, ppos, pipe, count, flags); }
/* file operations for directories */ int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) { struct dentry *coda_dentry = coda_file->f_path.dentry; struct coda_file_info *cfi; struct file *host_file; struct inode *host_inode; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; coda_vfs_stat.readdir++; host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; if (!host_file->f_op->readdir) { /* Venus: we must read Venus dirents from the file */ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry); } else { /* potemkin case: we were handed a directory inode. */ /* Yuk, we can't call vfs_readdir because we are already * holding the inode semaphore. */ ret = -ENOTDIR; if (!host_file->f_op || !host_file->f_op->readdir) goto out; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, filldir, dirent); file_accessed(host_file); } } out: coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); return ret; }
static ssize_t coda_file_write_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *coda_file = iocb->ki_filp; struct inode *coda_inode = file_inode(coda_file); struct coda_file_info *cfi = CODA_FTOC(coda_file); struct file *host_file; ssize_t ret; BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; file_start_write(host_file); inode_lock(coda_inode); ret = vfs_iter_write(cfi->cfi_container, to, &iocb->ki_pos); coda_inode->i_size = file_inode(host_file)->i_size; coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9; coda_inode->i_mtime = coda_inode->i_ctime = current_time(coda_inode); inode_unlock(coda_inode); file_end_write(host_file); return ret; }
/* file operations for directories */ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (!host_file->f_op) return -ENOTDIR; if (host_file->f_op->readdir) { /* potemkin case: we were handed a directory inode. * We can't use vfs_readdir because we have to keep the file * position in sync between the coda_file and the host_file. * and as such we need grab the inode mutex. */ struct inode *host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, buf, filldir); file_accessed(host_file); } coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); } else /* Venus: we must read Venus dirents from a file */ ret = coda_venus_readdir(coda_file, buf, filldir); return ret; }
int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) { struct file *host_file; struct inode *coda_inode = coda_dentry->d_inode; struct coda_file_info *cfi; int err = 0; if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || S_ISLNK(coda_inode->i_mode))) return -EINVAL; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; err = vfs_fsync(host_file, host_file->f_path.dentry, datasync); if ( !err && !datasync ) { lock_kernel(); err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); unlock_kernel(); } return err; }
/* file operations for directories */ static int coda_readdir(struct file *coda_file, struct dir_context *ctx) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (host_file->f_op->iterate) { struct inode *host_inode = file_inode(host_file); mutex_lock(&host_inode->i_mutex); ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->iterate(host_file, ctx); file_accessed(host_file); } mutex_unlock(&host_inode->i_mutex); return ret; } /* Venus: we must read Venus dirents from a file */ return coda_venus_readdir(coda_file, ctx); }
int coda_flush(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 inode *coda_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 ((coda_file->f_flags & O_ACCMODE) == O_RDONLY) return 0; if (use_coda_close) return 0; fcnt = file_count(coda_file); if (fcnt > 1) return 0; coda_inode = coda_file->f_dentry->d_inode; cfi = CODA_FTOC(coda_file); if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG(); err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, &cfi->cfi_cred); if (err == -EOPNOTSUPP) { use_coda_close = 1; err = 0; } return err; }
/* 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; }
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; }
/* 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; }