]>
Commit | Line | Data |
---|---|---|
5db11c21 | 1 | /* |
382f4581 | 2 | * Copyright 2017 Omnibond Systems, L.L.C. |
5db11c21 MM |
3 | */ |
4 | ||
5 | #include "protocol.h" | |
575e9461 MM |
6 | #include "orangefs-kernel.h" |
7 | #include "orangefs-bufmap.h" | |
5db11c21 | 8 | |
5db11c21 | 9 | /* |
382f4581 MB |
10 | * There can be up to 512 directory entries. Each entry is encoded as |
11 | * follows: | |
12 | * 4 bytes: string size (n) | |
13 | * n bytes: string | |
14 | * 1 byte: trailing zero | |
15 | * padding to 8 bytes | |
16 | * 16 bytes: khandle | |
17 | * padding to 8 bytes | |
5db11c21 | 18 | */ |
382f4581 | 19 | #define MAX_DIRECTORY ((4 + 257 + 3 + 16)*512) |
1808f8cc | 20 | |
382f4581 MB |
21 | struct orangefs_dir { |
22 | __u64 token; | |
23 | void *directory; | |
24 | size_t i, len; | |
25 | int error; | |
26 | }; | |
5db11c21 | 27 | |
5db11c21 | 28 | /* |
382f4581 MB |
29 | * The userspace component sends several directory entries of the |
30 | * following format. The first four bytes are the string length not | |
31 | * including a trailing zero byte. This is followed by the string and a | |
32 | * trailing zero padded to the next four byte boundry. This is followed | |
33 | * by the sixteen byte khandle padded to the next eight byte boundry. | |
34 | * | |
35 | * The trailer_buf starts with a struct orangefs_readdir_response_s | |
36 | * which must be skipped to get to the directory data. | |
5db11c21 | 37 | */ |
5db11c21 | 38 | |
382f4581 MB |
39 | static int orangefs_dir_more(struct orangefs_inode_s *oi, |
40 | struct orangefs_dir *od, struct dentry *dentry) | |
41 | { | |
42 | const size_t offset = | |
43 | sizeof(struct orangefs_readdir_response_s); | |
44 | struct orangefs_readdir_response_s *resp; | |
45 | struct orangefs_kernel_op_s *op; | |
46 | int bufi, r; | |
47 | ||
48 | op = op_alloc(ORANGEFS_VFS_OP_READDIR); | |
49 | if (!op) { | |
50 | od->error = -ENOMEM; | |
5db11c21 | 51 | return -ENOMEM; |
382f4581 | 52 | } |
5db11c21 | 53 | |
ee3b8d37 | 54 | /* |
382f4581 MB |
55 | * Despite the badly named field, readdir does not use shared |
56 | * memory. However, there are a limited number of readdir | |
57 | * slots, which must be allocated here. This flag simply tells | |
58 | * the op scheduler to return the op here for retry. | |
ee3b8d37 | 59 | */ |
382f4581 MB |
60 | op->uses_shared_memory = 1; |
61 | op->upcall.req.readdir.refn = oi->refn; | |
62 | op->upcall.req.readdir.token = od->token; | |
63 | op->upcall.req.readdir.max_dirent_count = | |
7d221485 | 64 | ORANGEFS_MAX_DIRENT_COUNT_READDIR; |
5db11c21 | 65 | |
382f4581 MB |
66 | again: |
67 | bufi = orangefs_readdir_index_get(); | |
68 | if (bufi < 0) { | |
69 | op_release(op); | |
70 | od->error = bufi; | |
71 | return bufi; | |
5db11c21 | 72 | } |
5db11c21 | 73 | |
382f4581 | 74 | op->upcall.req.readdir.buf_index = bufi; |
5db11c21 | 75 | |
382f4581 MB |
76 | r = service_operation(op, "orangefs_readdir", |
77 | get_interruptible_flag(dentry->d_inode)); | |
5db11c21 | 78 | |
382f4581 | 79 | orangefs_readdir_index_put(bufi); |
ee3b8d37 | 80 | |
382f4581 MB |
81 | if (op_state_purged(op)) { |
82 | if (r == -EAGAIN) { | |
83 | vfree(op->downcall.trailer_buf); | |
84 | goto again; | |
85 | } else if (r == -EIO) { | |
86 | vfree(op->downcall.trailer_buf); | |
87 | op_release(op); | |
88 | od->error = r; | |
89 | return r; | |
90 | } | |
5db11c21 MM |
91 | } |
92 | ||
382f4581 MB |
93 | if (r < 0) { |
94 | vfree(op->downcall.trailer_buf); | |
95 | op_release(op); | |
96 | od->error = r; | |
97 | return r; | |
98 | } else if (op->downcall.status) { | |
99 | vfree(op->downcall.trailer_buf); | |
100 | op_release(op); | |
101 | od->error = op->downcall.status; | |
102 | return op->downcall.status; | |
103 | } | |
104 | ||
105 | resp = (struct orangefs_readdir_response_s *) | |
106 | op->downcall.trailer_buf; | |
107 | od->token = resp->token; | |
108 | ||
109 | if (od->len + op->downcall.trailer_size - offset <= | |
110 | MAX_DIRECTORY) { | |
111 | memcpy(od->directory + od->len, | |
112 | op->downcall.trailer_buf + offset, | |
113 | op->downcall.trailer_size - offset); | |
114 | od->len += op->downcall.trailer_size - offset; | |
115 | } else { | |
116 | /* This limit was chosen based on protocol limits. */ | |
117 | gossip_err("orangefs_dir_more: userspace sent too much data\n"); | |
118 | vfree(op->downcall.trailer_buf); | |
119 | op_release(op); | |
120 | od->error = -EIO; | |
121 | return -EIO; | |
122 | } | |
123 | ||
124 | vfree(op->downcall.trailer_buf); | |
125 | op_release(op); | |
126 | return 0; | |
127 | } | |
9f5e2f7f | 128 | |
382f4581 MB |
129 | static int orangefs_dir_fill(struct orangefs_inode_s *oi, |
130 | struct orangefs_dir *od, struct dentry *dentry, | |
131 | struct dir_context *ctx) | |
132 | { | |
133 | struct orangefs_khandle *khandle; | |
134 | __u32 *len, padlen; | |
135 | char *s; | |
136 | while (od->i < od->len) { | |
137 | if (od->len < od->i + sizeof *len) | |
138 | goto eio; | |
139 | len = od->directory + od->i; | |
140 | /* | |
141 | * len is the size of the string itself. padlen is the | |
142 | * total size of the encoded string. | |
143 | */ | |
144 | padlen = (sizeof *len + *len + 1) + | |
145 | (4 - (sizeof *len + *len + 1)%8)%8; | |
146 | if (od->len < od->i + padlen + sizeof *khandle) | |
147 | goto eio; | |
148 | s = od->directory + od->i + sizeof *len; | |
149 | if (s[*len] != 0) | |
150 | goto eio; | |
151 | khandle = od->directory + od->i + padlen; | |
152 | ||
153 | if (!dir_emit(ctx, s, *len, | |
154 | orangefs_khandle_to_ino(khandle), DT_UNKNOWN)) | |
155 | return 0; | |
156 | od->i += padlen + sizeof *khandle; | |
157 | od->i = od->i + (8 - od->i%8)%8; | |
158 | ctx->pos = 2 + od->i; | |
159 | } | |
160 | BUG_ON(od->i > od->len); | |
161 | return 0; | |
162 | eio: | |
163 | gossip_err("orangefs_dir_fill: userspace returns corrupt data\n"); | |
164 | od->error = -EIO; | |
165 | return -EIO; | |
166 | } | |
5db11c21 | 167 | |
382f4581 MB |
168 | static int orangefs_dir_iterate(struct file *file, |
169 | struct dir_context *ctx) | |
170 | { | |
171 | struct orangefs_inode_s *oi; | |
172 | struct orangefs_dir *od; | |
173 | struct dentry *dentry; | |
174 | int r; | |
5db11c21 | 175 | |
382f4581 MB |
176 | dentry = file->f_path.dentry; |
177 | oi = ORANGEFS_I(dentry->d_inode); | |
178 | od = file->private_data; | |
5db11c21 | 179 | |
382f4581 MB |
180 | if (od->error) |
181 | return od->error; | |
5db11c21 | 182 | |
382f4581 MB |
183 | if (ctx->pos == 0) { |
184 | if (!dir_emit_dot(file, ctx)) | |
185 | return 0; | |
186 | ctx->pos++; | |
5db11c21 | 187 | } |
382f4581 MB |
188 | if (ctx->pos == 1) { |
189 | if (!dir_emit_dotdot(file, ctx)) | |
190 | return 0; | |
5db11c21 | 191 | ctx->pos++; |
5db11c21 MM |
192 | } |
193 | ||
382f4581 MB |
194 | r = 0; |
195 | ||
196 | if (od->i < od->len) { | |
197 | r = orangefs_dir_fill(oi, od, dentry, ctx); | |
198 | if (r) | |
199 | return r; | |
5db11c21 MM |
200 | } |
201 | ||
382f4581 MB |
202 | if (od->token != ORANGEFS_READDIR_END) { |
203 | r = orangefs_dir_more(oi, od, dentry); | |
204 | if (r) | |
205 | return r; | |
206 | r = orangefs_dir_fill(oi, od, dentry, ctx); | |
5db11c21 MM |
207 | } |
208 | ||
382f4581 | 209 | return r; |
5db11c21 MM |
210 | } |
211 | ||
8bb8aefd | 212 | static int orangefs_dir_open(struct inode *inode, struct file *file) |
5db11c21 | 213 | { |
382f4581 MB |
214 | struct orangefs_dir *od; |
215 | file->private_data = kmalloc(sizeof(struct orangefs_dir), | |
216 | GFP_KERNEL); | |
5db11c21 MM |
217 | if (!file->private_data) |
218 | return -ENOMEM; | |
382f4581 MB |
219 | od = file->private_data; |
220 | od->token = ORANGEFS_READDIR_START; | |
221 | /* | |
222 | * XXX: It seems wasteful to allocate such a large buffer for | |
223 | * each request. Most will be much smaller. | |
224 | */ | |
225 | od->directory = alloc_pages_exact(MAX_DIRECTORY, GFP_KERNEL); | |
226 | if (!od->directory) { | |
227 | kfree(file->private_data); | |
228 | return -ENOMEM; | |
229 | } | |
230 | od->i = 0; | |
231 | od->len = 0; | |
232 | od->error = 0; | |
5db11c21 MM |
233 | return 0; |
234 | } | |
235 | ||
8bb8aefd | 236 | static int orangefs_dir_release(struct inode *inode, struct file *file) |
5db11c21 | 237 | { |
382f4581 | 238 | struct orangefs_dir *od = file->private_data; |
8bb8aefd | 239 | orangefs_flush_inode(inode); |
382f4581 MB |
240 | free_pages_exact(od->directory, MAX_DIRECTORY); |
241 | kfree(od); | |
5db11c21 MM |
242 | return 0; |
243 | } | |
244 | ||
8bb8aefd | 245 | const struct file_operations orangefs_dir_operations = { |
5db11c21 | 246 | .read = generic_read_dir, |
382f4581 | 247 | .iterate = orangefs_dir_iterate, |
8bb8aefd | 248 | .open = orangefs_dir_open, |
382f4581 | 249 | .release = orangefs_dir_release |
5db11c21 | 250 | }; |