fs/readdir.c

Source file repositories/reference/linux-study-clean/fs/readdir.c

File Facts

System
Linux kernel
Corpus path
fs/readdir.c
Extension
.c
Size
15484 bytes
Lines
574
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: syscall or user/kernel boundary
Status
core implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
		struct old_linux_dirent __user *, dirent, unsigned int, count)
{
	int error;
	CLASS(fd_pos, f)(fd);
	struct readdir_callback buf = {
		.ctx.actor = fillonedir,
		.ctx.count = 1, /* Hint to fs: just one entry. */
		.dirent = dirent
	};

	if (fd_empty(f))
		return -EBADF;

	error = iterate_dir(fd_file(f), &buf.ctx);
	if (buf.result)
		error = buf.result;

	return error;
}

#endif /* __ARCH_WANT_OLD_READDIR */

/*
 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 * interface. 
 */
struct linux_dirent {
	unsigned long	d_ino;
	unsigned long	d_off;
	unsigned short	d_reclen;
	char		d_name[];
};

struct getdents_callback {
	struct dir_context ctx;
	struct linux_dirent __user * current_dir;
	int prev_reclen;
	int error;
};

static bool filldir(struct dir_context *ctx, const char *name, int namlen,
		   loff_t offset, u64 ino, unsigned int d_type)
{
	struct linux_dirent __user *dirent, *prev;
	struct getdents_callback *buf =
		container_of(ctx, struct getdents_callback, ctx);
	unsigned long d_ino;
	int reclen = ALIGN(dirent_size(dirent, namlen + 2), sizeof(long));
	int prev_reclen;
	unsigned int flags = d_type;

	BUILD_BUG_ON(FILLDIR_FLAG_NOINTR & S_DT_MASK);
	d_type &= S_DT_MASK;

	buf->error = verify_dirent_name(name, namlen);
	if (unlikely(buf->error))
		return false;
	buf->error = -EINVAL;	/* only used if we fail.. */
	if (reclen > ctx->count)
		return false;
	d_ino = ino;
	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
		buf->error = -EOVERFLOW;
		return false;
	}
	prev_reclen = buf->prev_reclen;
	if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current))
		return false;
	dirent = buf->current_dir;
	prev = (void __user *) dirent - prev_reclen;
	scoped_user_write_access_size(prev, reclen + prev_reclen, efault) {
		/* This might be 'dirent->d_off', but if so it will get overwritten */
		unsafe_put_user(offset, &prev->d_off, efault);
		unsafe_put_user(d_ino, &dirent->d_ino, efault);
		unsafe_put_user(reclen, &dirent->d_reclen, efault);
		unsafe_put_user(d_type, (char __user *)dirent + reclen - 1, efault);
		unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault);
	}

	buf->current_dir = (void __user *)dirent + reclen;
	buf->prev_reclen = reclen;
	ctx->count -= reclen;
	return true;
efault:
	buf->error = -EFAULT;
	return false;
}

SYSCALL_DEFINE3(getdents, unsigned int, fd,

Annotation

Implementation Notes