8 #include <errno.h> |
8 #include <errno.h> |
9 #include <assert.h> |
9 #include <assert.h> |
10 |
10 |
11 #include <event2/event.h> |
11 #include <event2/event.h> |
12 |
12 |
|
13 #include "dbfs.h" |
13 #include "evfuse.h" |
14 #include "evfuse.h" |
14 #include "evsql.h" |
15 #include "evsql.h" |
15 #include "dirbuf.h" |
|
16 #include "lib/log.h" |
16 #include "lib/log.h" |
17 #include "lib/signals.h" |
17 #include "lib/signals.h" |
18 #include "lib/misc.h" |
18 #include "lib/misc.h" |
19 |
19 |
20 #define SERROR(val) do { (val); goto error; } while(0) |
|
21 |
|
22 struct dbfs { |
|
23 struct event_base *ev_base; |
|
24 struct signals *signals; |
|
25 |
|
26 const char *db_conninfo; |
|
27 struct evsql *db; |
|
28 |
|
29 struct evfuse *ev_fuse; |
|
30 }; |
|
31 |
|
32 #define CONNINFO_DEFAULT "dbname=test" |
20 #define CONNINFO_DEFAULT "dbname=test" |
33 |
21 |
34 // XXX: not sure how this should work |
|
35 #define CACHE_TIMEOUT 1.0 |
|
36 |
|
37 mode_t _dbfs_mode (const char *type) { |
|
38 if (!strcmp(type, "DIR")) |
|
39 return S_IFDIR; |
|
40 |
|
41 if (!strcmp(type, "REG")) |
|
42 return S_IFREG; |
|
43 |
|
44 else { |
|
45 WARNING("[dbfs] weird mode-type: %s", type); |
|
46 return 0; |
|
47 } |
|
48 } |
|
49 |
|
50 void dbfs_init (void *userdata, struct fuse_conn_info *conn) { |
|
51 INFO("[dbfs.init] userdata=%p, conn=%p", userdata, conn); |
|
52 |
|
53 } |
|
54 |
|
55 void dbfs_destroy (void *arg) { |
|
56 struct dbfs *ctx = arg; |
|
57 INFO("[dbfs.destroy %p]", ctx); |
|
58 |
|
59 // exit libevent |
|
60 event_base_loopexit(ctx->ev_base, NULL); |
|
61 } |
|
62 |
|
63 /* |
|
64 * Check the result set. |
|
65 * |
|
66 * Returns; |
|
67 * -1 if the query failed, the columns do not match, or there are too many/few rows (unless rows was zero) |
|
68 * 0 the results match |
|
69 * 1 there were no results |
|
70 */ |
|
71 int _dbfs_check_res (const struct evsql_result_info *res, size_t rows, size_t cols) { |
|
72 int err = 0; |
|
73 |
|
74 // check if it failed |
|
75 if (res->error) |
|
76 NERROR(evsql_result_error(res)); |
|
77 |
|
78 // not found? |
|
79 if (evsql_result_rows(res) == 0) |
|
80 SERROR(err = 1); |
|
81 |
|
82 // duplicate rows? |
|
83 if (rows && evsql_result_rows(res) != rows) |
|
84 ERROR("wrong number of rows returned"); |
|
85 |
|
86 // correct number of columns |
|
87 if (evsql_result_cols(res) != cols) |
|
88 ERROR("wrong number of columns: %zu", evsql_result_cols(res)); |
|
89 |
|
90 // good |
|
91 return 0; |
|
92 |
|
93 error: |
|
94 if (!err) |
|
95 err = -1; |
|
96 |
|
97 return err; |
|
98 } |
|
99 |
|
100 int _dbfs_stat_info (struct stat *st, const struct evsql_result_info *res, size_t row, size_t col_offset) { |
|
101 int err = 0; |
|
102 |
|
103 uint16_t mode; |
|
104 uint64_t size, nlink; |
|
105 const char *type; |
|
106 |
|
107 // extract the data |
|
108 if (0 |
|
109 || evsql_result_string(res, row, col_offset + 0, &type, 0 ) // inodes.type |
|
110 || evsql_result_uint16(res, row, col_offset + 1, &mode, 0 ) // inodes.mode |
|
111 || evsql_result_uint64(res, row, col_offset + 2, &size, 0 ) // inodes.size |
|
112 || evsql_result_uint64(res, row, col_offset + 3, &nlink, 0 ) // count(*) |
|
113 ) |
|
114 EERROR(err = EIO, "invalid db data"); |
|
115 |
|
116 INFO("\tst_mode=S_IF%s | %ho, st_nlink=%llu, st_size=%llu", type, mode, (long long unsigned int) nlink, (long long unsigned int) size); |
|
117 |
|
118 // convert and store |
|
119 st->st_mode = _dbfs_mode(type) | mode; |
|
120 st->st_nlink = nlink; |
|
121 st->st_size = size; |
|
122 |
|
123 // good |
|
124 return 0; |
|
125 |
|
126 error: |
|
127 return -1; |
|
128 } |
|
129 |
|
130 void _dbfs_lookup_result (const struct evsql_result_info *res, void *arg) { |
|
131 struct fuse_req *req = arg; |
|
132 struct fuse_entry_param e; ZINIT(e); |
|
133 int err = 0; |
|
134 |
|
135 uint32_t ino; |
|
136 |
|
137 // check the results |
|
138 if ((err = _dbfs_check_res(res, 1, 5))) |
|
139 SERROR(err = (err == 1 ? ENOENT : EIO)); |
|
140 |
|
141 // get the data |
|
142 if (0 |
|
143 || evsql_result_uint32(res, 0, 0, &ino, 0 ) // inodes.ino |
|
144 ) |
|
145 EERROR(err = EIO, "invalid db data"); |
|
146 |
|
147 INFO("[dbfs.lookup] -> ino=%u", ino); |
|
148 |
|
149 // stat attrs |
|
150 if (_dbfs_stat_info(&e.attr, res, 0, 1)) |
|
151 goto error; |
|
152 |
|
153 // other attrs |
|
154 e.ino = e.attr.st_ino = ino; |
|
155 e.attr_timeout = CACHE_TIMEOUT; |
|
156 e.entry_timeout = CACHE_TIMEOUT; |
|
157 |
|
158 // reply |
|
159 if ((err = fuse_reply_entry(req, &e))) |
|
160 EERROR(err, "fuse_reply_entry"); |
|
161 |
|
162 error: |
|
163 if (err && (err = fuse_reply_err(req, err))) |
|
164 EWARNING(err, "fuse_reply_err"); |
|
165 |
|
166 // free |
|
167 evsql_result_free(res); |
|
168 } |
|
169 |
|
170 void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name) { |
|
171 struct dbfs *ctx = fuse_req_userdata(req); |
|
172 int err; |
|
173 |
|
174 INFO("[dbfs.lookup] parent=%lu name=%s", parent, name); |
|
175 |
|
176 // query and params |
|
177 const char *sql = |
|
178 "SELECT" |
|
179 " inodes.ino, inodes.type, inodes.mode, inodes.size, count(*)" |
|
180 " FROM file_tree INNER JOIN inodes ON (file_tree.inode = inodes.ino)" |
|
181 " WHERE file_tree.parent = $1::int4 AND file_tree.name = $2::varchar" |
|
182 " GROUP BY inodes.ino, inodes.type, inodes.mode, inodes.size"; |
|
183 |
|
184 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
|
185 EVSQL_PARAM ( UINT32 ), |
|
186 EVSQL_PARAM ( STRING ), |
|
187 |
|
188 EVSQL_PARAMS_END |
|
189 }; |
|
190 |
|
191 // build params |
|
192 if (0 |
|
193 || evsql_param_uint32(¶ms, 0, parent) |
|
194 || evsql_param_string(¶ms, 1, name) |
|
195 ) |
|
196 EERROR(err = EIO, "evsql_param_*"); |
|
197 |
|
198 // query |
|
199 if (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_lookup_result, req) == NULL) |
|
200 EERROR(err = EIO, "evsql_query_params"); |
|
201 |
|
202 // XXX: handle interrupts |
|
203 |
|
204 // wait |
|
205 return; |
|
206 |
|
207 error: |
|
208 if ((err = fuse_reply_err(req, err))) |
|
209 EWARNING(err, "fuse_reply_err"); |
|
210 } |
|
211 |
|
212 void _dbfs_getattr_result (const struct evsql_result_info *res, void *arg) { |
|
213 struct fuse_req *req = arg; |
|
214 struct stat st; ZINIT(st); |
|
215 int err = 0; |
|
216 |
|
217 // check the results |
|
218 if ((err = _dbfs_check_res(res, 1, 4))) |
|
219 SERROR(err = (err == 1 ? ENOENT : EIO)); |
|
220 |
|
221 INFO("[dbfs.getattr %p] -> (stat follows)", req); |
|
222 |
|
223 // stat attrs |
|
224 if (_dbfs_stat_info(&st, res, 0, 0)) |
|
225 goto error; |
|
226 |
|
227 // XXX: we don't have the ino |
|
228 st.st_ino = 0; |
|
229 |
|
230 // reply |
|
231 if ((err = fuse_reply_attr(req, &st, CACHE_TIMEOUT))) |
|
232 EERROR(err, "fuse_reply_entry"); |
|
233 |
|
234 error: |
|
235 if (err && (err = fuse_reply_err(req, err))) |
|
236 EWARNING(err, "fuse_reply_err"); |
|
237 |
|
238 // free |
|
239 evsql_result_free(res); |
|
240 } |
|
241 |
|
242 static void dbfs_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
243 struct dbfs *ctx = fuse_req_userdata(req); |
|
244 int err; |
|
245 |
|
246 (void) fi; |
|
247 |
|
248 INFO("[dbfs.getattr %p] ino=%lu", req, ino); |
|
249 |
|
250 const char *sql = |
|
251 "SELECT" |
|
252 " inodes.type, inodes.mode, inodes.size, count(*)" |
|
253 " FROM inodes" |
|
254 " WHERE inodes.ino = $1::int4" |
|
255 " GROUP BY inodes.type, inodes.mode, inodes.size"; |
|
256 |
|
257 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
|
258 EVSQL_PARAM ( UINT32 ), |
|
259 |
|
260 EVSQL_PARAMS_END |
|
261 }; |
|
262 |
|
263 // build params |
|
264 if (0 |
|
265 || evsql_param_uint32(¶ms, 0, ino) |
|
266 ) |
|
267 SERROR(err = EIO); |
|
268 |
|
269 // query |
|
270 if (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_getattr_result, req) == NULL) |
|
271 SERROR(err = EIO); |
|
272 |
|
273 // XXX: handle interrupts |
|
274 |
|
275 // wait |
|
276 return; |
|
277 |
|
278 error: |
|
279 if ((err = fuse_reply_err(req, err))) |
|
280 EWARNING(err, "fuse_reply_err"); |
|
281 } |
|
282 |
|
283 struct dbfs_dirop { |
|
284 struct fuse_file_info fi; |
|
285 struct fuse_req *req; |
|
286 |
|
287 struct evsql_trans *trans; |
|
288 |
|
289 // dir/parent dir inodes |
|
290 uint32_t ino, parent; |
|
291 |
|
292 // opendir has returned and releasedir hasn't been called yet |
|
293 int open; |
|
294 |
|
295 // for readdir |
|
296 struct dirbuf dirbuf; |
|
297 }; |
|
298 |
|
299 /* |
|
300 * Free the dirop, aborting any in-progress transaction. |
|
301 * |
|
302 * req must be NULL. |
|
303 */ |
|
304 static void dbfs_dirop_free (struct dbfs_dirop *dirop) { |
|
305 assert(dirop); |
|
306 assert(!dirop->open); |
|
307 assert(!dirop->req); |
|
308 |
|
309 if (dirop->trans) { |
|
310 WARNING("aborting transaction"); |
|
311 evsql_trans_abort(dirop->trans); |
|
312 } |
|
313 |
|
314 dirbuf_release(&dirop->dirbuf); |
|
315 |
|
316 free(dirop); |
|
317 } |
|
318 |
|
319 static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) { |
|
320 struct dbfs_dirop *dirop = arg; |
|
321 struct fuse_req *req = dirop->req; dirop->req = NULL; |
|
322 int err; |
|
323 |
|
324 assert(req != NULL); |
|
325 |
|
326 // check the results |
|
327 if ((err = _dbfs_check_res(res, 1, 2))) |
|
328 SERROR(err = (err == 1 ? ENOENT : EIO)); |
|
329 |
|
330 const char *type; |
|
331 |
|
332 // extract the data |
|
333 if (0 |
|
334 || evsql_result_uint32(res, 0, 0, &dirop->parent, 1 ) // file_tree.parent |
|
335 || evsql_result_string(res, 0, 1, &type, 0 ) // inodes.type |
|
336 ) |
|
337 SERROR(err = EIO); |
|
338 |
|
339 // is it a dir? |
|
340 if (_dbfs_mode(type) != S_IFDIR) |
|
341 EERROR(err = ENOTDIR, "wrong type: %s", type); |
|
342 |
|
343 INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type); |
|
344 |
|
345 // send the openddir reply |
|
346 if ((err = fuse_reply_open(req, &dirop->fi))) |
|
347 EERROR(err, "fuse_reply_open"); |
|
348 |
|
349 // dirop is now open |
|
350 dirop->open = 1; |
|
351 |
|
352 // ok, wait for the opendir call |
|
353 return; |
|
354 |
|
355 error: |
|
356 if (err) { |
|
357 // abort the trans |
|
358 evsql_trans_abort(dirop->trans); |
|
359 |
|
360 dirop->trans = NULL; |
|
361 |
|
362 if ((err = fuse_reply_err(req, err))) |
|
363 EWARNING(err, "fuse_reply_err"); |
|
364 } |
|
365 |
|
366 // free |
|
367 evsql_result_free(res); |
|
368 } |
|
369 |
|
370 /* |
|
371 * The opendir transaction is ready |
|
372 */ |
|
373 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) { |
|
374 struct dbfs_dirop *dirop = arg; |
|
375 struct fuse_req *req = dirop->req; |
|
376 struct dbfs *ctx = fuse_req_userdata(req); |
|
377 int err; |
|
378 |
|
379 assert(req != NULL); |
|
380 |
|
381 INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, req, trans); |
|
382 |
|
383 // remember the transaction |
|
384 dirop->trans = trans; |
|
385 |
|
386 // first fetch info about the dir itself |
|
387 const char *sql = |
|
388 "SELECT" |
|
389 " file_tree.parent, inodes.type" |
|
390 " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" |
|
391 " WHERE file_tree.inode = $1::int4"; |
|
392 |
|
393 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
|
394 EVSQL_PARAM ( UINT32 ), |
|
395 |
|
396 EVSQL_PARAMS_END |
|
397 }; |
|
398 |
|
399 // build params |
|
400 if (0 |
|
401 || evsql_param_uint32(¶ms, 0, dirop->ino) |
|
402 ) |
|
403 SERROR(err = EIO); |
|
404 |
|
405 // query |
|
406 if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_opendir_info_res, dirop) == NULL) |
|
407 SERROR(err = EIO); |
|
408 |
|
409 // ok, wait for the info results |
|
410 return; |
|
411 |
|
412 error: |
|
413 // we handle the req |
|
414 dirop->req = NULL; |
|
415 |
|
416 // free the dirop |
|
417 dbfs_dirop_free(dirop); |
|
418 |
|
419 if ((err = fuse_reply_err(req, err))) |
|
420 EWARNING(err, "fuse_reply_err"); |
|
421 } |
|
422 |
|
423 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) { |
|
424 struct dbfs_dirop *dirop = arg; |
|
425 struct fuse_req *req = dirop->req; dirop->req = NULL; |
|
426 int err; |
|
427 |
|
428 assert(req != NULL); |
|
429 |
|
430 INFO("[dbfs.releasedir %p:%p] -> OK", dirop, req); |
|
431 |
|
432 // forget trans |
|
433 dirop->trans = NULL; |
|
434 |
|
435 // just reply |
|
436 if ((err = fuse_reply_err(req, 0))) |
|
437 EWARNING(err, "fuse_reply_err"); |
|
438 |
|
439 // we can free dirop |
|
440 dbfs_dirop_free(dirop); |
|
441 } |
|
442 |
|
443 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) { |
|
444 struct dbfs_dirop *dirop = arg; |
|
445 int err; |
|
446 |
|
447 INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans)); |
|
448 |
|
449 // deassociate the trans |
|
450 dirop->trans = NULL; |
|
451 |
|
452 // error out and pending req |
|
453 if (dirop->req) { |
|
454 if ((err = fuse_reply_err(dirop->req, EIO))) |
|
455 EWARNING(err, "fuse_erply_err"); |
|
456 |
|
457 dirop->req = NULL; |
|
458 |
|
459 // only free the dirop if it isn't open |
|
460 if (!dirop->open) |
|
461 dbfs_dirop_free(dirop); |
|
462 } |
|
463 } |
|
464 |
|
465 static void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
466 struct dbfs *ctx = fuse_req_userdata(req); |
|
467 struct dbfs_dirop *dirop = NULL; |
|
468 int err; |
|
469 |
|
470 // allocate it |
|
471 if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO)) |
|
472 ERROR("calloc"); |
|
473 |
|
474 INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi); |
|
475 |
|
476 // store the dirop |
|
477 // copy *fi since it's on the stack |
|
478 dirop->fi = *fi; |
|
479 dirop->fi.fh = (uint64_t) dirop; |
|
480 dirop->req = req; |
|
481 dirop->ino = ino; |
|
482 |
|
483 // start a new transaction |
|
484 if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL) |
|
485 SERROR(err = EIO); |
|
486 |
|
487 // XXX: handle interrupts |
|
488 |
|
489 // wait |
|
490 return; |
|
491 |
|
492 error: |
|
493 // we handle the req |
|
494 dirop->req = NULL; |
|
495 |
|
496 dbfs_dirop_free(dirop); |
|
497 |
|
498 if ((err = fuse_reply_err(req, err))) |
|
499 EWARNING(err, "fuse_reply_err"); |
|
500 } |
|
501 |
|
502 static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) { |
|
503 struct dbfs_dirop *dirop = arg; |
|
504 struct fuse_req *req = dirop->req; dirop->req = NULL; |
|
505 int err; |
|
506 size_t row; |
|
507 |
|
508 assert(req != NULL); |
|
509 |
|
510 // check the results |
|
511 if ((err = _dbfs_check_res(res, 0, 4)) < 0) |
|
512 SERROR(err = EIO); |
|
513 |
|
514 INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, req, evsql_result_rows(res)); |
|
515 |
|
516 // iterate over the rows |
|
517 for (row = 0; row < evsql_result_rows(res); row++) { |
|
518 uint32_t off, ino; |
|
519 const char *name, *type; |
|
520 |
|
521 // extract the data |
|
522 if (0 |
|
523 || evsql_result_uint32(res, row, 0, &off, 0 ) // file_tree.offset |
|
524 || evsql_result_string(res, row, 1, &name, 0 ) // file_tree.name |
|
525 || evsql_result_uint32(res, row, 2, &ino, 0 ) // inodes.ino |
|
526 || evsql_result_string(res, row, 3, &type, 0 ) // inodes.type |
|
527 ) |
|
528 SERROR(err = EIO); |
|
529 |
|
530 INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type); |
|
531 |
|
532 // add to the dirbuf |
|
533 // offsets are just offset + 2 |
|
534 if ((err = dirbuf_add(req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO)) |
|
535 ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino); |
|
536 |
|
537 // stop if it's full |
|
538 if (err > 0) |
|
539 break; |
|
540 } |
|
541 |
|
542 // send it |
|
543 if ((err = dirbuf_done(req, &dirop->dirbuf))) |
|
544 EERROR(err, "failed to send buf"); |
|
545 |
|
546 // good, fallthrough |
|
547 err = 0; |
|
548 |
|
549 error: |
|
550 if (err) { |
|
551 // abort the trans |
|
552 evsql_trans_abort(dirop->trans); |
|
553 |
|
554 dirop->trans = NULL; |
|
555 |
|
556 // we handle the req |
|
557 dirop->req = NULL; |
|
558 |
|
559 if ((err = fuse_reply_err(req, err))) |
|
560 EWARNING(err, "fuse_reply_err"); |
|
561 } |
|
562 |
|
563 // free |
|
564 evsql_result_free(res); |
|
565 } |
|
566 |
|
567 static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
|
568 struct dbfs *ctx = fuse_req_userdata(req); |
|
569 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
|
570 int err; |
|
571 |
|
572 assert(!dirop->req); |
|
573 assert(dirop->trans); |
|
574 assert(dirop->ino == ino); |
|
575 |
|
576 INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans); |
|
577 |
|
578 // update dirop |
|
579 dirop->req = req; |
|
580 |
|
581 // create the dirbuf |
|
582 if (dirbuf_init(&dirop->dirbuf, size, off)) |
|
583 SERROR(err = EIO); |
|
584 |
|
585 // add . and .. |
|
586 // we set the next offset to 2, because all dirent offsets will be larger than that |
|
587 if ((err = (0 |
|
588 || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->ino, S_IFDIR ) |
|
589 || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..", |
|
590 dirop->parent ? dirop->parent : dirop->ino, S_IFDIR ) |
|
591 )) && (err = EIO)) |
|
592 ERROR("failed to add . and .. dirents"); |
|
593 |
|
594 // select all relevant file entries |
|
595 const char *sql = |
|
596 "SELECT" |
|
597 " file_tree.\"offset\", file_tree.name, inodes.ino, inodes.type" |
|
598 " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" |
|
599 " WHERE file_tree.parent = $1::int4 AND file_tree.\"offset\" >= $2::int4" |
|
600 " LIMIT $3::int4"; |
|
601 |
|
602 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
|
603 EVSQL_PARAM ( UINT32 ), |
|
604 EVSQL_PARAM ( UINT32 ), |
|
605 EVSQL_PARAM ( UINT32 ), |
|
606 |
|
607 EVSQL_PARAMS_END |
|
608 }; |
|
609 |
|
610 // adjust offset to take . and .. into account |
|
611 if (off > 2) |
|
612 off -= 2; |
|
613 |
|
614 // build params |
|
615 if (0 |
|
616 || evsql_param_uint32(¶ms, 0, dirop->ino) |
|
617 || evsql_param_uint32(¶ms, 1, off) |
|
618 || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0)) |
|
619 ) |
|
620 SERROR(err = EIO); |
|
621 |
|
622 // query |
|
623 if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_readdir_files_res, dirop) == NULL) |
|
624 SERROR(err = EIO); |
|
625 |
|
626 // good, wait |
|
627 return; |
|
628 |
|
629 error: |
|
630 // we handle the req |
|
631 dirop->req = NULL; |
|
632 |
|
633 // abort the trans |
|
634 evsql_trans_abort(dirop->trans); dirop->trans = NULL; |
|
635 |
|
636 if ((err = fuse_reply_err(req, err))) |
|
637 EWARNING(err, "fuse_reply_err"); |
|
638 |
|
639 } |
|
640 |
|
641 static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
642 struct dbfs *ctx = fuse_req_userdata(req); |
|
643 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
|
644 int err; |
|
645 |
|
646 (void) ctx; |
|
647 |
|
648 assert(!dirop->req); |
|
649 assert(dirop->ino == ino); |
|
650 |
|
651 INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans); |
|
652 |
|
653 // update dirop. Must keep it open so that dbfs_dirop_error won't free it |
|
654 // copy *fi since it's on the stack |
|
655 dirop->fi = *fi; |
|
656 dirop->fi.fh = (uint64_t) dirop; |
|
657 dirop->req = req; |
|
658 |
|
659 if (dirop->trans) { |
|
660 // we can commit the transaction, although we didn't make any changes |
|
661 // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be |
|
662 // NULL |
|
663 if (evsql_trans_commit(dirop->trans)) |
|
664 SERROR(err = EIO); |
|
665 |
|
666 } else { |
|
667 // trans failed earlier, so have releasedir just succeed |
|
668 if ((err = fuse_reply_err(req, 0))) |
|
669 EERROR(err, "fuse_reply_err"); |
|
670 |
|
671 // req is done |
|
672 dirop->req = NULL; |
|
673 } |
|
674 |
|
675 // fall-through to cleanup |
|
676 err = 0; |
|
677 |
|
678 error: |
|
679 // the dirop is not open anymore and can be freed once done with |
|
680 dirop->open = 0; |
|
681 |
|
682 // if trans_commit triggered an error but didn't call dbfs_dirop_error, we need to take care of it |
|
683 if (err && dirop->req) { |
|
684 int err2; |
|
685 |
|
686 // we handle the req |
|
687 dirop->req = NULL; |
|
688 |
|
689 if ((err2 = fuse_reply_err(req, err))) |
|
690 EWARNING(err2, "fuse_reply_err"); |
|
691 } |
|
692 |
|
693 // same for trans, we need to abort it if trans_commit failed and fs_dirop_error didn't get called |
|
694 if (err && dirop->trans) { |
|
695 dbfs_dirop_free(dirop); |
|
696 |
|
697 } else |
|
698 // alternatively, if the trans error'd itself away (now or earlier), we don't need to keep the dirop around |
|
699 // anymore now that we've checkd its state |
|
700 if (!dirop->trans) { |
|
701 dbfs_dirop_free(dirop); |
|
702 } |
|
703 } |
|
704 |
|
705 struct fuse_lowlevel_ops dbfs_llops = { |
|
706 |
|
707 .init = dbfs_init, |
|
708 .destroy = dbfs_destroy, |
|
709 |
|
710 .lookup = dbfs_lookup, |
|
711 |
|
712 .getattr = dbfs_getattr, |
|
713 |
|
714 .opendir = dbfs_opendir, |
|
715 .readdir = dbfs_readdir, |
|
716 .releasedir = dbfs_releasedir, |
|
717 }; |
|
718 |
|
719 void dbfs_sql_error (struct evsql *evsql, void *arg) { |
|
720 struct dbfs *ctx = arg; |
|
721 |
|
722 // AAAAAAAAAA.... panic |
|
723 WARNING("[dbfs] SQL error: BREAKING MAIN LOOP LIEK NAO"); |
|
724 |
|
725 event_base_loopbreak(ctx->ev_base); |
|
726 } |
|
727 |
|
728 int main (int argc, char **argv) { |
22 int main (int argc, char **argv) { |
|
23 struct event_base *ev_base = NULL; |
|
24 struct signals *signals = NULL; |
|
25 struct dbfs *ctx = NULL; |
|
26 const char *db_conninfo; |
729 struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv); |
27 struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv); |
730 struct dbfs ctx; ZINIT(ctx); |
|
731 |
28 |
732 // parse args, XXX: fuse_args |
29 // parse args, XXX: fuse_args |
733 ctx.db_conninfo = CONNINFO_DEFAULT; |
30 db_conninfo = CONNINFO_DEFAULT; |
734 |
31 |
735 // init libevent |
32 // init libevent |
736 if ((ctx.ev_base = event_base_new()) == NULL) |
33 if ((ev_base = event_base_new()) == NULL) |
737 ERROR("event_base_new"); |
34 ERROR("event_base_new"); |
738 |
35 |
739 // setup signals |
36 // setup signals |
740 if ((ctx.signals = signals_default(ctx.ev_base)) == NULL) |
37 if ((signals = signals_default(ev_base)) == NULL) |
741 ERROR("signals_default"); |
38 ERROR("signals_default"); |
742 |
39 |
743 // open sql |
40 // setup dbfs |
744 if ((ctx.db = evsql_new_pq(ctx.ev_base, ctx.db_conninfo, dbfs_sql_error, &ctx)) == NULL) |
41 if ((ctx = dbfs_open(ev_base, &fuse_args, db_conninfo)) == NULL) |
745 ERROR("evsql_new_pq"); |
42 ERROR("dbfs_open"); |
746 |
|
747 // open fuse |
|
748 if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &dbfs_llops, &ctx)) == NULL) |
|
749 ERROR("evfuse_new"); |
|
750 |
43 |
751 // run libevent |
44 // run libevent |
752 INFO("running libevent loop"); |
45 INFO("running libevent loop"); |
753 |
46 |
754 if (event_base_dispatch(ctx.ev_base)) |
47 if (event_base_dispatch(ev_base)) |
755 PERROR("event_base_dispatch"); |
48 PERROR("event_base_dispatch"); |
756 |
49 |
757 // clean shutdown |
50 // clean shutdown |
758 |
51 |
759 error : |
52 error : |
760 // cleanup |
53 if (ctx) |
761 if (ctx.ev_fuse) |
54 dbfs_release(ctx); |
762 evfuse_free(ctx.ev_fuse); |
55 |
|
56 if (signals) |
|
57 signals_free(signals); |
763 |
58 |
764 // XXX: ctx.db |
59 if (ev_base) |
765 |
60 event_base_free(ev_base); |
766 if (ctx.signals) |
|
767 signals_free(ctx.signals); |
|
768 |
|
769 if (ctx.ev_base) |
|
770 event_base_free(ctx.ev_base); |
|
771 |
61 |
772 fuse_opt_free_args(&fuse_args); |
62 fuse_opt_free_args(&fuse_args); |
773 } |
63 } |
774 |
64 |