+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_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_SECTOR_SIZE - 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;
+
+ offset = (char *)de - kaddr;
+
+ UFSD("filldir(%s,%u)\n", de->d_name,
+ fs32_to_cpu(sb, de->d_ino));
+ UFSD("namlen %u\n", ufs_get_de_namlen(sb, de));
+
+ if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
+ d_type = de->d_u.d_44.d_type;
+
+ over = filldir(dirent, de->d_name,
+ ufs_get_de_namlen(sb, de),
+ (n<<PAGE_CACHE_SHIFT) | offset,
+ fs32_to_cpu(sb, de->d_ino), d_type);
+ if (over) {
+ ufs_put_page(page);
+ return 0;
+ }
+ }
+ filp->f_pos += fs16_to_cpu(sb, de->d_reclen);
+ }
+ ufs_put_page(page);
+ }