00001
00007 #include <errno.h>
00008 #include <fcntl.h>
00009 #include <fuse.h>
00010 #include <limits.h>
00011 #include <stdint.h>
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <strings.h>
00016 #include <syslog.h>
00017 #include <sys/stat.h>
00018 #include <sys/time.h>
00019 #include <sys/types.h>
00020 #include <time.h>
00021 #include <unistd.h>
00022
00023 #include "check.h"
00024 #include "db5.h"
00025 #include "file.h"
00026 #include "logger.h"
00027 #include "utf8.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00053 #define ESUCCESS 0
00054
00055
00057 char *fuse_device;
00058
00060 static time_t fuse_mount_date;
00061
00063 static char fuse_localfile[PATH_MAX];
00065 static int fuse_error;
00066
00067
00068 static void fuse_impl_exit()
00069 {
00070 check(fuse_get_context() != NULL);
00071
00072 fuse_device = NULL;
00073 fuse_exit(fuse_get_context()->fuse);
00074 }
00075
00076
00077 void *fuse_impl_init (struct fuse_conn_info *fs_attr)
00078 {
00079 check(fuse_device != NULL);
00080
00081 fuse_mount_date = time(NULL);
00082
00083 if (file_set_context(fuse_device) != true)
00084 {
00085
00086 syslog(LOG_ERR, "[fuse]init: fatal, unable to reach device '%s'\n", fuse_device);
00087 fuse_impl_exit();
00088 }
00089
00090 open_log();
00091 add_log(ADDLOG_DEBUG, "[fuse]init", "version compiled the %s at %s\n", __DATE__, __TIME__);
00092 add_log(ADDLOG_OPERATION, "[fuse]init", "initialization, device is '%s'\n", fuse_device);
00093
00094 if (db5_init() != true)
00095 {
00096 add_log(ADDLOG_CRITICAL, "[fuse]init", "unable to initialize filesystem\n");
00097 fuse_impl_exit();
00098 }
00099
00100 add_log(ADDLOG_OP_SUCCESS, "[fuse]init", "done.\n");
00101
00102 return NULL;
00103 }
00104
00105
00106 void fuse_impl_destroy (void *data)
00107 {
00108 if (fuse_device != NULL)
00109 {
00110 add_log(ADDLOG_OPERATION, "[fuse]destroy", "building indexes\n");
00111 db5_index();
00112 }
00113
00114 add_log(ADDLOG_OPERATION, "[fuse]destroy", "exiting filesystem\n");
00115 db5_free();
00116
00117 add_log(ADDLOG_DEBUG, "[fuse]destroy", "good bye!\n");
00118 close_log();
00119 }
00120
00121
00122 int fuse_impl_create (const char *path, mode_t mode, struct fuse_file_info *filedata)
00123 {
00124 check(path != NULL);
00125 check(filedata != NULL);
00126
00127 (void) mode;
00128
00129 add_log(ADDLOG_OPERATION, "[fuse]create", "called, args='%s',0%o\n", path, (unsigned int)mode);
00130
00131
00132 if (db5_exists(file_remove_headslash(path)))
00133 {
00134 add_log(ADDLOG_USER_ERROR, "[fuse]create", "file '%s' already exists\n", path);
00135
00136 return -EEXIST;
00137 }
00138
00139
00140 if (!db5_insert(file_remove_headslash(path)))
00141 {
00142 add_log(ADDLOG_FAIL, "[fuse]create", "unable to insert file '%s' in database\n", path);
00143
00144 return -EIO;
00145 }
00146
00147
00148 if (!db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)))
00149 {
00150 add_log(ADDLOG_FAIL, "[fuse]create", "unable to retrieve local file of '%s'\n", path);
00151
00152 return -EIO;
00153 }
00154
00155 add_log(ADDLOG_DUMP, "[fuse]create", "$path -> $localfile\n", path, fuse_localfile);
00156 log_dump("path", path);
00157 log_dump("localfile", fuse_localfile);
00158
00159
00160 filedata->fh = open(fuse_localfile, filedata->flags, 0644);
00161 if (filedata->fh == -1)
00162 {
00163 fuse_error = errno;
00164 add_log(ADDLOG_FAIL, "[fuse]open", "open fail: '%s'\n", strerror(fuse_error));
00165
00166 return -fuse_error;
00167 }
00168
00169 add_log(ADDLOG_OP_SUCCESS, "[fuse]create", "done.\n");
00170
00171
00172 return -ESUCCESS;
00173 }
00174
00175
00176 int fuse_impl_utimens (const char *path, const struct timespec tv[2])
00177 {
00178 struct utimbuf time;
00179
00180 check(path != NULL);
00181
00182 add_log(ADDLOG_OPERATION, "[fuse]utimens", "called, args='%s',(%u,%u)\n", path, tv[0], tv[1]);
00183
00184 if (db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)) != true)
00185 {
00186 add_log(ADDLOG_USER_ERROR, "[fuse]utimens", "file '%s' does not exists\n", fuse_localfile);
00187
00188 return -ENOENT;
00189 }
00190
00191 time.actime = tv[0].tv_sec;
00192 time.modtime = tv[1].tv_sec;
00193
00194 if (utime(fuse_localfile, &time) != 0)
00195 {
00196 fuse_error = errno;
00197 add_log(ADDLOG_FAIL, "[fuse]utimens", "unable to set access/modification time\n");
00198 log_dump("path", path);
00199 log_dump("localfile", fuse_localfile);
00200
00201 add_log(ADDLOG_FAIL, "[fuse]utimens", "utime: 0x%x '%s'\n", fuse_error, strerror(fuse_error));
00202
00203 return -fuse_error;
00204 }
00205
00206 add_log(ADDLOG_OP_SUCCESS, "[fuse]utimens", "done.\n");
00207
00208
00209 return -ESUCCESS;
00210 }
00211
00212
00213 int fuse_impl_getattr(const char *path, struct stat *attr)
00214 {
00215 struct stat localattr;
00216
00217 check(path != NULL);
00218 check(attr != NULL);
00219 check(fuse_get_context() != NULL);
00220
00221 add_log(ADDLOG_OPERATION, "[fuse]getattr", "called, args='%s'\n", path);
00222
00223 memset(attr, 0, sizeof(attr));
00224
00225
00226 if(strcmp(path, "/") == 0)
00227 {
00228
00229 attr->st_mode = S_IFDIR | 0755;
00230
00231 attr->st_nlink = 2;
00232
00233 attr->st_size = db5_count();
00234
00235 attr->st_atime = fuse_mount_date;
00236 attr->st_mtime = fuse_mount_date;
00237 attr->st_ctime = fuse_mount_date;
00238
00239 attr->st_uid = fuse_get_context()->uid;
00240 attr->st_gid = fuse_get_context()->gid;
00241
00242 add_log(ADDLOG_OP_SUCCESS, "[fuse]getattr", "done.\n");
00243
00244
00245 return -ESUCCESS;
00246 }
00247
00248 attr->st_ino = 0;
00249
00250 if (!db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)))
00251 {
00252 add_log(ADDLOG_USER_ERROR, "[fuse]getattr", "unable to find local file for '%s'\n", path);
00253
00254 return -ENOENT;
00255 }
00256
00257 if (stat(fuse_localfile, &localattr) != 0)
00258 {
00259 fuse_error = errno;
00260 add_log(ADDLOG_FAIL, "[fuse]getattr", "unable to get information from local file: %s\n", strerror(fuse_error));
00261 log_dump("localfile", fuse_localfile);
00262 log_dump("path", path);
00263
00264 return -fuse_error;
00265 }
00266
00267
00268 attr->st_size = localattr.st_size;
00269
00270 attr->st_atime = localattr.st_atime;
00271 attr->st_mtime = localattr.st_mtime;
00272 attr->st_ctime = localattr.st_ctime;
00273
00274 attr->st_blocks = localattr.st_blocks;
00275
00276 attr->st_mode = S_IFREG | 0644;
00277 attr->st_nlink = 1;
00278
00279 attr->st_uid = fuse_get_context()->uid;
00280 attr->st_gid = fuse_get_context()->gid;
00281
00282 add_log(ADDLOG_OP_SUCCESS, "[fuse]getattr", "done.\n");
00283
00284
00285 return -ESUCCESS;
00286 }
00287
00288
00289 int fuse_impl_unlink (const char *path)
00290 {
00291 check(path != NULL);
00292
00293 add_log(ADDLOG_OPERATION, "[fuse]unlink", "called, args='%s'\n", path);
00294
00295 if (!db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)))
00296 {
00297 add_log(ADDLOG_USER_ERROR, "[fuse]unlink", "unable to find file '%s'\n", path);
00298
00299 return -ENOENT;
00300 }
00301
00302 if (!db5_delete(file_remove_headslash(path)))
00303 {
00304 add_log(ADDLOG_FAIL, "[fuse]unlink", "unable to remove file '%s' from database\n", path);
00305
00306 return -EIO;
00307 }
00308
00309 if (unlink(fuse_localfile) != 0)
00310 {
00311 add_log(ADDLOG_RECOVER, "[fuse]unlink", "unable to remove local file: %s\n", strerror(fuse_error));
00312 log_dump("localfile", fuse_localfile);
00313 log_dump("path", path);
00314 }
00315
00316 add_log(ADDLOG_OP_SUCCESS, "[fuse]unlink", "done.\n");
00317
00318
00319 return -ESUCCESS;
00320 }
00321
00322
00323 int fuse_impl_rename (const char *path, const char *newname)
00324 {
00325 char localfile_new[PATH_MAX];
00326
00327 check(path != NULL);
00328 check(newname != NULL);
00329
00330 add_log(ADDLOG_OPERATION, "[fuse]rename", "called, args='%s' -> '%s'\n", path, newname);
00331
00332 if (!db5_exists(file_remove_headslash(path)))
00333 {
00334 add_log(ADDLOG_USER_ERROR, "[fuse]rename", "source file '%s' does not exists\n", path);
00335
00336 return -ENOENT;
00337 }
00338
00339 if (db5_exists(file_remove_headslash(newname)))
00340 {
00341 add_log(ADDLOG_USER_ERROR, "[fuse]rename", "destination file '%s' already exists\n", newname);
00342
00343 return -EEXIST;
00344 }
00345
00346
00347 if (!db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)))
00348 {
00349 add_log(ADDLOG_FAIL, "[fuse]rename", "unable to locate local file of '%s'\n", path);
00350
00351 return -EIO;
00352 }
00353
00354
00355 if (!db5_insert(file_remove_headslash(newname)))
00356 {
00357 add_log(ADDLOG_FAIL, "[fuse]rename", "unable to insert '%s' in database\n", newname);
00358
00359 return -EIO;
00360 }
00361
00362
00363 if (!db5_localfile(file_remove_headslash(newname), localfile_new, sizeof(localfile_new)))
00364 {
00365 add_log(ADDLOG_FAIL, "[fuse]rename", "unable to locate local file of '%s'\n", newname);
00366
00367 return -EIO;
00368 }
00369
00370 add_log(ADDLOG_DEBUG, "[fuse]rename", "renaming local file\n");
00371 log_dump("source", fuse_localfile);
00372 log_dump("destination", localfile_new);
00373
00374
00375 if (rename(fuse_localfile, localfile_new) != 0)
00376 {
00377 fuse_error = errno;
00378 add_log(ADDLOG_FAIL, "[fuse]rename", "unable to rename local file: %s\n", strerror(fuse_error));
00379 log_dump("source", fuse_localfile);
00380 log_dump("destination", localfile_new);
00381
00382 return -fuse_error;
00383 }
00384
00385
00386 if (!db5_delete(file_remove_headslash(path)))
00387 {
00388 add_log(ADDLOG_FAIL, "[fuse]rename", "unable to remove '%s' in database\n", path);
00389
00390 return -EIO;
00391 }
00392
00393
00394 db5_update(file_remove_headslash(newname));
00395
00396 add_log(ADDLOG_OP_SUCCESS, "[fuse]rename", "done.\n");
00397
00398
00399 return -ESUCCESS;
00400 }
00401
00402
00403 int fuse_impl_truncate (const char *path, off_t newsize)
00404 {
00405 check(path != NULL);
00406
00407 add_log(ADDLOG_OPERATION, "[fuse]truncate", "called, args='%s', %u\n", path, newsize);
00408
00409 if (!db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)))
00410 {
00411 add_log(ADDLOG_USER_ERROR, "[fuse]truncate", "unable to find file '%s'\n", path);
00412
00413 return -ENOENT;
00414 }
00415
00416 if (truncate(fuse_localfile, newsize) != 0)
00417 {
00418 fuse_error = errno;
00419 add_log(ADDLOG_FAIL, "[fuse]truncate", "unable to truncate local file: %s\n", strerror(fuse_error));
00420 log_dump("localfile", fuse_localfile);
00421 log_dump("path", path);
00422
00423 return -fuse_error;
00424 }
00425
00426 add_log(ADDLOG_OP_SUCCESS, "[fuse]truncate", "done.\n");
00427
00428
00429 return -ESUCCESS;
00430 }
00431
00432
00433 int fuse_impl_open(const char *path, struct fuse_file_info *filedata)
00434 {
00435 check(path != NULL);
00436 check(filedata != NULL);
00437
00438 add_log(ADDLOG_OPERATION, "[fuse]open", "called, args='%s'\n", path);
00439
00440 if (db5_localfile(file_remove_headslash(path), fuse_localfile, sizeof(fuse_localfile)) != true)
00441 {
00442 add_log(ADDLOG_USER_ERROR, "[fuse]open", "unable to find file '%s'\n", path);
00443
00444 return -ENOENT;
00445 }
00446
00447
00448 filedata->fh = open(fuse_localfile, filedata->flags);
00449
00450 if (filedata->fh == -1)
00451 {
00452 fuse_error = errno;
00453 add_log(ADDLOG_FAIL, "[fuse]open", "open fail: '%s'\n", strerror(fuse_error));
00454
00455 return -fuse_error;
00456 }
00457
00458 add_log(ADDLOG_OP_SUCCESS, "[fuse]open", "done.\n");
00459
00460
00461 return -ESUCCESS;
00462 }
00463
00464
00465 int fuse_impl_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *filedata)
00466 {
00467 int file;
00468 int result;
00469
00470 check(path != NULL);
00471
00472 check(filedata != NULL);
00473 check((int)filedata->fh != 0);
00474
00475 add_log(ADDLOG_OPERATION, "[fuse]read", "called, args='%s',buf:%p,size:%u,off:%u\n", path, buf, size, offset);
00476
00477 if (buf == NULL)
00478 {
00479 add_log(ADDLOG_USER_ERROR, "[fuse]read", "buffer is a NULL pointer\n");
00480
00481 errno = EINVAL;
00482 return 0;
00483 }
00484
00485 file = filedata->fh;
00486
00487 if (lseek(file, offset, SEEK_SET) == (off_t)-1)
00488 {
00489 fuse_error = errno;
00490 add_log(ADDLOG_USER_ERROR, "[fuse]read", "read fail: '%s'\n", strerror(fuse_error));
00491
00492 errno = fuse_error;
00493 return 0;
00494 }
00495
00496 result = read(file, buf, size);
00497 if (result == -1)
00498 {
00499 fuse_error = errno;
00500 add_log(ADDLOG_USER_ERROR, "[fuse]read", "read fail: '%s'\n", strerror(fuse_error));
00501
00502 errno = fuse_error;
00503 return 0;
00504 }
00505
00506 add_log(ADDLOG_OP_SUCCESS, "[fuse]read", "done.\n");
00507
00508 return result;
00509 }
00510
00511
00512 int fuse_impl_write (const char *path, const char *data, size_t size, off_t offset, struct fuse_file_info *filedata)
00513 {
00514 int file;
00515 int result;
00516
00517 check(path != NULL);
00518
00519 check(filedata != NULL);
00520 check((int)filedata->fh != 0);
00521
00522 add_log(ADDLOG_OPERATION, "[fuse]write", "called, args='%s',buf:%p,size:%u,off:%u\n", path, data, size, offset);
00523
00524 if (data == NULL)
00525 {
00526 add_log(ADDLOG_USER_ERROR, "[fuse]write", "buffer is a NULL pointer\n");
00527
00528 errno = EINVAL;
00529 return 0;
00530 }
00531
00532 file = filedata->fh;
00533
00534 if (lseek(file, offset, SEEK_SET) == (off_t)-1)
00535 {
00536 fuse_error = errno;
00537 add_log(ADDLOG_USER_ERROR, "[fuse]write", "write fail: '%s'\n", strerror(fuse_error));
00538
00539 errno = fuse_error;
00540 return 0;
00541 }
00542
00543 result = write(file, data, size);
00544 if (result == -1)
00545 {
00546 fuse_error = errno;
00547 add_log(ADDLOG_USER_ERROR, "[fuse]write", "write fail: '%s'\n", strerror(fuse_error));
00548
00549 errno = fuse_error;
00550 return 0;
00551 }
00552
00553 add_log(ADDLOG_OP_SUCCESS, "[fuse]write", "done.\n");
00554
00555 return result;
00556 }
00557
00558
00559 int fuse_impl_statfs (const char *path, struct statvfs *stat)
00560 {
00561 check(path != NULL);
00562 check(stat != NULL);
00563
00564 add_log(ADDLOG_OPERATION, "[fuse]statfs", "called, args='%s'\n", path);
00565
00566 if (statvfs(CONFIG_DB5_DATA_DIR, stat) != 0)
00567 {
00568 fuse_error = errno;
00569 add_log(ADDLOG_FAIL, "[fuse]statfs", "error during statfs: '%s'\n", strerror(fuse_error));
00570
00571 return -fuse_error;
00572 }
00573
00574 add_log(ADDLOG_OP_SUCCESS, "[fuse]statfs", "done.\n");
00575
00576
00577 return -ESUCCESS;
00578 }
00579
00580
00581 int fuse_impl_flush (const char *path, struct fuse_file_info *filedata)
00582 {
00583 check(path != NULL);
00584 check(filedata != NULL);
00585 check((int)filedata->fh != 0);
00586
00587 add_log(ADDLOG_OPERATION, "[fuse]flush", "called, args='%s'\n", path);
00588
00589
00590 if (db5_update(file_remove_headslash(path)) != true)
00591 {
00592 add_log(ADDLOG_RECOVER, "[fuse]flush", "unable to update database for '%s'\n", path);
00593 }
00594
00595 add_log(ADDLOG_OP_SUCCESS, "[fuse]flush", "done.\n");
00596
00597
00598 return -ESUCCESS;
00599 }
00600
00601
00602 int fuse_impl_readdir(const char *path, void *data, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *filedata)
00603 {
00604 uint32_t i;
00605 char **files_list;
00606
00607 check(path != NULL);
00608 check(filler != NULL);
00609
00610 (void) offset;
00611 (void) filedata;
00612
00613 add_log(ADDLOG_OPERATION, "[fuse]readdir", "called, args='%s',data:%p,filler:%p\n", path, data, filler);
00614
00615
00616 if(strcmp(path, "/") != 0)
00617 {
00618 add_log(ADDLOG_USER_ERROR, "[fuse]readdir", "directory '%s' does not exist\n", path);
00619
00620 return -ENOENT;
00621 }
00622
00623 filler(data, ".", NULL, 0);
00624 filler(data, "..", NULL, 0);
00625
00626 files_list = db5_select_filename();
00627 if (files_list == NULL)
00628 {
00629 add_log(ADDLOG_FAIL, "[fuse]readdir", "unable to get file information form database\n");
00630
00631 return -EIO;
00632 }
00633
00634 for(i=0; files_list[i] != NULL; i++)
00635 {
00636 add_log(ADDLOG_DEBUG, "[fuse]readdir", "%u:'%s'\n", i, files_list[i]);
00637 filler(data, files_list[i], NULL, 0);
00638 free(files_list[i]);
00639 }
00640 free(files_list);
00641
00642 add_log(ADDLOG_OP_SUCCESS, "[fuse]readdir", "done.\n");
00643
00644
00645 return -ESUCCESS;
00646 }
00647
00648
00649 int fuse_impl_fsync(const char *path, int inode, struct fuse_file_info *filedata)
00650 {
00651 check(path != NULL);
00652 check(filedata != NULL);
00653 check((int)filedata->fh != 0);
00654
00655 add_log(ADDLOG_OPERATION, "[fuse]fsync", "called, args='%s'\n", path);
00656
00657 if (fsync((int)filedata->fh == -1))
00658 {
00659 fuse_error = errno;
00660 add_log(ADDLOG_FAIL, "[fuse]fsync", "sync fail: '%s'\n", strerror(fuse_error));
00661
00662 return -fuse_error;
00663 }
00664
00665 add_log(ADDLOG_OP_SUCCESS, "[fuse]fsync", "done.\n");
00666
00667
00668 return -ESUCCESS;
00669 }
00670