xref: /openbmc/linux/fs/orangefs/dir.c (revision 72f66b83)
15db11c21SMike Marshall /*
2382f4581SMartin Brandenburg  * Copyright 2017 Omnibond Systems, L.L.C.
35db11c21SMike Marshall  */
45db11c21SMike Marshall 
55db11c21SMike Marshall #include "protocol.h"
6575e9461SMike Marshall #include "orangefs-kernel.h"
7575e9461SMike Marshall #include "orangefs-bufmap.h"
85db11c21SMike Marshall 
95db11c21SMike Marshall /*
10382f4581SMartin Brandenburg  * There can be up to 512 directory entries.  Each entry is encoded as
11382f4581SMartin Brandenburg  * follows:
12382f4581SMartin Brandenburg  * 4 bytes: string size (n)
13382f4581SMartin Brandenburg  * n bytes: string
14382f4581SMartin Brandenburg  * 1 byte: trailing zero
15382f4581SMartin Brandenburg  * padding to 8 bytes
16382f4581SMartin Brandenburg  * 16 bytes: khandle
17382f4581SMartin Brandenburg  * padding to 8 bytes
185db11c21SMike Marshall  */
19382f4581SMartin Brandenburg #define MAX_DIRECTORY ((4 + 257 + 3 + 16)*512)
20382f4581SMartin Brandenburg 
21382f4581SMartin Brandenburg struct orangefs_dir {
22382f4581SMartin Brandenburg 	__u64 token;
23382f4581SMartin Brandenburg 	void *directory;
2472f66b83SMartin Brandenburg 	size_t len;
25382f4581SMartin Brandenburg 	int error;
26382f4581SMartin Brandenburg };
27382f4581SMartin Brandenburg 
28382f4581SMartin Brandenburg /*
29382f4581SMartin Brandenburg  * The userspace component sends several directory entries of the
30382f4581SMartin Brandenburg  * following format.  The first four bytes are the string length not
31382f4581SMartin Brandenburg  * including a trailing zero byte.  This is followed by the string and a
32382f4581SMartin Brandenburg  * trailing zero padded to the next four byte boundry.  This is followed
33382f4581SMartin Brandenburg  * by the sixteen byte khandle padded to the next eight byte boundry.
34382f4581SMartin Brandenburg  *
35382f4581SMartin Brandenburg  * The trailer_buf starts with a struct orangefs_readdir_response_s
36382f4581SMartin Brandenburg  * which must be skipped to get to the directory data.
37382f4581SMartin Brandenburg  */
38382f4581SMartin Brandenburg 
39382f4581SMartin Brandenburg static int orangefs_dir_more(struct orangefs_inode_s *oi,
40382f4581SMartin Brandenburg     struct orangefs_dir *od, struct dentry *dentry)
415db11c21SMike Marshall {
42382f4581SMartin Brandenburg 	const size_t offset =
43382f4581SMartin Brandenburg 	    sizeof(struct orangefs_readdir_response_s);
44382f4581SMartin Brandenburg 	struct orangefs_readdir_response_s *resp;
45382f4581SMartin Brandenburg 	struct orangefs_kernel_op_s *op;
46382f4581SMartin Brandenburg 	int bufi, r;
475db11c21SMike Marshall 
48382f4581SMartin Brandenburg 	op = op_alloc(ORANGEFS_VFS_OP_READDIR);
49382f4581SMartin Brandenburg 	if (!op) {
50382f4581SMartin Brandenburg 		od->error = -ENOMEM;
51382f4581SMartin Brandenburg 		return -ENOMEM;
525db11c21SMike Marshall 	}
535db11c21SMike Marshall 
545db11c21SMike Marshall 	/*
55382f4581SMartin Brandenburg 	 * Despite the badly named field, readdir does not use shared
56382f4581SMartin Brandenburg 	 * memory.  However, there are a limited number of readdir
57382f4581SMartin Brandenburg 	 * slots, which must be allocated here.  This flag simply tells
58382f4581SMartin Brandenburg 	 * the op scheduler to return the op here for retry.
595db11c21SMike Marshall 	 */
60382f4581SMartin Brandenburg 	op->uses_shared_memory = 1;
61382f4581SMartin Brandenburg 	op->upcall.req.readdir.refn = oi->refn;
62382f4581SMartin Brandenburg 	op->upcall.req.readdir.token = od->token;
63382f4581SMartin Brandenburg 	op->upcall.req.readdir.max_dirent_count =
64382f4581SMartin Brandenburg 	    ORANGEFS_MAX_DIRENT_COUNT_READDIR;
655db11c21SMike Marshall 
66382f4581SMartin Brandenburg again:
67382f4581SMartin Brandenburg 	bufi = orangefs_readdir_index_get();
68382f4581SMartin Brandenburg 	if (bufi < 0) {
69382f4581SMartin Brandenburg 		op_release(op);
70382f4581SMartin Brandenburg 		od->error = bufi;
71382f4581SMartin Brandenburg 		return bufi;
72382f4581SMartin Brandenburg 	}
735db11c21SMike Marshall 
74382f4581SMartin Brandenburg 	op->upcall.req.readdir.buf_index = bufi;
755db11c21SMike Marshall 
76382f4581SMartin Brandenburg 	r = service_operation(op, "orangefs_readdir",
77382f4581SMartin Brandenburg 	    get_interruptible_flag(dentry->d_inode));
78382f4581SMartin Brandenburg 
79382f4581SMartin Brandenburg 	orangefs_readdir_index_put(bufi);
80382f4581SMartin Brandenburg 
81382f4581SMartin Brandenburg 	if (op_state_purged(op)) {
82382f4581SMartin Brandenburg 		if (r == -EAGAIN) {
83382f4581SMartin Brandenburg 			vfree(op->downcall.trailer_buf);
84382f4581SMartin Brandenburg 			goto again;
85382f4581SMartin Brandenburg 		} else if (r == -EIO) {
86382f4581SMartin Brandenburg 			vfree(op->downcall.trailer_buf);
87382f4581SMartin Brandenburg 			op_release(op);
88382f4581SMartin Brandenburg 			od->error = r;
89382f4581SMartin Brandenburg 			return r;
90382f4581SMartin Brandenburg 		}
91382f4581SMartin Brandenburg 	}
92382f4581SMartin Brandenburg 
93382f4581SMartin Brandenburg 	if (r < 0) {
94382f4581SMartin Brandenburg 		vfree(op->downcall.trailer_buf);
95382f4581SMartin Brandenburg 		op_release(op);
96382f4581SMartin Brandenburg 		od->error = r;
97382f4581SMartin Brandenburg 		return r;
98382f4581SMartin Brandenburg 	} else if (op->downcall.status) {
99382f4581SMartin Brandenburg 		vfree(op->downcall.trailer_buf);
100382f4581SMartin Brandenburg 		op_release(op);
101382f4581SMartin Brandenburg 		od->error = op->downcall.status;
102382f4581SMartin Brandenburg 		return op->downcall.status;
103382f4581SMartin Brandenburg 	}
104382f4581SMartin Brandenburg 
105382f4581SMartin Brandenburg 	resp = (struct orangefs_readdir_response_s *)
106382f4581SMartin Brandenburg 	    op->downcall.trailer_buf;
107382f4581SMartin Brandenburg 	od->token = resp->token;
108382f4581SMartin Brandenburg 
109382f4581SMartin Brandenburg 	if (od->len + op->downcall.trailer_size - offset <=
110382f4581SMartin Brandenburg 	    MAX_DIRECTORY) {
111382f4581SMartin Brandenburg 		memcpy(od->directory + od->len,
112382f4581SMartin Brandenburg 		    op->downcall.trailer_buf + offset,
113382f4581SMartin Brandenburg 		    op->downcall.trailer_size - offset);
114382f4581SMartin Brandenburg 		od->len += op->downcall.trailer_size - offset;
115382f4581SMartin Brandenburg 	} else {
116382f4581SMartin Brandenburg 		/* This limit was chosen based on protocol limits. */
117382f4581SMartin Brandenburg 		gossip_err("orangefs_dir_more: userspace sent too much data\n");
118382f4581SMartin Brandenburg 		vfree(op->downcall.trailer_buf);
119382f4581SMartin Brandenburg 		op_release(op);
120382f4581SMartin Brandenburg 		od->error = -EIO;
121382f4581SMartin Brandenburg 		return -EIO;
122382f4581SMartin Brandenburg 	}
123382f4581SMartin Brandenburg 
124382f4581SMartin Brandenburg 	vfree(op->downcall.trailer_buf);
125382f4581SMartin Brandenburg 	op_release(op);
1265db11c21SMike Marshall 	return 0;
1275db11c21SMike Marshall }
1285db11c21SMike Marshall 
129382f4581SMartin Brandenburg static int orangefs_dir_fill(struct orangefs_inode_s *oi,
130382f4581SMartin Brandenburg     struct orangefs_dir *od, struct dentry *dentry,
131382f4581SMartin Brandenburg     struct dir_context *ctx)
132382f4581SMartin Brandenburg {
133382f4581SMartin Brandenburg 	struct orangefs_khandle *khandle;
134382f4581SMartin Brandenburg 	__u32 *len, padlen;
13572f66b83SMartin Brandenburg 	loff_t i;
136382f4581SMartin Brandenburg 	char *s;
13772f66b83SMartin Brandenburg 	i = ctx->pos - 2;
13872f66b83SMartin Brandenburg 	while (i < od->len) {
13972f66b83SMartin Brandenburg 		if (od->len < i + sizeof *len)
140382f4581SMartin Brandenburg 			goto eio;
14172f66b83SMartin Brandenburg 		len = od->directory + i;
142ee3b8d37SMartin Brandenburg 		/*
143382f4581SMartin Brandenburg 		 * len is the size of the string itself.  padlen is the
144382f4581SMartin Brandenburg 		 * total size of the encoded string.
145ee3b8d37SMartin Brandenburg 		 */
146382f4581SMartin Brandenburg 		padlen = (sizeof *len + *len + 1) +
147382f4581SMartin Brandenburg 		    (4 - (sizeof *len + *len + 1)%8)%8;
14872f66b83SMartin Brandenburg 		if (od->len < i + padlen + sizeof *khandle)
149382f4581SMartin Brandenburg 			goto eio;
15072f66b83SMartin Brandenburg 		s = od->directory + i + sizeof *len;
151382f4581SMartin Brandenburg 		if (s[*len] != 0)
152382f4581SMartin Brandenburg 			goto eio;
15372f66b83SMartin Brandenburg 		khandle = od->directory + i + padlen;
1545db11c21SMike Marshall 
155382f4581SMartin Brandenburg 		if (!dir_emit(ctx, s, *len,
156382f4581SMartin Brandenburg 		    orangefs_khandle_to_ino(khandle), DT_UNKNOWN))
157382f4581SMartin Brandenburg 			return 0;
15872f66b83SMartin Brandenburg 		i += padlen + sizeof *khandle;
15972f66b83SMartin Brandenburg 		i = i + (8 - i%8)%8;
16072f66b83SMartin Brandenburg 		ctx->pos = i + 2;
1615db11c21SMike Marshall 	}
16272f66b83SMartin Brandenburg 	BUG_ON(i > od->len);
163382f4581SMartin Brandenburg 	return 0;
164382f4581SMartin Brandenburg eio:
16572f66b83SMartin Brandenburg 	/*
16672f66b83SMartin Brandenburg 	 * Here either data from userspace is corrupt or the application
16772f66b83SMartin Brandenburg 	 * has sought to an invalid location.
16872f66b83SMartin Brandenburg 	 */
169382f4581SMartin Brandenburg 	od->error = -EIO;
170382f4581SMartin Brandenburg 	return -EIO;
1715db11c21SMike Marshall }
1725db11c21SMike Marshall 
173382f4581SMartin Brandenburg static int orangefs_dir_iterate(struct file *file,
174382f4581SMartin Brandenburg     struct dir_context *ctx)
175382f4581SMartin Brandenburg {
176382f4581SMartin Brandenburg 	struct orangefs_inode_s *oi;
177382f4581SMartin Brandenburg 	struct orangefs_dir *od;
178382f4581SMartin Brandenburg 	struct dentry *dentry;
179382f4581SMartin Brandenburg 	int r;
1805db11c21SMike Marshall 
181382f4581SMartin Brandenburg 	dentry = file->f_path.dentry;
182382f4581SMartin Brandenburg 	oi = ORANGEFS_I(dentry->d_inode);
183382f4581SMartin Brandenburg 	od = file->private_data;
1845db11c21SMike Marshall 
185382f4581SMartin Brandenburg 	if (od->error)
186382f4581SMartin Brandenburg 		return od->error;
1879f5e2f7fSAl Viro 
188382f4581SMartin Brandenburg 	if (ctx->pos == 0) {
189382f4581SMartin Brandenburg 		if (!dir_emit_dot(file, ctx))
190382f4581SMartin Brandenburg 			return 0;
1915db11c21SMike Marshall 		ctx->pos++;
192382f4581SMartin Brandenburg 	}
193382f4581SMartin Brandenburg 	if (ctx->pos == 1) {
194382f4581SMartin Brandenburg 		if (!dir_emit_dotdot(file, ctx))
195382f4581SMartin Brandenburg 			return 0;
196382f4581SMartin Brandenburg 		ctx->pos++;
1975db11c21SMike Marshall 	}
1985db11c21SMike Marshall 
199382f4581SMartin Brandenburg 	r = 0;
200382f4581SMartin Brandenburg 
20172f66b83SMartin Brandenburg 	/*
20272f66b83SMartin Brandenburg 	 * Must read more if the user has sought past what has been read
20372f66b83SMartin Brandenburg 	 * so far.  Stop a user who has sought past the end.
20472f66b83SMartin Brandenburg 	 */
20572f66b83SMartin Brandenburg 	while (od->token != ORANGEFS_READDIR_END && ctx->pos - 2 >
20672f66b83SMartin Brandenburg 	    od->len) {
20772f66b83SMartin Brandenburg 		r = orangefs_dir_more(oi, od, dentry);
20872f66b83SMartin Brandenburg 		if (r)
20972f66b83SMartin Brandenburg 			return r;
21072f66b83SMartin Brandenburg 	}
21172f66b83SMartin Brandenburg 	if (od->token == ORANGEFS_READDIR_END && ctx->pos - 2 >
21272f66b83SMartin Brandenburg 	    od->len) {
21372f66b83SMartin Brandenburg 		return -EIO;
21472f66b83SMartin Brandenburg 	}
21572f66b83SMartin Brandenburg 
21672f66b83SMartin Brandenburg 	/* Then try to fill if there's any left in the buffer. */
21772f66b83SMartin Brandenburg 	if (ctx->pos - 2 < od->len) {
218382f4581SMartin Brandenburg 		r = orangefs_dir_fill(oi, od, dentry, ctx);
219382f4581SMartin Brandenburg 		if (r)
220382f4581SMartin Brandenburg 			return r;
2215db11c21SMike Marshall 	}
2225db11c21SMike Marshall 
22372f66b83SMartin Brandenburg 	/* Finally get some more and try to fill. */
224382f4581SMartin Brandenburg 	if (od->token != ORANGEFS_READDIR_END) {
225382f4581SMartin Brandenburg 		r = orangefs_dir_more(oi, od, dentry);
226382f4581SMartin Brandenburg 		if (r)
227382f4581SMartin Brandenburg 			return r;
228382f4581SMartin Brandenburg 		r = orangefs_dir_fill(oi, od, dentry, ctx);
2295db11c21SMike Marshall 	}
2305db11c21SMike Marshall 
231382f4581SMartin Brandenburg 	return r;
2325db11c21SMike Marshall }
2335db11c21SMike Marshall 
2348bb8aefdSYi Liu static int orangefs_dir_open(struct inode *inode, struct file *file)
2355db11c21SMike Marshall {
236382f4581SMartin Brandenburg 	struct orangefs_dir *od;
237382f4581SMartin Brandenburg 	file->private_data = kmalloc(sizeof(struct orangefs_dir),
238382f4581SMartin Brandenburg 	    GFP_KERNEL);
2395db11c21SMike Marshall 	if (!file->private_data)
2405db11c21SMike Marshall 		return -ENOMEM;
241382f4581SMartin Brandenburg 	od = file->private_data;
242382f4581SMartin Brandenburg 	od->token = ORANGEFS_READDIR_START;
243382f4581SMartin Brandenburg 	/*
244382f4581SMartin Brandenburg 	 * XXX: It seems wasteful to allocate such a large buffer for
245382f4581SMartin Brandenburg 	 * each request.  Most will be much smaller.
246382f4581SMartin Brandenburg 	 */
247382f4581SMartin Brandenburg 	od->directory = alloc_pages_exact(MAX_DIRECTORY, GFP_KERNEL);
248382f4581SMartin Brandenburg 	if (!od->directory) {
249382f4581SMartin Brandenburg 		kfree(file->private_data);
250382f4581SMartin Brandenburg 		return -ENOMEM;
251382f4581SMartin Brandenburg 	}
252382f4581SMartin Brandenburg 	od->len = 0;
253382f4581SMartin Brandenburg 	od->error = 0;
2545db11c21SMike Marshall 	return 0;
2555db11c21SMike Marshall }
2565db11c21SMike Marshall 
2578bb8aefdSYi Liu static int orangefs_dir_release(struct inode *inode, struct file *file)
2585db11c21SMike Marshall {
259382f4581SMartin Brandenburg 	struct orangefs_dir *od = file->private_data;
2608bb8aefdSYi Liu 	orangefs_flush_inode(inode);
261382f4581SMartin Brandenburg 	free_pages_exact(od->directory, MAX_DIRECTORY);
262382f4581SMartin Brandenburg 	kfree(od);
2635db11c21SMike Marshall 	return 0;
2645db11c21SMike Marshall }
2655db11c21SMike Marshall 
2668bb8aefdSYi Liu const struct file_operations orangefs_dir_operations = {
26772f66b83SMartin Brandenburg 	.llseek = default_llseek,
2685db11c21SMike Marshall 	.read = generic_read_dir,
269382f4581SMartin Brandenburg 	.iterate = orangefs_dir_iterate,
2708bb8aefdSYi Liu 	.open = orangefs_dir_open,
271382f4581SMartin Brandenburg 	.release = orangefs_dir_release
2725db11c21SMike Marshall };
273