Commit | Line | Data |
---|---|---|
5db11c21 MM |
1 | /* |
2 | * (C) 2001 Clemson University and The University of Chicago | |
3 | * | |
4 | * See COPYING in top-level directory. | |
5 | */ | |
6 | ||
7 | #include "protocol.h" | |
575e9461 MM |
8 | #include "orangefs-kernel.h" |
9 | #include "orangefs-bufmap.h" | |
5db11c21 MM |
10 | |
11 | struct readdir_handle_s { | |
12 | int buffer_index; | |
8bb8aefd | 13 | struct orangefs_readdir_response_s readdir_response; |
5db11c21 MM |
14 | void *dents_buf; |
15 | }; | |
16 | ||
17 | /* | |
18 | * decode routine needed by kmod to make sense of the shared page for readdirs. | |
19 | */ | |
8092895f | 20 | static long decode_dirents(char *ptr, size_t size, |
8bb8aefd | 21 | struct orangefs_readdir_response_s *readdir) |
5db11c21 MM |
22 | { |
23 | int i; | |
8bb8aefd YL |
24 | struct orangefs_readdir_response_s *rd = |
25 | (struct orangefs_readdir_response_s *) ptr; | |
5db11c21 | 26 | char *buf = ptr; |
5db11c21 | 27 | |
8bb8aefd | 28 | if (size < offsetof(struct orangefs_readdir_response_s, dirent_array)) |
8092895f AV |
29 | return -EINVAL; |
30 | ||
5db11c21 | 31 | readdir->token = rd->token; |
8bb8aefd YL |
32 | readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount; |
33 | readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount, | |
5db11c21 MM |
34 | sizeof(*readdir->dirent_array), |
35 | GFP_KERNEL); | |
36 | if (readdir->dirent_array == NULL) | |
37 | return -ENOMEM; | |
8092895f | 38 | |
8bb8aefd YL |
39 | buf += offsetof(struct orangefs_readdir_response_s, dirent_array); |
40 | size -= offsetof(struct orangefs_readdir_response_s, dirent_array); | |
8092895f | 41 | |
8bb8aefd | 42 | for (i = 0; i < readdir->orangefs_dirent_outcount; i++) { |
8092895f AV |
43 | __u32 len; |
44 | ||
45 | if (size < 4) | |
46 | goto Einval; | |
47 | ||
48 | len = *(__u32 *)buf; | |
49 | if (len >= (unsigned)-24) | |
50 | goto Einval; | |
51 | ||
9be68b08 | 52 | readdir->dirent_array[i].d_name = buf + 4; |
9be68b08 | 53 | readdir->dirent_array[i].d_length = len; |
8092895f | 54 | |
7d221485 MB |
55 | /* |
56 | * Round 4 + len + 1, which is the encoded size plus the string | |
57 | * plus the null terminator to the nearest eight byte boundry. | |
58 | */ | |
59 | len = ((4 + len + 1) + 7) & ~7; | |
8092895f AV |
60 | if (size < len + 16) |
61 | goto Einval; | |
62 | size -= len + 16; | |
63 | ||
64 | buf += len; | |
65 | ||
5db11c21 | 66 | readdir->dirent_array[i].khandle = |
8bb8aefd | 67 | *(struct orangefs_khandle *) buf; |
9be68b08 | 68 | buf += 16; |
5db11c21 | 69 | } |
9be68b08 | 70 | return buf - ptr; |
8092895f AV |
71 | Einval: |
72 | kfree(readdir->dirent_array); | |
73 | readdir->dirent_array = NULL; | |
74 | return -EINVAL; | |
5db11c21 MM |
75 | } |
76 | ||
77 | static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf, | |
8092895f | 78 | size_t size, int buffer_index) |
5db11c21 MM |
79 | { |
80 | long ret; | |
81 | ||
82 | if (buf == NULL) { | |
83 | gossip_err | |
84 | ("Invalid NULL buffer specified in readdir_handle_ctor\n"); | |
85 | return -ENOMEM; | |
86 | } | |
87 | if (buffer_index < 0) { | |
88 | gossip_err | |
89 | ("Invalid buffer index specified in readdir_handle_ctor\n"); | |
90 | return -EINVAL; | |
91 | } | |
92 | rhandle->buffer_index = buffer_index; | |
93 | rhandle->dents_buf = buf; | |
8092895f | 94 | ret = decode_dirents(buf, size, &rhandle->readdir_response); |
5db11c21 MM |
95 | if (ret < 0) { |
96 | gossip_err("Could not decode readdir from buffer %ld\n", ret); | |
97 | rhandle->buffer_index = -1; | |
98 | gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", buf); | |
99 | vfree(buf); | |
100 | rhandle->dents_buf = NULL; | |
101 | } | |
102 | return ret; | |
103 | } | |
104 | ||
8bb8aefd | 105 | static void readdir_handle_dtor(struct orangefs_bufmap *bufmap, |
5db11c21 MM |
106 | struct readdir_handle_s *rhandle) |
107 | { | |
108 | if (rhandle == NULL) | |
109 | return; | |
110 | ||
111 | /* kfree(NULL) is safe */ | |
112 | kfree(rhandle->readdir_response.dirent_array); | |
113 | rhandle->readdir_response.dirent_array = NULL; | |
114 | ||
115 | if (rhandle->buffer_index >= 0) { | |
7d221485 | 116 | orangefs_readdir_index_put(bufmap, rhandle->buffer_index); |
5db11c21 MM |
117 | rhandle->buffer_index = -1; |
118 | } | |
119 | if (rhandle->dents_buf) { | |
120 | gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", | |
121 | rhandle->dents_buf); | |
122 | vfree(rhandle->dents_buf); | |
123 | rhandle->dents_buf = NULL; | |
124 | } | |
125 | } | |
126 | ||
127 | /* | |
128 | * Read directory entries from an instance of an open directory. | |
5db11c21 | 129 | */ |
8bb8aefd | 130 | static int orangefs_readdir(struct file *file, struct dir_context *ctx) |
5db11c21 | 131 | { |
8bb8aefd | 132 | struct orangefs_bufmap *bufmap = NULL; |
5db11c21 MM |
133 | int ret = 0; |
134 | int buffer_index; | |
88309aae MM |
135 | /* |
136 | * ptoken supports Orangefs' distributed directory logic, added | |
137 | * in 2.9.2. | |
138 | */ | |
5db11c21 MM |
139 | __u64 *ptoken = file->private_data; |
140 | __u64 pos = 0; | |
141 | ino_t ino = 0; | |
142 | struct dentry *dentry = file->f_path.dentry; | |
8bb8aefd YL |
143 | struct orangefs_kernel_op_s *new_op = NULL; |
144 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(dentry->d_inode); | |
5db11c21 MM |
145 | int buffer_full = 0; |
146 | struct readdir_handle_s rhandle; | |
147 | int i = 0; | |
148 | int len = 0; | |
149 | ino_t current_ino = 0; | |
150 | char *current_entry = NULL; | |
151 | long bytes_decoded; | |
152 | ||
88309aae MM |
153 | gossip_debug(GOSSIP_DIR_DEBUG, |
154 | "%s: ctx->pos:%lld, ptoken = %llu\n", | |
155 | __func__, | |
156 | lld(ctx->pos), | |
157 | llu(*ptoken)); | |
5db11c21 MM |
158 | |
159 | pos = (__u64) ctx->pos; | |
160 | ||
161 | /* are we done? */ | |
8bb8aefd | 162 | if (pos == ORANGEFS_READDIR_END) { |
5db11c21 MM |
163 | gossip_debug(GOSSIP_DIR_DEBUG, |
164 | "Skipping to termination path\n"); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | gossip_debug(GOSSIP_DIR_DEBUG, | |
8bb8aefd | 169 | "orangefs_readdir called on %s (pos=%llu)\n", |
5db11c21 MM |
170 | dentry->d_name.name, llu(pos)); |
171 | ||
172 | rhandle.buffer_index = -1; | |
173 | rhandle.dents_buf = NULL; | |
174 | memset(&rhandle.readdir_response, 0, sizeof(rhandle.readdir_response)); | |
175 | ||
8bb8aefd | 176 | new_op = op_alloc(ORANGEFS_VFS_OP_READDIR); |
5db11c21 MM |
177 | if (!new_op) |
178 | return -ENOMEM; | |
179 | ||
180 | new_op->uses_shared_memory = 1; | |
8bb8aefd | 181 | new_op->upcall.req.readdir.refn = orangefs_inode->refn; |
7d221485 MB |
182 | new_op->upcall.req.readdir.max_dirent_count = |
183 | ORANGEFS_MAX_DIRENT_COUNT_READDIR; | |
5db11c21 MM |
184 | |
185 | gossip_debug(GOSSIP_DIR_DEBUG, | |
186 | "%s: upcall.req.readdir.refn.khandle: %pU\n", | |
187 | __func__, | |
188 | &new_op->upcall.req.readdir.refn.khandle); | |
189 | ||
5db11c21 MM |
190 | new_op->upcall.req.readdir.token = *ptoken; |
191 | ||
192 | get_new_buffer_index: | |
7d221485 | 193 | ret = orangefs_readdir_index_get(&bufmap, &buffer_index); |
5db11c21 | 194 | if (ret < 0) { |
7d221485 | 195 | gossip_lerr("orangefs_readdir: orangefs_readdir_index_get() failure (%d)\n", |
5db11c21 MM |
196 | ret); |
197 | goto out_free_op; | |
198 | } | |
199 | new_op->upcall.req.readdir.buf_index = buffer_index; | |
200 | ||
201 | ret = service_operation(new_op, | |
8bb8aefd | 202 | "orangefs_readdir", |
5db11c21 MM |
203 | get_interruptible_flag(dentry->d_inode)); |
204 | ||
205 | gossip_debug(GOSSIP_DIR_DEBUG, | |
206 | "Readdir downcall status is %d. ret:%d\n", | |
207 | new_op->downcall.status, | |
208 | ret); | |
209 | ||
210 | if (ret == -EAGAIN && op_state_purged(new_op)) { | |
211 | /* | |
212 | * readdir shared memory aread has been wiped due to | |
213 | * pvfs2-client-core restarting, so we must get a new | |
214 | * index into the shared memory. | |
215 | */ | |
216 | gossip_debug(GOSSIP_DIR_DEBUG, | |
217 | "%s: Getting new buffer_index for retry of readdir..\n", | |
218 | __func__); | |
7d221485 | 219 | orangefs_readdir_index_put(bufmap, buffer_index); |
5db11c21 MM |
220 | goto get_new_buffer_index; |
221 | } | |
222 | ||
223 | if (ret == -EIO && op_state_purged(new_op)) { | |
224 | gossip_err("%s: Client is down. Aborting readdir call.\n", | |
225 | __func__); | |
7d221485 | 226 | orangefs_readdir_index_put(bufmap, buffer_index); |
5db11c21 MM |
227 | goto out_free_op; |
228 | } | |
229 | ||
230 | if (ret < 0 || new_op->downcall.status != 0) { | |
231 | gossip_debug(GOSSIP_DIR_DEBUG, | |
232 | "Readdir request failed. Status:%d\n", | |
233 | new_op->downcall.status); | |
7d221485 | 234 | orangefs_readdir_index_put(bufmap, buffer_index); |
5db11c21 MM |
235 | if (ret >= 0) |
236 | ret = new_op->downcall.status; | |
237 | goto out_free_op; | |
238 | } | |
239 | ||
240 | bytes_decoded = | |
241 | readdir_handle_ctor(&rhandle, | |
242 | new_op->downcall.trailer_buf, | |
8092895f | 243 | new_op->downcall.trailer_size, |
5db11c21 MM |
244 | buffer_index); |
245 | if (bytes_decoded < 0) { | |
8bb8aefd | 246 | gossip_err("orangefs_readdir: Could not decode trailer buffer into a readdir response %d\n", |
5db11c21 MM |
247 | ret); |
248 | ret = bytes_decoded; | |
7d221485 | 249 | orangefs_readdir_index_put(bufmap, buffer_index); |
5db11c21 MM |
250 | goto out_free_op; |
251 | } | |
252 | ||
253 | if (bytes_decoded != new_op->downcall.trailer_size) { | |
8bb8aefd | 254 | gossip_err("orangefs_readdir: # bytes decoded (%ld) " |
88309aae MM |
255 | "!= trailer size (%ld)\n", |
256 | bytes_decoded, | |
257 | (long)new_op->downcall.trailer_size); | |
5db11c21 MM |
258 | ret = -EINVAL; |
259 | goto out_destroy_handle; | |
260 | } | |
261 | ||
88309aae | 262 | /* |
8bb8aefd | 263 | * orangefs doesn't actually store dot and dot-dot, but |
88309aae MM |
264 | * we need to have them represented. |
265 | */ | |
5db11c21 MM |
266 | if (pos == 0) { |
267 | ino = get_ino_from_khandle(dentry->d_inode); | |
268 | gossip_debug(GOSSIP_DIR_DEBUG, | |
269 | "%s: calling dir_emit of \".\" with pos = %llu\n", | |
270 | __func__, | |
271 | llu(pos)); | |
272 | ret = dir_emit(ctx, ".", 1, ino, DT_DIR); | |
88309aae | 273 | pos += 1; |
5db11c21 MM |
274 | } |
275 | ||
276 | if (pos == 1) { | |
277 | ino = get_parent_ino_from_dentry(dentry); | |
278 | gossip_debug(GOSSIP_DIR_DEBUG, | |
279 | "%s: calling dir_emit of \"..\" with pos = %llu\n", | |
280 | __func__, | |
281 | llu(pos)); | |
282 | ret = dir_emit(ctx, "..", 2, ino, DT_DIR); | |
88309aae | 283 | pos += 1; |
5db11c21 MM |
284 | } |
285 | ||
88309aae | 286 | /* |
8bb8aefd | 287 | * we stored ORANGEFS_ITERATE_NEXT in ctx->pos last time around |
88309aae MM |
288 | * to prevent "finding" dot and dot-dot on any iteration |
289 | * other than the first. | |
290 | */ | |
8bb8aefd | 291 | if (ctx->pos == ORANGEFS_ITERATE_NEXT) |
88309aae MM |
292 | ctx->pos = 0; |
293 | ||
294 | for (i = ctx->pos; | |
8bb8aefd | 295 | i < rhandle.readdir_response.orangefs_dirent_outcount; |
88309aae | 296 | i++) { |
5db11c21 MM |
297 | len = rhandle.readdir_response.dirent_array[i].d_length; |
298 | current_entry = rhandle.readdir_response.dirent_array[i].d_name; | |
8bb8aefd | 299 | current_ino = orangefs_khandle_to_ino( |
5db11c21 MM |
300 | &(rhandle.readdir_response.dirent_array[i].khandle)); |
301 | ||
302 | gossip_debug(GOSSIP_DIR_DEBUG, | |
88309aae MM |
303 | "calling dir_emit for %s with len %d" |
304 | ", ctx->pos %ld\n", | |
5db11c21 MM |
305 | current_entry, |
306 | len, | |
88309aae MM |
307 | (unsigned long)ctx->pos); |
308 | /* | |
309 | * type is unknown. We don't return object type | |
310 | * in the dirent_array. This leaves getdents | |
311 | * clueless about type. | |
312 | */ | |
5db11c21 MM |
313 | ret = |
314 | dir_emit(ctx, current_entry, len, current_ino, DT_UNKNOWN); | |
88309aae MM |
315 | if (!ret) |
316 | break; | |
5db11c21 | 317 | ctx->pos++; |
88309aae | 318 | gossip_debug(GOSSIP_DIR_DEBUG, |
5db11c21 MM |
319 | "%s: ctx->pos:%lld\n", |
320 | __func__, | |
321 | lld(ctx->pos)); | |
322 | ||
5db11c21 MM |
323 | } |
324 | ||
54804949 | 325 | /* |
88309aae MM |
326 | * we ran all the way through the last batch, set up for |
327 | * getting another batch... | |
328 | */ | |
329 | if (ret) { | |
5db11c21 | 330 | *ptoken = rhandle.readdir_response.token; |
8bb8aefd | 331 | ctx->pos = ORANGEFS_ITERATE_NEXT; |
5db11c21 MM |
332 | } |
333 | ||
334 | /* | |
335 | * Did we hit the end of the directory? | |
336 | */ | |
8bb8aefd | 337 | if (rhandle.readdir_response.token == ORANGEFS_READDIR_END && |
5db11c21 | 338 | !buffer_full) { |
88309aae | 339 | gossip_debug(GOSSIP_DIR_DEBUG, |
8bb8aefd YL |
340 | "End of dir detected; setting ctx->pos to ORANGEFS_READDIR_END.\n"); |
341 | ctx->pos = ORANGEFS_READDIR_END; | |
5db11c21 MM |
342 | } |
343 | ||
5db11c21 MM |
344 | out_destroy_handle: |
345 | readdir_handle_dtor(bufmap, &rhandle); | |
346 | out_free_op: | |
347 | op_release(new_op); | |
8bb8aefd | 348 | gossip_debug(GOSSIP_DIR_DEBUG, "orangefs_readdir returning %d\n", ret); |
5db11c21 MM |
349 | return ret; |
350 | } | |
351 | ||
8bb8aefd | 352 | static int orangefs_dir_open(struct inode *inode, struct file *file) |
5db11c21 MM |
353 | { |
354 | __u64 *ptoken; | |
355 | ||
356 | file->private_data = kmalloc(sizeof(__u64), GFP_KERNEL); | |
357 | if (!file->private_data) | |
358 | return -ENOMEM; | |
359 | ||
360 | ptoken = file->private_data; | |
8bb8aefd | 361 | *ptoken = ORANGEFS_READDIR_START; |
5db11c21 MM |
362 | return 0; |
363 | } | |
364 | ||
8bb8aefd | 365 | static int orangefs_dir_release(struct inode *inode, struct file *file) |
5db11c21 | 366 | { |
8bb8aefd | 367 | orangefs_flush_inode(inode); |
5db11c21 MM |
368 | kfree(file->private_data); |
369 | return 0; | |
370 | } | |
371 | ||
8bb8aefd YL |
372 | /** ORANGEFS implementation of VFS directory operations */ |
373 | const struct file_operations orangefs_dir_operations = { | |
5db11c21 | 374 | .read = generic_read_dir, |
8bb8aefd YL |
375 | .iterate = orangefs_readdir, |
376 | .open = orangefs_dir_open, | |
377 | .release = orangefs_dir_release, | |
5db11c21 | 378 | }; |