1 |
1 |
2 #include <stdlib.h> |
2 #include <stdlib.h> |
3 #include <assert.h> |
3 #include <assert.h> |
4 |
4 |
5 #include "common.h" |
5 #include "dbfs.h" |
6 #include "ops.h" |
6 #include "op_base.h" |
7 #include "../dirbuf.h" |
7 #include "../dirbuf.h" |
|
8 #include "../lib/log.h" |
8 |
9 |
9 /* |
10 /* |
10 * Directory related functionality like opendir, readdir, releasedir |
11 * Directory related functionality like opendir, readdir, releasedir |
11 */ |
12 */ |
12 |
|
13 struct dbfs_dirop { |
13 struct dbfs_dirop { |
14 struct fuse_file_info fi; |
14 struct dbfs_op base; |
15 struct fuse_req *req; |
15 |
16 |
16 // parent dir inodes |
17 struct evsql_trans *trans; |
17 uint32_t parent; |
18 |
18 |
19 // dir/parent dir inodes |
|
20 uint32_t ino, parent; |
|
21 |
|
22 // opendir has returned and releasedir hasn't been called yet |
|
23 int open; |
|
24 |
|
25 // for readdir |
19 // for readdir |
26 struct dirbuf dirbuf; |
20 struct dirbuf dirbuf; |
27 }; |
21 }; |
28 |
22 |
29 /* |
23 /* |
30 * Free the dirop, aborting any in-progress transaction. |
24 * Release the dirbuf. |
31 * |
25 */ |
32 * The dirop must any oustanding request responded to first, must not be open, and must not have a transaction. |
26 static void _dbfs_dirop_free (struct dbfs_op *op_base) { |
33 * |
27 struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base; |
34 * The dirbuf will be released, and the dirop free'd. |
28 |
35 */ |
|
36 static void _dbfs_dirop_free (struct dbfs_dirop *dirop) { |
|
37 assert(dirop); |
|
38 assert(!dirop->open); |
|
39 assert(!dirop->req); |
|
40 assert(!dirop->trans); |
|
41 |
|
42 // just release the dirbuf |
29 // just release the dirbuf |
43 dirbuf_release(&dirop->dirbuf); |
30 dirbuf_release(&dirop->dirbuf); |
44 |
|
45 // and then free the dirop |
|
46 free(dirop); |
|
47 } |
|
48 |
|
49 /* |
|
50 * This will handle backend failures during requests. |
|
51 * |
|
52 * 1) if we have a trans, abort it |
|
53 * 2) fail the req (mandatory) |
|
54 * |
|
55 * If the dirop is open, then we don't release it, but if it's not open, then the dirop will be free'd completely. |
|
56 * |
|
57 */ |
|
58 static void _dbfs_dirop_fail (struct dbfs_dirop *dirop) { |
|
59 int err; |
|
60 |
|
61 assert(dirop->req); |
|
62 |
|
63 if (dirop->trans) { |
|
64 // abort the trans |
|
65 evsql_trans_abort(dirop->trans); |
|
66 |
|
67 dirop->trans = NULL; |
|
68 } |
|
69 |
|
70 // send an error reply |
|
71 if ((err = fuse_reply_err(dirop->req, err))) |
|
72 // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid dirops |
|
73 EFATAL(err, "dbfs.fail %p:%p dirop_fail: reply with fuse_reply_err", dirop, dirop->req); |
|
74 |
|
75 // drop the req |
|
76 dirop->req = NULL; |
|
77 |
|
78 // is it open? |
|
79 if (!dirop->open) { |
|
80 // no, we can free it now and then forget about the whole thing |
|
81 _dbfs_dirop_free(dirop); |
|
82 |
|
83 } else { |
|
84 // we need to wait for releasedir |
|
85 |
|
86 } |
|
87 } |
31 } |
88 |
32 |
89 /* |
33 /* |
90 * Handle the results for the initial attribute lookup for the dir itself during opendir ops. |
34 * Handle the results for the initial attribute lookup for the dir itself during opendir ops. |
91 */ |
35 */ |
92 static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) { |
36 static void dbfs_opendir_res (const struct evsql_result_info *res, void *arg) { |
93 struct dbfs_dirop *dirop = arg; |
37 struct dbfs_dirop *dirop = arg; |
94 int err; |
38 int err; |
95 |
39 |
96 assert(dirop->trans); |
40 assert(dirop->base.req); |
97 assert(dirop->req); |
41 assert(dirop->base.trans); // query callbacks don't get called if the trans fails |
98 assert(!dirop->open); |
42 assert(!dirop->base.open); |
99 |
43 |
100 // check the results |
44 // check the results |
101 if ((err = _dbfs_check_res(res, 1, 2))) |
45 if ((err = _dbfs_check_res(res, 1, 2))) |
102 SERROR(err = (err == 1 ? ENOENT : EIO)); |
46 SERROR(err = (err == 1 ? ENOENT : EIO)); |
103 |
47 |
112 |
56 |
113 // is it a dir? |
57 // is it a dir? |
114 if (_dbfs_mode(type) != S_IFDIR) |
58 if (_dbfs_mode(type) != S_IFDIR) |
115 EERROR(err = ENOTDIR, "wrong type: %s", type); |
59 EERROR(err = ENOTDIR, "wrong type: %s", type); |
116 |
60 |
117 INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, dirop->req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type); |
61 INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, dirop->base.req, (unsigned long int) dirop->base.ino, (unsigned long int) dirop->parent, type); |
118 |
62 |
119 // send the openddir reply |
63 // open_fn done, do the open_reply |
120 if ((err = fuse_reply_open(dirop->req, &dirop->fi))) |
64 if ((err = dbfs_op_open_reply(&dirop->base))) |
121 EERROR(err, "fuse_reply_open"); |
65 goto error; |
122 |
|
123 // req is done |
|
124 dirop->req = NULL; |
|
125 |
|
126 // dirop is now open |
|
127 dirop->open = 1; |
|
128 |
66 |
129 // success, fallthrough for evsql_result_free |
67 // success, fallthrough for evsql_result_free |
130 err = 0; |
68 err = 0; |
131 |
69 |
132 error: |
70 error: |
133 if (err) |
71 if (err) |
134 // fail it |
72 // fail it |
135 _dbfs_dirop_fail(dirop); |
73 dbfs_op_fail(&dirop->base, err); |
136 |
74 |
137 // free |
75 // free |
138 evsql_result_free(res); |
76 evsql_result_free(res); |
139 } |
77 } |
140 |
78 |
141 /* |
79 /* |
142 * The opendir transaction is ready for use. Query for the given dir's info |
80 * The opendir transaction is ready for use. Query for the given dir's info |
143 */ |
81 */ |
144 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) { |
82 static void dbfs_dirop_open (struct dbfs_op *op_base) { |
145 struct dbfs_dirop *dirop = arg; |
83 struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base; |
146 struct dbfs *ctx = fuse_req_userdata(dirop->req); |
84 struct dbfs *ctx = fuse_req_userdata(dirop->base.req); |
147 int err; |
85 int err; |
148 |
86 |
149 // XXX: unless we abort queries |
87 assert(dirop->base.trans); |
150 assert(trans == dirop->trans); |
88 assert(dirop->base.req); |
151 assert(dirop->req); |
89 assert(!dirop->base.open); |
152 assert(!dirop->open); |
90 |
153 |
91 INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, dirop->base.req, dirop->base.trans); |
154 INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, dirop->req, trans); |
|
155 |
|
156 // remember the transaction |
|
157 dirop->trans = trans; |
|
158 |
92 |
159 // first fetch info about the dir itself |
93 // first fetch info about the dir itself |
160 const char *sql = |
94 const char *sql = |
161 "SELECT" |
95 "SELECT" |
162 " file_tree.parent, inodes.type" |
96 " file_tree.parent, inodes.type" |
169 EVSQL_PARAMS_END |
103 EVSQL_PARAMS_END |
170 }; |
104 }; |
171 |
105 |
172 // build params |
106 // build params |
173 if (0 |
107 if (0 |
174 || evsql_param_uint32(¶ms, 0, dirop->ino) |
108 || evsql_param_uint32(¶ms, 0, dirop->base.ino) |
175 ) |
109 ) |
176 SERROR(err = EIO); |
110 SERROR(err = EIO); |
177 |
111 |
178 // query |
112 // query |
179 if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_opendir_info_res, dirop) == NULL) |
113 if (evsql_query_params(ctx->db, dirop->base.trans, sql, ¶ms, dbfs_opendir_res, dirop) == NULL) |
180 SERROR(err = EIO); |
114 SERROR(err = EIO); |
181 |
115 |
182 // ok, wait for the info results |
116 // ok, wait for the info results |
183 return; |
117 return; |
184 |
118 |
185 error: |
119 error: |
186 // fail it |
120 // fail it |
187 _dbfs_dirop_fail(dirop); |
121 dbfs_op_fail(&dirop->base, err); |
188 } |
122 } |
189 |
123 |
190 /* |
124 /* |
191 * The dirop trans was committed, i.e. releasedir has completed |
125 * Handle opendir(), this means starting a new op. |
192 */ |
|
193 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) { |
|
194 struct dbfs_dirop *dirop = arg; |
|
195 int err; |
|
196 |
|
197 assert(dirop->trans); |
|
198 assert(dirop->req); |
|
199 assert(!dirop->open); // should not be considered as open anymore at this point, as errors should release |
|
200 |
|
201 INFO("[dbfs.releasedir %p:%p] -> OK", dirop, dirop->req); |
|
202 |
|
203 // forget trans |
|
204 dirop->trans = NULL; |
|
205 |
|
206 // just reply |
|
207 if ((err = fuse_reply_err(dirop->req, 0))) |
|
208 // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid dirops |
|
209 EFATAL(err, "[dbfs.releasedir %p:%p] dirop_done: reply with fuse_reply_err", dirop, dirop->req); |
|
210 |
|
211 // req is done |
|
212 dirop->req = NULL; |
|
213 |
|
214 // then we can just free dirop |
|
215 _dbfs_dirop_free(dirop); |
|
216 } |
|
217 |
|
218 /* |
|
219 * The dirop trans has failed, somehow, at some point, some where. |
|
220 * |
|
221 * This might happend during the opendir evsql_trans, during a readdir evsql_query, during the releasedir |
|
222 * evsql_trans_commit, or at any point in between. |
|
223 * |
|
224 * 1) loose the transaction |
|
225 * 2) if dirop has a req, we handle failing it |
|
226 */ |
|
227 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) { |
|
228 struct dbfs_dirop *dirop = arg; |
|
229 |
|
230 INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans)); |
|
231 |
|
232 // deassociate the trans |
|
233 dirop->trans = NULL; |
|
234 |
|
235 // if we were answering a req, error it out, and if the dirop isn't open, release it |
|
236 // if we didn't have a req outstanding, the dirop must be open, so we wouldn't free it in any case, and must wait |
|
237 // for the next readdir/releasedir to detect this and return an error reply |
|
238 if (dirop->req) |
|
239 _dbfs_dirop_fail(dirop); |
|
240 else |
|
241 assert(dirop->open); |
|
242 } |
|
243 |
|
244 /* |
|
245 * Handle opendir(), this means starting a new transaction, dbfs_dirop_ready/error will continue on from there. |
|
246 * |
|
247 * The contents of fi will be copied into the dirop, and will be used as the basis for the fuse_reply_open reply. |
|
248 */ |
126 */ |
249 void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
127 void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
250 struct dbfs *ctx = fuse_req_userdata(req); |
128 struct dbfs *ctx = fuse_req_userdata(req); |
251 struct dbfs_dirop *dirop = NULL; |
129 struct dbfs_dirop *dirop = NULL; |
252 int err; |
130 int err; |
253 |
131 |
254 // allocate it |
132 // allocate it |
255 if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO)) |
133 if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO)) |
256 ERROR("calloc"); |
134 ERROR("calloc"); |
257 |
135 |
|
136 // do the op_open |
|
137 if ((err = dbfs_op_open(ctx, &dirop->base, req, ino, fi, _dbfs_dirop_free, dbfs_dirop_open))) |
|
138 ERROR("dbfs_op_open"); |
|
139 |
258 INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi); |
140 INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi); |
259 |
|
260 // store the dirop |
|
261 // copy *fi since it's on the stack |
|
262 dirop->fi = *fi; |
|
263 dirop->fi.fh = (uint64_t) dirop; |
|
264 dirop->req = req; |
|
265 dirop->ino = ino; |
|
266 |
|
267 // start a new transaction |
|
268 if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL) |
|
269 SERROR(err = EIO); |
|
270 |
|
271 // XXX: handle interrupts |
|
272 |
141 |
273 // wait |
142 // wait |
274 return; |
143 return; |
275 |
144 |
276 error: |
145 error: |
277 if (dirop) { |
146 if (dirop) { |
278 // we can fail normally |
147 // we can fail normally |
279 _dbfs_dirop_fail(dirop); |
148 dbfs_op_fail(&dirop->base, err); |
280 |
149 |
281 } else { |
150 } else { |
282 // must error out manually as we couldn't alloc the context |
151 // must error out manually as we couldn't alloc the context |
283 if ((err = fuse_reply_err(req, err))) |
152 if ((err = fuse_reply_err(req, err))) |
284 EWARNING(err, "fuse_reply_err"); |
153 EWARNING(err, "fuse_reply_err"); |
289 * Got the list of files for our readdir() request. |
158 * Got the list of files for our readdir() request. |
290 * |
159 * |
291 * Fill up the dirbuf, and then send the reply. |
160 * Fill up the dirbuf, and then send the reply. |
292 * |
161 * |
293 */ |
162 */ |
294 static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) { |
163 static void dbfs_readdir_res (const struct evsql_result_info *res, void *arg) { |
295 struct dbfs_dirop *dirop = arg; |
164 struct dbfs_dirop *dirop = arg; |
296 int err; |
165 int err; |
297 size_t row; |
166 size_t row; |
298 |
167 |
299 assert(dirop->req); |
168 assert(dirop->base.req); |
300 assert(dirop->trans); |
169 assert(dirop->base.trans); // query callbacks don't get called if the trans fails |
301 assert(dirop->open); |
170 assert(dirop->base.open); |
302 |
171 |
303 // check the results |
172 // check the results |
304 if ((err = _dbfs_check_res(res, 0, 4)) < 0) |
173 if ((err = _dbfs_check_res(res, 0, 4)) < 0) |
305 SERROR(err = EIO); |
174 SERROR(err = EIO); |
306 |
175 |
307 INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, dirop->req, evsql_result_rows(res)); |
176 INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, dirop->base.req, evsql_result_rows(res)); |
308 |
177 |
309 // iterate over the rows |
178 // iterate over the rows |
310 for (row = 0; row < evsql_result_rows(res); row++) { |
179 for (row = 0; row < evsql_result_rows(res); row++) { |
311 uint32_t off, ino; |
180 uint32_t off, ino; |
312 const char *name, *type; |
181 const char *name, *type; |
322 |
191 |
323 INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type); |
192 INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type); |
324 |
193 |
325 // add to the dirbuf |
194 // add to the dirbuf |
326 // offsets are just offset + 2 |
195 // offsets are just offset + 2 |
327 if ((err = dirbuf_add(dirop->req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO)) |
196 if ((err = dirbuf_add(dirop->base.req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO)) |
328 ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino); |
197 ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino); |
329 |
198 |
330 // stop if it's full |
199 // stop if it's full |
331 if (err > 0) |
200 if (err > 0) |
332 break; |
201 break; |
333 } |
202 } |
334 |
203 |
335 // send it |
204 // send it |
336 if ((err = dirbuf_done(dirop->req, &dirop->dirbuf))) |
205 if ((err = dirbuf_done(dirop->base.req, &dirop->dirbuf))) |
337 EERROR(err, "failed to send buf"); |
206 EERROR(err, "failed to send buf"); |
338 |
207 |
339 // req is done |
208 // handled the req |
340 dirop->req = NULL; |
209 if ((err = dbfs_op_req_done(&dirop->base))) |
341 |
210 goto error; |
|
211 |
342 // good, fallthrough |
212 // good, fallthrough |
343 err = 0; |
213 err = 0; |
344 |
214 |
345 error: |
215 error: |
346 if (err) |
216 if (err) |
347 _dbfs_dirop_fail(dirop); |
217 dbfs_op_fail(&dirop->base, err); |
348 |
218 |
349 // free |
219 // free |
350 evsql_result_free(res); |
220 evsql_result_free(res); |
351 } |
221 } |
352 |
222 |
353 /* |
223 /* |
354 * Handle a readdir request. This will execute a SQL query inside the transaction to get the files at the given offset, |
224 * Handle a readdir request. This will execute a SQL query inside the transaction to get the files at the given offset, |
355 * and _dbfs_readdir_res will handle the results. |
225 * and dbfs_readdir_res will handle the results. |
356 * |
226 * |
357 * If trans failed earlier, detect that and return an error. |
227 * If trans failed earlier, detect that and return an error. |
358 */ |
228 */ |
359 void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
229 void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
360 struct dbfs *ctx = fuse_req_userdata(req); |
230 struct dbfs *ctx = fuse_req_userdata(req); |
361 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
231 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
362 int err; |
232 int err; |
363 |
233 |
364 assert(dirop); |
234 // get the op |
365 assert(!dirop->req); |
235 if ((dirop = (struct dbfs_dirop *) dbfs_op_req(req, ino, fi)) == NULL) |
366 assert(dirop->open); |
236 SERROR(err = EIO); |
367 assert(dirop->ino == ino); |
237 |
368 |
238 INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->base.trans); |
369 // store the new req |
|
370 dirop->req = req; |
|
371 |
|
372 // detect earlier failures |
|
373 if (!dirop->trans && (err = EIO)) |
|
374 ERROR("dirop trans has failed"); |
|
375 |
|
376 INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans); |
|
377 |
239 |
378 // create the dirbuf |
240 // create the dirbuf |
379 if (dirbuf_init(&dirop->dirbuf, size, off)) |
241 if (dirbuf_init(&dirop->dirbuf, size, off)) |
380 SERROR(err = EIO); |
242 SERROR(err = EIO); |
381 |
243 |
382 // add . and .. |
244 // add . and .. |
383 // we set the next offset to 2, because all dirent offsets will be larger than that |
245 // we set the next offset to 2, because all dirent offsets will be larger than that |
384 // assume that these two should *always* fit |
246 // assume that these two should *always* fit |
385 if ((err = (0 |
247 if ((err = (0 |
386 || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->ino, S_IFDIR ) |
248 || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->base.ino, S_IFDIR ) |
387 || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..", |
249 || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..", |
388 dirop->parent ? dirop->parent : dirop->ino, S_IFDIR ) |
250 dirop->parent ? dirop->parent : dirop->base.ino, S_IFDIR ) |
389 )) && (err = EIO)) |
251 )) && (err = EIO)) |
390 ERROR("failed to add . and .. dirents"); |
252 ERROR("failed to add . and .. dirents"); |
391 |
253 |
392 // select all relevant file entries |
254 // select all relevant file entries |
393 const char *sql = |
255 const char *sql = |
409 if (off > 2) |
271 if (off > 2) |
410 off -= 2; |
272 off -= 2; |
411 |
273 |
412 // build params |
274 // build params |
413 if (0 |
275 if (0 |
414 || evsql_param_uint32(¶ms, 0, dirop->ino) |
276 || evsql_param_uint32(¶ms, 0, ino) |
415 || evsql_param_uint32(¶ms, 1, off) |
277 || evsql_param_uint32(¶ms, 1, off) |
416 || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0)) |
278 || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0)) |
417 ) |
279 ) |
418 SERROR(err = EIO); |
280 SERROR(err = EIO); |
419 |
281 |
420 // query |
282 // query |
421 if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_readdir_files_res, dirop) == NULL) |
283 if (evsql_query_params(ctx->db, dirop->base.trans, sql, ¶ms, dbfs_readdir_res, dirop) == NULL) |
422 SERROR(err = EIO); |
284 SERROR(err = EIO); |
423 |
285 |
424 // good, wait |
286 // good, wait |
425 return; |
287 return; |
426 |
288 |
427 error: |
289 error: |
428 _dbfs_dirop_fail(dirop); |
290 dbfs_op_fail(&dirop->base, err); |
429 } |
291 } |
430 |
292 |
431 /* |
293 /* |
432 * "For every [succesfull] opendir call there will be exactly one releasedir call." |
294 * "For every [succesfull] opendir call there will be exactly one releasedir call." |
433 * |
295 * |
434 * The dirop may be in a failed state. |
296 * The dirop may be in a failed state. |
435 */ |
297 */ |
436 void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
298 void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
437 struct dbfs *ctx = fuse_req_userdata(req); |
299 // just passthrough to dbfs_op |
438 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
300 dbfs_op_release(req, ino, fi); |
439 int err; |
301 } |
440 |
302 |
441 (void) ctx; |
|
442 |
|
443 assert(dirop); |
|
444 assert(!dirop->req); |
|
445 assert(dirop->ino == ino); |
|
446 |
|
447 // update to this req |
|
448 dirop->req = req; |
|
449 |
|
450 // fi is irrelevant, we don't touch the flags anyways |
|
451 (void) fi; |
|
452 |
|
453 // handle failed trans |
|
454 if (!dirop->trans) |
|
455 ERROR("trans has failed"); |
|
456 |
|
457 // log |
|
458 INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans); |
|
459 |
|
460 // we must commit the transaction (although it was jut SELECTs, no changes). |
|
461 // Note that this might cause dbfs_dirop_error to be called, we can tell if that happaned by looking at dirop->req |
|
462 // or dirop->trans this means that we need to keep the dirop open when calling trans_commit, so that dirop_error |
|
463 // doesn't free it out from underneath us. |
|
464 if (evsql_trans_commit(dirop->trans)) |
|
465 SERROR(err = EIO); |
|
466 |
|
467 // fall-through to cleanup |
|
468 err = 0; |
|
469 |
|
470 error: |
|
471 // the dirop is not open anymore and can be free'd: |
|
472 // a) if we already caught an error |
|
473 // b) if we get+send an error later on |
|
474 // c) if we get+send the done/no-error later on |
|
475 dirop->open = 0; |
|
476 |
|
477 // did the commit/pre-commit-checks fail? |
|
478 if (err) { |
|
479 // a) the trans failed earlier (readdir), so we have a req but no trans |
|
480 // b) the trans commit failed, dirop_error got called -> no req and no trans |
|
481 // c) the trans commit failed, dirop_error did not get called -> have req and trans |
|
482 // we either have a req (may or may not have trans), or we don't have a trans either |
|
483 // i.e. there is no situation where we don't have a req but do have a trans |
|
484 |
|
485 if (dirop->req) |
|
486 _dbfs_dirop_fail(dirop); |
|
487 else |
|
488 assert(!dirop->trans); |
|
489 |
|
490 } else { |
|
491 // shouldn't slip by, dirop_done should not get called directly. Once it does, it will handle both. |
|
492 assert(dirop->req); |
|
493 assert(dirop->trans); |
|
494 } |
|
495 } |
|
496 |
|