Unverified Commit 796432ef authored by Chuck Lever's avatar Chuck Lever Committed by Christian Brauner
Browse files

libfs: getdents() should return 0 after reaching EOD



The new directory offset helpers don't conform with the convention
of getdents() returning no more entries once a directory file
descriptor has reached the current end-of-directory.

To address this, copy the logic from dcache_readdir() to mark the
open directory file descriptor once EOD has been reached. Seeking
resets the mark.

Reported-by: default avatarTavian Barnes <tavianator@tavianator.com>
Closes: https://lore.kernel.org/linux-fsdevel/20231113180616.2831430-1-tavianator@tavianator.com/


Fixes: 6faddda6 ("libfs: Add directory operations for stable offsets")
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Link: https://lore.kernel.org/r/170043792492.4628.15646203084646716134.stgit@bazille.1015granger.net


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 9c041384
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -399,6 +399,8 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence)
		return -EINVAL;
	}

	/* In this case, ->private_data is protected by f_pos_lock */
	file->private_data = NULL;
	return vfs_setpos(file, offset, U32_MAX);
}

@@ -428,7 +430,7 @@ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
			  inode->i_ino, fs_umode_to_dtype(inode->i_mode));
}

static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
{
	struct offset_ctx *so_ctx = inode->i_op->get_offset_ctx(inode);
	XA_STATE(xas, &so_ctx->xa, ctx->pos);
@@ -437,7 +439,7 @@ static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
	while (true) {
		dentry = offset_find_next(&xas);
		if (!dentry)
			break;
			return ERR_PTR(-ENOENT);

		if (!offset_dir_emit(ctx, dentry)) {
			dput(dentry);
@@ -447,6 +449,7 @@ static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
		dput(dentry);
		ctx->pos = xas.xa_index + 1;
	}
	return NULL;
}

/**
@@ -479,7 +482,12 @@ static int offset_readdir(struct file *file, struct dir_context *ctx)
	if (!dir_emit_dots(file, ctx))
		return 0;

	offset_iterate_dir(d_inode(dir), ctx);
	/* In this case, ->private_data is protected by f_pos_lock */
	if (ctx->pos == 2)
		file->private_data = NULL;
	else if (file->private_data == ERR_PTR(-ENOENT))
		return 0;
	file->private_data = offset_iterate_dir(d_inode(dir), ctx);
	return 0;
}