@@ -4158,16 +4158,21 @@ static unsigned char btrfs_filetype_table[] = {
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
};
+/*
+ * Return value:
+ * 0 - Reached end of directory/root in the ctree.
+ * 1 - buffer is full
+ * <0 - error happened
+ */
static int btrfs_real_readdir(struct file *filp, void *dirent,
- filldir_t filldir)
+ filldir_t filldir, struct btrfs_root *root,
+ int key_type, struct btrfs_path *path)
{
struct inode *inode = filp->f_dentry->d_inode;
- struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_item *item;
struct btrfs_dir_item *di;
struct btrfs_key key;
struct btrfs_key found_key;
- struct btrfs_path *path;
int ret;
u32 nritems;
struct extent_buffer *leaf;
@@ -4178,36 +4183,10 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
u32 di_cur;
u32 di_total;
u32 di_len;
- int key_type = BTRFS_DIR_INDEX_KEY;
char tmp_name[32];
char *name_ptr;
int name_len;
- /* FIXME, use a real flag for deciding about the key type */
- if (root->fs_info->tree_root == root)
- key_type = BTRFS_DIR_ITEM_KEY;
-
- /* special case for "." */
- if (filp->f_pos == 0) {
- over = filldir(dirent, ".", 1,
- 1, inode->i_ino,
- DT_DIR);
- if (over)
- return 0;
- filp->f_pos = 1;
- }
- /* special case for .., just use the back ref */
- if (filp->f_pos == 1) {
- u64 pino = parent_ino(filp->f_path.dentry);
- over = filldir(dirent, "..", 2,
- 2, pino, DT_DIR);
- if (over)
- return 0;
- filp->f_pos = 2;
- }
- path = btrfs_alloc_path();
- path->reada = 2;
-
btrfs_set_key_type(&key, key_type);
key.offset = filp->f_pos;
key.objectid = inode->i_ino;
@@ -4224,7 +4203,9 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
if (advance || slot >= nritems) {
if (slot >= nritems - 1) {
ret = btrfs_next_leaf(root, path);
- if (ret)
+ if (ret < 0)
+ goto err;
+ else if (ret > 0)
break;
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
@@ -4287,8 +4268,10 @@ skip:
if (name_ptr != tmp_name)
kfree(name_ptr);
- if (over)
- goto nopos;
+ if (over) {
+ ret = 1;
+ goto err;
+ }
di_len = btrfs_dir_name_len(leaf, di) +
btrfs_dir_data_len(leaf, di) + sizeof(*di);
di_cur += di_len;
@@ -4296,6 +4279,55 @@ skip:
}
}
+ ret = 0;
+err:
+ btrfs_release_path(root, path);
+ return ret;
+}
+
+static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_path *path;
+ int key_type = BTRFS_DIR_INDEX_KEY;
+ int ret;
+ int over = 0;
+
+ /* FIXME, use a real flag for deciding about the key type */
+ if (root->fs_info->tree_root == root)
+ key_type = BTRFS_DIR_ITEM_KEY;
+
+ /* special case for "." */
+ if (filp->f_pos == 0) {
+ over = filldir(dirent, ".", 1,
+ 1, inode->i_ino,
+ DT_DIR);
+ if (over)
+ return 0;
+ filp->f_pos = 1;
+ }
+ /* special case for .., just use the back ref */
+ if (filp->f_pos == 1) {
+ u64 pino = parent_ino(filp->f_path.dentry);
+ over = filldir(dirent, "..", 2,
+ 2, pino, DT_DIR);
+ if (over)
+ return 0;
+ filp->f_pos = 2;
+ }
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ path->reada = 2;
+
+ ret = btrfs_real_readdir(filp, dirent, filldir, root, key_type, path);
+ if (ret < 0)
+ goto err;
+ else if (ret > 0)
+ goto nopos;
+
/* Reached end of directory/root. Bump pos past the last item. */
if (key_type == BTRFS_DIR_INDEX_KEY)
/*
@@ -7238,7 +7270,7 @@ static const struct inode_operations btrfs_dir_ro_inode_operations = {
static const struct file_operations btrfs_dir_file_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
- .readdir = btrfs_real_readdir,
+ .readdir = btrfs_readdir,
.unlocked_ioctl = btrfs_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = btrfs_ioctl,