277 if ((err = fuse_reply_err(req, err))) |
279 if ((err = fuse_reply_err(req, err))) |
278 EWARNING(err, "fuse_reply_err"); |
280 EWARNING(err, "fuse_reply_err"); |
279 } |
281 } |
280 |
282 |
281 struct dbfs_dirop { |
283 struct dbfs_dirop { |
282 struct fuse_file_info *fi; |
284 struct fuse_file_info fi; |
283 struct fuse_req *req; |
285 struct fuse_req *req; |
284 |
286 |
285 struct evsql_trans *trans; |
287 struct evsql_trans *trans; |
|
288 |
|
289 // dir/parent dir inodes |
|
290 uint32_t ino, parent; |
286 |
291 |
287 // opendir has returned and releasedir hasn't been called yet |
292 // opendir has returned and releasedir hasn't been called yet |
288 int open; |
293 int open; |
|
294 |
|
295 // for readdir |
|
296 struct dirbuf dirbuf; |
289 }; |
297 }; |
290 |
298 |
291 /* |
299 /* |
292 * Free the dirop, aborting any in-progress transaction. |
300 * Free the dirop, aborting any in-progress transaction. |
293 * |
301 * |
294 * req must be NULL. |
302 * req must be NULL. |
295 */ |
303 */ |
296 static void dbfs_dirop_free (struct dbfs_dirop *dirop) { |
304 static void dbfs_dirop_free (struct dbfs_dirop *dirop) { |
297 assert(dirop->req == NULL); |
305 assert(dirop); |
298 |
306 assert(!dirop->open); |
299 if (dirop->trans) |
307 assert(!dirop->req); |
|
308 |
|
309 if (dirop->trans) { |
|
310 WARNING("aborting transaction"); |
300 evsql_trans_abort(dirop->trans); |
311 evsql_trans_abort(dirop->trans); |
|
312 } |
|
313 |
|
314 dirbuf_release(&dirop->dirbuf); |
301 |
315 |
302 free(dirop); |
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); |
303 } |
368 } |
304 |
369 |
305 /* |
370 /* |
306 * The opendir transaction is ready |
371 * The opendir transaction is ready |
307 */ |
372 */ |
308 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) { |
373 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) { |
309 struct dbfs_dirop *dirop = arg; |
374 struct dbfs_dirop *dirop = arg; |
310 struct fuse_req *req = dirop->req; dirop->req = NULL; |
375 struct fuse_req *req = dirop->req; |
311 int err; |
376 struct dbfs *ctx = fuse_req_userdata(req); |
312 |
377 int err; |
313 INFO("[dbfs.openddir %p:%p] -> trans=%p", dirop, req, trans); |
378 |
|
379 assert(req != NULL); |
|
380 |
|
381 INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, req, trans); |
314 |
382 |
315 // remember the transaction |
383 // remember the transaction |
316 dirop->trans = trans; |
384 dirop->trans = trans; |
317 |
385 |
318 // send the openddir reply |
386 // first fetch info about the dir itself |
319 if ((err = fuse_reply_open(dirop->req, dirop->fi))) |
387 const char *sql = |
320 EERROR(err, "fuse_reply_open"); |
388 "SELECT" |
321 |
389 " file_tree.parent, inodes.type" |
322 // dirop is now open |
390 " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" |
323 dirop->open = 1; |
391 " WHERE file_tree.inode = $1::int4"; |
324 |
392 |
325 // ok, wait for the next fs req |
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 |
326 return; |
410 return; |
327 |
411 |
328 error: |
412 error: |
|
413 // we handle the req |
|
414 dirop->req = NULL; |
|
415 |
|
416 // free the dirop |
329 dbfs_dirop_free(dirop); |
417 dbfs_dirop_free(dirop); |
330 |
418 |
331 if ((err = fuse_reply_err(req, err))) |
419 if ((err = fuse_reply_err(req, err))) |
332 EWARNING(err, "fuse_reply_err"); |
420 EWARNING(err, "fuse_reply_err"); |
333 } |
421 } |
334 |
422 |
335 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) { |
423 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) { |
336 struct dbfs_dirop *dirop = arg; |
424 struct dbfs_dirop *dirop = arg; |
337 int err; |
425 struct fuse_req *req = dirop->req; dirop->req = NULL; |
338 |
426 int err; |
339 |
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); |
340 } |
441 } |
341 |
442 |
342 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) { |
443 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) { |
343 struct dbfs_dirop *dirop = arg; |
444 struct dbfs_dirop *dirop = arg; |
344 int err; |
445 int err; |
392 |
495 |
393 dbfs_dirop_free(dirop); |
496 dbfs_dirop_free(dirop); |
394 |
497 |
395 if ((err = fuse_reply_err(req, err))) |
498 if ((err = fuse_reply_err(req, err))) |
396 EWARNING(err, "fuse_reply_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); |
397 } |
565 } |
398 |
566 |
399 static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
567 static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
400 struct dbfs *ctx = fuse_req_userdata(req); |
568 struct dbfs *ctx = fuse_req_userdata(req); |
401 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
569 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
402 int err; |
570 int err; |
403 |
571 |
|
572 assert(!dirop->req); |
|
573 assert(dirop->trans); |
|
574 assert(dirop->ino == ino); |
|
575 |
404 INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans); |
576 INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans); |
405 |
577 |
406 // update dirop |
578 // update dirop |
407 dirop->req = req; |
579 dirop->req = req; |
408 assert(dirop->fi == fi); |
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"); |
409 |
593 |
410 // select all relevant file entries |
594 // select all relevant file entries |
411 const char *sql = |
595 const char *sql = |
412 "SELECT" |
596 "SELECT" |
413 " \"file_tree.offset\", file_tree.name, inodes.ino, inodes.type" |
597 " file_tree.\"offset\", file_tree.name, inodes.ino, inodes.type" |
414 " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" |
598 " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" |
415 " WHERE file_tree.parent = $1::int4 AND \"file_tree.offset\" >= $2::int4" |
599 " WHERE file_tree.parent = $1::int4 AND file_tree.\"offset\" >= $2::int4" |
416 " LIMIT $3::int4"; |
600 " LIMIT $3::int4"; |
417 |
601 |
418 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
602 static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { |
419 EVSQL_PARAM ( UINT32 ), |
603 EVSQL_PARAM ( UINT32 ), |
420 EVSQL_PARAM ( UINT32 ), |
604 EVSQL_PARAM ( UINT32 ), |
421 EVSQL_PARAM ( UINT32 ), |
605 EVSQL_PARAM ( UINT32 ), |
422 |
606 |
423 EVSQL_PARAMS_END |
607 EVSQL_PARAMS_END |
424 }; |
608 }; |
425 |
609 |
426 // XXX: incomplete |
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 |
427 } |
639 } |
428 |
640 |
429 static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
641 static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { |
430 struct dbfs *ctx = fuse_req_userdata(req); |
642 struct dbfs *ctx = fuse_req_userdata(req); |
431 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
643 struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; |
432 int err; |
644 int err; |
433 |
645 |
434 (void) ctx; |
646 (void) ctx; |
|
647 |
|
648 assert(!dirop->req); |
|
649 assert(dirop->ino == ino); |
435 |
650 |
436 INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans); |
651 INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans); |
437 |
652 |
438 // update dirop. Must keep it open so that dbfs_dirop_error won't free it |
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; |
439 dirop->req = req; |
657 dirop->req = req; |
440 assert(dirop->fi == fi); |
658 |
441 |
659 if (dirop->trans) { |
442 // we can commit the transaction, although we didn't make any changes |
660 // we can commit the transaction, although we didn't make any changes |
443 // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be |
661 // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be |
444 // NULL |
662 // NULL |
445 if (evsql_trans_commit(dirop->trans)) |
663 if (evsql_trans_commit(dirop->trans)) |
446 SERROR(err = EIO); |
664 SERROR(err = EIO); |
447 |
665 |
448 // not open anymore |
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 |
449 dirop->open = 0; |
680 dirop->open = 0; |
450 |
681 |
451 // XXX: handle interrupts |
682 // if trans_commit triggered an error but didn't call dbfs_dirop_error, we need to take care of it |
452 |
683 if (err && dirop->req) { |
453 // wait |
684 int err2; |
454 return; |
685 |
455 |
|
456 error: |
|
457 if (dirop->req) { |
|
458 // we handle the req |
686 // we handle the req |
459 dirop->req = NULL; |
687 dirop->req = NULL; |
460 |
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) { |
461 dbfs_dirop_free(dirop); |
695 dbfs_dirop_free(dirop); |
462 |
696 |
463 if ((err = fuse_reply_err(req, err))) |
697 } else |
464 EWARNING(err, "fuse_reply_err"); |
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); |
465 } |
702 } |
466 } |
703 } |
467 |
704 |
468 struct fuse_lowlevel_ops dbfs_llops = { |
705 struct fuse_lowlevel_ops dbfs_llops = { |
469 |
706 |