+ /* OFFSET_CACHE */
+out_put:
+ ufs_put_page(page);
+out:
+ return err;
+out_unlock:
+ unlock_page(page);
+ goto out_put;
+}
+
+static inline unsigned
+ufs_validate_entry(struct super_block *sb, char *base,
+ unsigned offset, unsigned mask)
+{
+ struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset);
+ struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask));
+ while ((char*)p < (char*)de) {
+ if (p->d_reclen == 0)
+ break;
+ p = ufs_next_entry(sb, p);
+ }
+ return (char *)p - base;
+}
+
+
+/*
+ * This is blatantly stolen from ext2fs
+ */
+static int
+ufs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ loff_t pos = filp->f_pos;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned int offset = pos & ~PAGE_CACHE_MASK;
+ unsigned long n = pos >> PAGE_CACHE_SHIFT;
+ unsigned long npages = ufs_dir_pages(inode);
+ unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
+ int need_revalidate = filp->f_version != inode->i_version;
+ unsigned flags = UFS_SB(sb)->s_flags;
+
+ UFSD("BEGIN\n");
+
+ if (pos > inode->i_size - UFS_DIR_REC_LEN(1))
+ return 0;
+
+ for ( ; n < npages; n++, offset = 0) {
+ char *kaddr, *limit;
+ struct ufs_dir_entry *de;
+
+ struct page *page = ufs_get_page(inode, n);
+
+ if (IS_ERR(page)) {
+ ufs_error(sb, __FUNCTION__,
+ "bad page in #%lu",
+ inode->i_ino);
+ filp->f_pos += PAGE_CACHE_SIZE - offset;
+ return -EIO;
+ }
+ kaddr = page_address(page);
+ if (unlikely(need_revalidate)) {
+ if (offset) {
+ offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask);
+ filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
+ }
+ filp->f_version = inode->i_version;
+ need_revalidate = 0;
+ }
+ de = (struct ufs_dir_entry *)(kaddr+offset);
+ limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1);
+ for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) {
+ if (de->d_reclen == 0) {
+ ufs_error(sb, __FUNCTION__,
+ "zero-length directory entry");
+ ufs_put_page(page);
+ return -EIO;
+ }
+ if (de->d_ino) {
+ int over;
+ unsigned char d_type = DT_UNKNOWN;