00001 #include <dirent.h>
00002 #include <fcntl.h>
00003 #include <limits.h>
00004 #include <stdbool.h>
00005 #include <stdio.h>
00006 #include <strings.h>
00007 #include <sys/stat.h>
00008 #include <sys/types.h>
00009 #include <unistd.h>
00010
00011 #include "check.h"
00012 #include "config.h"
00013 #include "db5_dat.h"
00014 #include "db5.h"
00015 #include "db5_hdr.h"
00016 #include "db5_types.h"
00017 #include "file.h"
00018 #include "utf8.h"
00019 #include "wstring.h"
00020
00021 void fsck_init(const char *fuse_device);
00022 void fsck_check(const bool fix);
00023 bool fsck_check_step1(const bool fix);
00024 bool fsck_check_step2(const bool fix);
00025 bool fsck_check_step3(const bool fix);
00026 bool fsck_check_step4(const bool fix);
00027 bool fsck_check_step5(const bool fix);
00028 void fsck_free();
00029
00030 static uint32_t real_count;
00031 static db5_row row;
00032
00033 void fsck_init(const char *fuse_device)
00034 {
00035 check(fuse_device != NULL);
00036
00037 if (file_set_context(fuse_device) != true)
00038 {
00039
00040 fprintf(stderr, "fsck.db5: fatal, unable to reach device '%s'\n", fuse_device);
00041 exit(EXIT_FAILURE);
00042 }
00043
00044 open_log();
00045 add_log(ADDLOG_NOTICE, "[fsck]init", "fsck.db5 will scan device '%s'\n", fuse_device);
00046
00047 if (db5_init() != true)
00048 {
00049 add_log(ADDLOG_CRITICAL, "[fsck]init", "unable to initialize filesystem\n");
00050 exit(EXIT_FAILURE);
00051 }
00052 }
00053
00054 void fsck_free()
00055 {
00056 add_log(ADDLOG_NOTICE, "[fsck]free", "scan complete\n");
00057 add_log(ADDLOG_DEBUG, "[fsck]free", "exiting program\n");
00058
00059 db5_free();
00060 close_log();
00061 }
00062
00063 void fsck_check(const bool fix)
00064 {
00065 add_log(ADDLOG_NOTICE, "[fsck]check", "filesystem will be checked\n");
00066
00067 add_log(ADDLOG_OPERATION, "[fsck]check", "step1: check number of files\n");
00068 if (fsck_check_step1(fix))
00069 {
00070 add_log(ADDLOG_NOTICE, "[fsck]check", "step1 ok\n");
00071 }
00072 else
00073 {
00074 add_log(ADDLOG_CRITICAL, "[fsck]check", "error during step1\n");
00075 return;
00076 }
00077
00078 add_log(ADDLOG_OPERATION, "[fsck]check", "step2: check music directory\n");
00079 if (fsck_check_step2(fix))
00080 {
00081 add_log(ADDLOG_NOTICE, "[fsck]check", "step2 ok\n");
00082 }
00083 else
00084 {
00085 add_log(ADDLOG_CRITICAL, "[fsck]check", "error during step2\n");
00086 return;
00087 }
00088
00089 add_log(ADDLOG_OPERATION, "[fsck]check", "step3: check file file existence and refresh information\n");
00090 if (fsck_check_step3(fix))
00091 {
00092 add_log(ADDLOG_NOTICE, "[fsck]check", "step3 ok\n");
00093 }
00094 else
00095 {
00096 add_log(ADDLOG_CRITICAL, "[fsck]check", "error during step3\n");
00097 return;
00098 }
00099
00100 add_log(ADDLOG_OPERATION, "[fsck]check", "step4: searching orphan files\n");
00101 if (fsck_check_step4(fix))
00102 {
00103 add_log(ADDLOG_NOTICE, "[fsck]check", "step4 ok\n");
00104 }
00105 else
00106 {
00107 add_log(ADDLOG_CRITICAL, "[fsck]check", "error during step4\n");
00108 return;
00109 }
00110
00111 add_log(ADDLOG_OPERATION, "[fsck]check", "step5: re-generating index\n");
00112 if (fsck_check_step5(fix))
00113 {
00114 add_log(ADDLOG_NOTICE, "[fsck]check", "step5 ok\n");
00115 }
00116 else
00117 {
00118 add_log(ADDLOG_CRITICAL, "[fsck]check", "error during step5\n");
00119 return;
00120 }
00121 }
00122
00123 bool fsck_check_step1(const bool fix)
00124 {
00125 uint32_t local_count;
00126
00127
00128 local_count = db5_hdr_count();
00129 add_log(ADDLOG_DEBUG, "[fsck]step1", "%u files registered\n", local_count);
00130
00131 real_count = 0;
00132 while(real_count <= CONFIG_MAX_DB5_ENTRY)
00133 {
00134 if (!db5_dat_select_row(real_count, &row))
00135 {
00136 break;
00137 }
00138 real_count++;
00139 }
00140 add_log(ADDLOG_DEBUG, "[fsck]step1", "%u files are detected\n", real_count);
00141
00142 if (real_count != local_count)
00143 {
00144 if (fix)
00145 {
00146 add_log(ADDLOG_FAIL, "[fsck]step1", "correct the number of files to %u\n", real_count);
00147 db5_hdr_grow(real_count-local_count);
00148 }
00149 else
00150 {
00151 add_log(ADDLOG_FAIL, "[fsck]step1", "number of files should be %u\n", real_count);
00152 }
00153 }
00154
00155 return true;
00156 }
00157
00158 bool fsck_check_step2(const bool fix)
00159 {
00160 struct stat dirstat;
00161
00162
00163 if (stat(CONFIG_MUSIC_PATH, &dirstat) != 0)
00164 {
00165 if (fix)
00166 {
00167 add_log(ADDLOG_FAIL, "[fsck]step2", "creates directory '%s'\n", CONFIG_MUSIC_PATH);
00168 if (mkdir(CONFIG_MUSIC_PATH, 0755) != 0)
00169 {
00170 add_log(ADDLOG_CRITICAL, "[fsck]step2", "unable to create directory '%s'\n", CONFIG_MUSIC_PATH);
00171 return false;
00172 }
00173 }
00174 else
00175 {
00176 add_log(ADDLOG_CRITICAL, "[fsck]step2", "directory '%s' should exists\n", CONFIG_MUSIC_PATH);
00177 return false;
00178 }
00179 }
00180 else
00181 {
00182 if (!S_ISDIR(dirstat.st_mode))
00183 {
00184 add_log(ADDLOG_CRITICAL, "[fsck]step2", "'%s' is not a directory\n", CONFIG_MUSIC_PATH);
00185 return false;
00186 }
00187 else
00188 {
00189 add_log(ADDLOG_DEBUG, "[fsck]step2", "directory '%s' found\n", CONFIG_MUSIC_PATH);
00190 }
00191 }
00192
00193 return true;
00194 }
00195
00196 bool fsck_check_step3(const bool fix)
00197 {
00198 int fd;
00199 char localfile[PATH_MAX];
00200 uint32_t i;
00201
00202
00203 for(i=0; i < real_count; i++)
00204 {
00205 if (db5_dat_select_row(i, &row) != true)
00206 {
00207 add_log(ADDLOG_FAIL, "[fsck]step3", "unable to get file information form database, entry id: %u\n", i);
00208 return NULL;
00209 }
00210 ws_wstoa(row.filename, membersizeof(db5_row, filename));
00211
00212 db5_shortname_to_localfile(row.filename, localfile, sizeof(localfile));
00213
00214 if (!file_exists(localfile))
00215 {
00216 add_log(ADDLOG_FAIL, "[fsck]step3", "local file of entry %u does not exists\n", i);
00217 log_dump_latin1("shortname", row.filename);
00218 log_dump("localfile", localfile);
00219
00220 if (fix)
00221 {
00222 fd = creat(localfile, 0644);
00223 if (fd == -1)
00224 {
00225 add_log(ADDLOG_FAIL, "[fsck]step3", "unable to recreate file '%s'\n", localfile);
00226 }
00227 else
00228 {
00229 add_log(ADDLOG_NOTICE, "[fsck]step3", "file '%s' recreated\n", localfile);
00230 close(fd);
00231 }
00232 }
00233 }
00234
00235 if (fix)
00236 {
00237 db5_generate_row(localfile, &row);
00238 db5_widechar_row(&row);
00239 db5_dat_update(i, &row);
00240 }
00241 }
00242
00243 return true;
00244 }
00245
00246 bool fsck_check_step4(const bool fix)
00247 {
00248 DIR *music;
00249 struct dirent *entry;
00250 char filename_latin1[PATH_MAX];
00251 char namebuffer[PATH_MAX];
00252 char *dir, *file, *ext;
00253 db5_row row;
00254
00255 music = opendir(CONFIG_MUSIC_PATH);
00256 if (music == NULL)
00257 {
00258 add_log(ADDLOG_FAIL, "[fsck]step4", "unable to open music directory\n");
00259 return false;
00260 }
00261
00262 entry = readdir(music);
00263 while(entry != NULL)
00264 {
00265 utf8_iso8859(entry->d_name, filename_latin1, sizeof(filename_latin1));
00266 strncpy(namebuffer, filename_latin1, sizeof(namebuffer));
00267 file_path_explode(namebuffer, &dir, &file, &ext);
00268
00269 if (ext == NULL)
00270 {
00271 ext = "";
00272 }
00273
00274
00275 if (strcasecmp(ext, CONFIG_ASF_EXT) == 0 || strcasecmp(ext, CONFIG_MPEG_EXT) == 0)
00276 {
00277
00278 if (strlen(filename_latin1) <= (membersizeof(db5_row, filename)/2))
00279 {
00280 if (db5_dat_select_by_filename(filename_latin1) == DB5_ROW_NOT_FOUND)
00281 {
00282 add_log(ADDLOG_FAIL, "[fsck]step4", "file '%s' is not in database\n", entry->d_name);
00283
00284 if (fix)
00285 {
00286
00287 db5_shortname_to_localfile(filename_latin1, namebuffer, sizeof(namebuffer));
00288 if (db5_generate_row(namebuffer, &row) != true)
00289 {
00290 add_log(ADDLOG_FAIL, "[fsck]step4", "unable to generate row from file '%s'\n", namebuffer);
00291 return false;
00292 }
00293 db5_widechar_row(&row);
00294
00295
00296 row.hidden = (uint32_t)(filename_latin1[0] == '.');
00297
00298
00299 if (db5_dat_insert(&row) != true)
00300 {
00301 add_log(ADDLOG_FAIL, "[fsck]step4", "unable to insert row in database '%s'\n", namebuffer);
00302 return false;
00303 }
00304 else
00305 {
00306 add_log(ADDLOG_NOTICE, "[fsck]step4", "file '%s' added\n", namebuffer);
00307 }
00308 }
00309 }
00310 }
00311 }
00312
00313 entry = readdir(music);
00314 }
00315 closedir(music);
00316 return true;
00317 }
00318
00319 bool fsck_check_step5(const bool fix)
00320 {
00321 if (fix)
00322 {
00323 return db5_index();
00324 }
00325 else
00326 {
00327 add_log(ADDLOG_DEBUG, "[fsck]step5", "read-only, step skipped\n");
00328 return true;
00329 }
00330 }
00331
00332 void usage()
00333 {
00334 fprintf(stderr, "usage: fsck.db5 [-f] <device>\n\n");
00335 fprintf(stderr, " -f use this option to fix errors.\n");
00336 fprintf(stderr, " Default behavior is only to list errors without fixing.\n");
00337 fprintf(stderr, " device the path of db5 device\n\n");
00338 fprintf(stderr, "check a db5fuse filesystem to fix it.\n\n");
00339
00340 exit(EXIT_FAILURE);
00341 }
00342
00343 int main(int argc, char *argv[])
00344 {
00345 const char *device;
00346 bool fix;
00347
00348 if (argc == 2)
00349 {
00350 device = argv[1];
00351 fix = false;
00352 }
00353 else if (argc == 3)
00354 {
00355 if (strcmp(argv[1], "-f") == 0)
00356 {
00357 device = argv[2];
00358 fix = true;
00359 }
00360 else if (strcmp(argv[2], "-f") == 0)
00361 {
00362 device = argv[1];
00363 fix = true;
00364 }
00365 else
00366 {
00367 usage();
00368 }
00369 }
00370 else
00371 {
00372 usage();
00373 }
00374
00375 printf("Scan of '%s'.", device);
00376 printf("Informations will be stored in file '%s/%s'\n", device, CONFIG_LOG_FILENAME);
00377
00378 fsck_init(device);
00379 fsck_check(fix);
00380 fsck_free();
00381
00382 printf("done.\n");
00383
00384 return EXIT_SUCCESS;
00385 }
00386