2 * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file fsck.c The program used to check an osl table. */
10 #include <sys/types.h>
17 #include "fsck.cmdline.h"
19 static struct fsck_args_info conf
;
24 INIT_STDERR_LOGGING(loglevel
);
27 signed char hexval_table
[256] = {
28 -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
29 -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
30 -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
31 -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
32 -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
33 -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
34 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */
35 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */
36 -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
37 -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
38 -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
39 -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
40 -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
41 -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
42 -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
43 -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
44 -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
45 -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
46 -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
47 -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
48 -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
49 -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
50 -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
51 -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
52 -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
53 -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
54 -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
55 -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
56 -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
57 -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
58 -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
59 -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
62 int asc_to_hash(const char *asc_hash
, int len
, HASH_TYPE
*hash
)
65 const unsigned char *asc
= (const unsigned char *) asc_hash
;
67 while (*asc
&& i
++ < len
) {
68 unsigned int val
= (hexval_table
[asc
[0]] << 4) | hexval_table
[asc
[1]];
79 * check for object boundary violations
81 * test whether the range pointed to by the index entry for a given cell is
82 * contained in mapped data file. This should always be the case. Otherwise
83 * we are in real trouble.
85 static int check_range(struct osl_table
*t
, uint32_t row_num
, uint32_t col_num
)
88 struct osl_object obj
;
89 struct osl_column
*col
;
91 char *map_start
, *obj_start
;
93 ret
= get_cell_index(t
, row_num
, col_num
, &index_entry
);
96 ret
= get_mapped_object(t
, col_num
, row_num
, &obj
);
99 col
= t
->columns
+ col_num
;
100 obj_start
= obj
.data
;
101 map_start
= col
->data_map
.data
;
102 // PARA_INFO_LOG("obj: %p..%p\n", obj_start, obj_start + obj.size);
103 // PARA_INFO_LOG("map: %p..%p\n", map_start, map_start + col->data_map.size);
104 if (obj_start
< map_start
|| obj_start
+ obj
.size
> map_start
+ col
->data_map
.size
) {
105 PARA_CRIT_LOG("range violation in row %u, col %u\n", row_num
,
107 return -E_RANGE_VIOLATION
;
109 PARA_DEBUG_LOG("col %u: ok\n", col_num
);
114 * check all cells of the given table for boundary violations
116 static int check_index_ranges(struct osl_table
*t
)
120 PARA_INFO_LOG("checking for range violations in index\n");
121 //PARA_DEBUG_LOG("%d rows. %d columns\n", t->num_rows, t->desc->num_columns);
122 t
->num_invalid_rows
= 0;
123 for (i
= 0; i
< t
->num_rows
; i
++) {
124 if (row_is_invalid(t
, i
)) {
125 t
->num_invalid_rows
++;
128 for (j
= 0; j
< t
->desc
->num_columns
; j
++) { /* FXIME */
129 const struct osl_column_description
*cd
=
130 get_column_description(t
->desc
, j
);
131 if (cd
->storage_type
!= OSL_MAPPED_STORAGE
)
133 ret
= check_range(t
, i
, j
);
135 if (ret
!= -E_INVALID_OBJECT
&&
136 ret
!= -E_RANGE_VIOLATION
)
138 if (ret
== -E_INVALID_OBJECT
) {
139 PARA_CRIT_LOG("row %d, col %d maps to an "
140 "invalid object\n", i
, j
);
142 ret
= mark_row_invalid(t
, i
);
145 t
->num_invalid_rows
++;
151 if (t
->num_invalid_rows
)
152 PARA_NOTICE_LOG("ranges OK. %d invalid row(s) detected\n",
153 t
->num_invalid_rows
);
155 PARA_INFO_LOG("no invalid rows, no range violations, good\n");
161 static int move_index_entry(struct osl_table
*t
, uint32_t dest
, uint32_t src
)
163 char *dest_ie
, *src_ie
;
164 int ret
= get_row_index(t
, dest
, &dest_ie
);
168 ret
= get_row_index(t
, src
, &src_ie
);
171 PARA_INFO_LOG("moving entry #%u to position %u\n", src
, dest
);
172 memcpy(dest_ie
, src_ie
, t
->row_index_size
);
176 static int map_index(const struct osl_table_description
*desc
, struct osl_object
*map
)
178 char *filename
= index_filename(desc
);
181 ret
= mmap_full_file(filename
, O_RDWR
, &map
->data
, &map
->size
, NULL
);
182 PARA_DEBUG_LOG("mapping index %s: ret: %d, size: %zu\n", filename
, ret
, map
->size
);
187 static int prune_invalid_rows_from_index(struct osl_table
*t
)
189 uint32_t top
= 0, bottom
;
193 if (!t
->num_invalid_rows
) {
194 PARA_INFO_LOG("all rows are valid, good\n");
197 PARA_NOTICE_LOG("deleting %u invalid row(s) (%d bytes) from index\n",
198 t
->num_invalid_rows
, t
->row_index_size
* t
->num_invalid_rows
);
199 bottom
= t
->num_rows
- 1;
200 while (top
< bottom
) {
201 if (!row_is_invalid(t
, top
)) {
205 while (bottom
> top
) {
206 if (row_is_invalid(t
, bottom
)) {
210 /* move bottom index entry to top */
211 move_index_entry(t
, top
, bottom
);
217 PARA_DEBUG_LOG("unmapping index\n");
218 para_munmap(t
->index_map
.data
, t
->index_map
.size
);
219 filename
= index_filename(t
->desc
);
220 ret
= para_truncate(filename
, t
->row_index_size
221 * t
->num_invalid_rows
);
225 ret
= map_index(t
->desc
, &t
->index_map
);
228 t
->num_rows
= table_num_rows(t
);
232 static int check_for_invalid_objects(struct osl_table
*t
, uint32_t **lost_bytes
)
235 const struct osl_column_description
*cd
;
236 uint32_t *loss
= para_malloc(sizeof(uint32_t) * t
->desc
->num_columns
);
238 PARA_INFO_LOG("looking for mapped objects not contained in index\n");
239 /* first count used bytes */
240 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
241 loss
[i
] = t
->columns
[i
].data_map
.size
;
242 for (j
= 0; j
< t
->num_rows
; j
++) {
243 struct osl_object obj
;
244 ret
= get_mapped_object(t
, i
, j
, &obj
);
246 loss
[i
] -= obj
.size
+ 1; /* add one for header byte */
249 if (ret
!= -E_INVALID_OBJECT
)
251 PARA_CRIT_LOG("row %d, col %d points to an invalid "
252 "mapped object, bad\n", j
, i
);
256 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
258 PARA_NOTICE_LOG("column %u contains %u lost bytes\n",
264 PARA_INFO_LOG("all mapped objects are valid, good\n");
272 /* prune_invalid_rows() must be run on the table before calling this */
273 static int prune_mapped_column(struct osl_table
*t
, uint32_t col_num
, int fd
)
276 uint32_t written
= 0;
277 struct osl_column
*col
= t
->columns
+ col_num
;
279 PARA_INFO_LOG("pruning col %u\n", col_num
);
280 for (i
= 0; i
< t
->num_rows
; i
++) {
281 struct osl_object obj
;
284 PARA_DEBUG_LOG("checking row %u/%u\n", i
, t
->num_rows
);
285 ret
= get_mapped_object(t
, col_num
, i
, &obj
);
288 ret
= para_write_all(fd
, (char *)(obj
.data
) - 1, obj
.size
+ 1);
291 written
+= obj
.size
+ 1;
292 ret
= get_row_index(t
, i
, &index_entry
);
295 update_cell_index(index_entry
, col
, written
, obj
.size
);
300 static int prune_objects(struct osl_table
*t
, uint32_t *lost_bytes
)
303 const struct osl_column_description
*cd
;
304 char **col_filenames
= para_calloc(t
->desc
->num_columns
* sizeof(char *));
305 char **new_col_filenames
= para_calloc(t
->desc
->num_columns
* sizeof(char *));
306 char *idx_filename
= index_filename(t
->desc
);
307 char *old_idx_filename
= make_message("%s.bak", idx_filename
);
310 PARA_NOTICE_LOG("removing unreferenced objects from data files\n");
311 /* first make a copy of the index */
312 ret
= para_open(old_idx_filename
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
316 ret
= para_write_all(fd
, t
->index_map
.data
, t
->index_map
.size
);
320 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
323 col_filenames
[i
] = column_filename(t
, i
);
324 new_col_filenames
[i
] = make_message("%s.fsck", col_filenames
[i
]);
325 ret
= para_open(new_col_filenames
[i
], O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
327 goto out_unlink_data
;
329 ret
= prune_mapped_column(t
, i
, fd
);
332 goto out_unlink_data
;
334 ret
= unmap_table(t
, OSL_MARK_CLEAN
);
336 goto out_unlink_data
;
337 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
340 ret
= para_rename(new_col_filenames
[i
], col_filenames
[i
]);
341 if (ret
< 0) { /* we're kinda screwed here */
342 PARA_CRIT_LOG("rename of col %i failed: %s\n", i
,
347 unlink(old_idx_filename
);
348 ret
= map_table(t
, 0);
351 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
)
352 unlink(new_col_filenames
[i
]);
354 free(old_idx_filename
);
356 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
357 free(col_filenames
[i
]);
358 free(new_col_filenames
[i
]);
361 free(new_col_filenames
);
365 static struct osl_column_description hash_tree_table_cols
[] = {
367 .storage_type
= OSL_NO_STORAGE
,
368 .storage_flags
= OSL_RBTREE
| OSL_FIXED_SIZE
| OSL_UNIQUE
,
370 .compare_function
= uint32_compare
,
371 .data_size
= HASH_SIZE
375 static const struct osl_table_description hash_tree_table_desc
= {
376 .dir
= "/", /* irrelevant */
380 .column_descriptions
= hash_tree_table_cols
384 * The hash_tree table contains all hashes of the disk storage name column.
385 * of each row. It is used for checking if a disk storage file has a reference
388 static struct osl_table
*hash_tree_table
;
389 static HASH_TYPE
*hashes
;
391 static int check_disk_storage_column(struct osl_table
*t
, int row_num
,
392 int col_num
, char *ds_name
, unsigned *num_missing_objects
)
396 char *path
= disk_storage_path(t
, col_num
, ds_name
);
397 unsigned dsnc
= t
->disk_storage_name_column
;
398 struct osl_object obj
;
400 PARA_DEBUG_LOG("checking if %s is a regular file\n", path
);
401 ret
= stat(path
, &statbuf
);
402 if (ret
< 0 && errno
== ENOENT
) {
404 (*num_missing_objects
)++;
405 PARA_ERROR_LOG("row %d: object %s is missing\n", row_num
, path
);
406 PARA_NOTICE_LOG("trying to delete row %d\n", row_num
);
407 ret
= osl_get_row(t
, dsnc
, &obj
, &row
);
409 PARA_CRIT_LOG("unable to get row %d\n", row_num
);
410 mark_row_invalid(t
, row_num
);
411 PARA_CRIT_LOG("Please re-run fsck\n");
414 ret
= osl_del_row(t
, row
);
422 ret
= -E_NOT_A_REGULAR_FILE
;
423 if (!(S_IFREG
& statbuf
.st_mode
))
428 static int check_disk_storage_presence(struct osl_table
*t
)
431 struct osl_object obj
, hash_obj
= {.size
= HASH_SIZE
};
433 const struct osl_column_description
*cd
;
434 unsigned dsnc
= t
->disk_storage_name_column
, missing_objects
= 0;
438 hashes
= para_malloc(t
->num_rows
* HASH_SIZE
);
439 PARA_INFO_LOG("looking for missing disk storage objects\n");
440 for (i
= 0; i
< t
->num_rows
; i
++) {
441 if (row_is_invalid(t
, i
))
443 ret
= get_mapped_object(t
, dsnc
, i
, &obj
);
446 hash_object(&obj
, hashes
+ i
* HASH_SIZE
);
447 hash_obj
.data
= hashes
+ i
* HASH_SIZE
;
448 osl_add_row(hash_tree_table
, &hash_obj
);
449 ds_name
= disk_storage_name_of_hash(t
, hashes
+ i
* HASH_SIZE
);
450 FOR_EACH_DISK_STORAGE_COLUMN(j
, t
, cd
) {
451 ret
= check_disk_storage_column(t
, i
, j
, ds_name
,
458 if (!missing_objects
)
459 PARA_INFO_LOG("all referenced disk storage objects exist, good\n");
461 PARA_NOTICE_LOG("%d missing object(s)\n", missing_objects
);
462 return missing_objects
;
468 static int dummy_compare(const struct osl_object
*obj1
, const struct osl_object
*obj2
)
477 static unsigned files_pruned
;
479 int prune_disk_storage_file(const char *path
, void *private_data
)
481 HASH_TYPE hash
[HASH_SIZE
];
482 unsigned flags
= *(unsigned *)private_data
;
483 struct osl_object obj
= {.data
= hash
, .size
= HASH_SIZE
};
486 size_t len
= strlen(path
);
489 PARA_DEBUG_LOG("path: %s\n", path
);
490 if (flags
& OSL_LARGE_TABLE
) {
491 if (len
< HASH_SIZE
* 2 + 2)
493 // PARA_NOTICE_LOG("p: %s\n", path + len - 2 * HASH_SIZE - 1);
494 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
- 1, 1, hash
);
497 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
+ 2, HASH_SIZE
- 1,
501 // PARA_INFO_LOG("high: %x, low: %x, hash: %x\n", high, low, hash);
503 if (len
< 2 * HASH_SIZE
+ 1)
505 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
, 2 * HASH_SIZE
, hash
);
508 // PARA_INFO_LOG("hash: %x\n", hash);
512 char asc
[2 * HASH_SIZE
+ 1];
513 hash_to_asc(hash
, asc
);
514 PARA_NOTICE_LOG("before: %s\nafter: %s\n", path
, asc
);
517 ret
= osl_get_row(hash_tree_table
, 0, &obj
, &row
);
520 PARA_NOTICE_LOG("unreferenced file in hash dir: %s\n", path
);
523 PARA_ERROR_LOG("could not read hash value of %s\n", path
);
525 PARA_NOTICE_LOG("removing %s\n", path
);
531 static int prune_disk_storage_files(struct osl_table
*t
)
534 const struct osl_column_description
*cd
;
536 PARA_INFO_LOG("looking for unreferenced disk storage files\n");
537 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
) {
538 char *dirname
= column_filename(t
, i
);
539 ret
= for_each_file_in_dir(dirname
, prune_disk_storage_file
,
540 (unsigned *)&t
->desc
->flags
);
544 PARA_NOTICE_LOG("%u disk storage files deleted\n",
547 PARA_INFO_LOG("all files are are referenced, good\n");
551 static int check_disk_storage_columns(struct osl_table
*t
)
554 const struct osl_column_description
*cd
;
556 if (!t
->num_disk_storage_columns
) {
557 PARA_INFO_LOG("no disk storage columns in table '%s', "
558 "skipping checks\n", t
->desc
->name
);
561 FOR_EACH_COLUMN(i
, t
->desc
, cd
)
562 t
->desc
->column_descriptions
[i
].compare_function
= dummy_compare
;
563 ret
= init_rbtrees(t
);
566 PARA_INFO_LOG("creating rbtree for disk storage hash values\n");
567 ret
= osl_open_table(&hash_tree_table_desc
, &hash_tree_table
);
570 ret
= check_disk_storage_presence(t
);
572 goto out_close_hash_tree
;
573 ret
= prune_disk_storage_files(t
);
575 osl_close_table(hash_tree_table
, 0);
579 clear_rbtrees(t
); /* TODO why are we doing that here? Seems odd */
583 static void set_dummy_contents(struct osl_table_description
*desc
)
586 struct osl_column_description
*cd
;
588 for (i
= 0; i
< desc
->num_columns
; i
++) {
589 cd
= get_column_description(desc
, i
);
590 cd
->compare_function
= dummy_compare
;
594 static int fsck_init(struct osl_table_description
*desc
, struct osl_table
**t
)
596 struct osl_object map
;
597 int ret
= map_index(desc
, &map
);
601 ret
= read_table_desc(&map
, desc
);
603 para_munmap(map
.data
, map
.size
);
606 set_dummy_contents(desc
);
607 ret
= init_table_structure(desc
, t
);
609 para_munmap(map
.data
, map
.size
);
612 PARA_DEBUG_LOG("unmapping index\n");
613 para_munmap(map
.data
, map
.size
);
614 if (conf
.force_given
)
615 ret
= map_table(*t
, (MAP_TBL_FL_IGNORE_DIRTY
));
617 ret
= map_table(*t
, 0);
619 (*t
)->num_rows
= table_num_rows(*t
);
624 static void fsck_cleanup(struct osl_table
*t
)
630 if (t
->desc
->column_descriptions
) {
631 struct osl_column_description
*cd
;
632 for (i
= 0; i
< t
->desc
->num_columns
; i
++) {
633 cd
= get_column_description(t
->desc
, i
);
634 free((char*)cd
->name
);
636 free(t
->desc
->column_descriptions
);
643 #define ST_CASE(st) case st: return #st
645 const char *get_asc_storage_type(enum osl_storage_type st
)
648 ST_CASE(OSL_MAPPED_STORAGE
);
649 ST_CASE(OSL_DISK_STORAGE
);
650 ST_CASE(OSL_NO_STORAGE
);
655 #define APPEND_ASC_SF(sf, flag, str) do { if (sf & flag) { \
656 if (str) str = para_strcat(str, " | " # flag); \
657 else str = para_strdup(#flag); }} while (0)
660 char *get_asc_storage_flags(enum osl_storage_type sf
)
664 APPEND_ASC_SF(sf
, OSL_RBTREE
, asc_sf
);
665 APPEND_ASC_SF(sf
, OSL_FIXED_SIZE
, asc_sf
);
666 APPEND_ASC_SF(sf
, OSL_UNIQUE
, asc_sf
);
670 static int dump_table_desc(struct osl_table
*t
, int fd
)
672 const struct osl_table_description
*desc
= t
->desc
;
674 struct osl_column_description
*cd
;
675 char *msg
= make_message("static struct osl_column_description cols[] = {\n");
676 ret
= para_write_all(fd
, msg
, strlen(msg
));
680 FOR_EACH_COLUMN(i
, desc
, cd
) {
682 msg
= make_message("\t[%d] = {\n", i
);
683 ret
= para_write_all(fd
, msg
, strlen(msg
));
687 asc_st
= get_asc_storage_type(cd
->storage_type
);
688 msg
= make_message("\t\t.storage_type = %s,\n", asc_st
);
689 ret
= para_write_all(fd
, msg
, strlen(msg
));
693 if (cd
->storage_flags
) {
694 char *asc_sf
= get_asc_storage_flags(cd
->storage_flags
);
695 msg
= make_message("\t\t,storage_flags = %s,\n", asc_sf
);
697 ret
= para_write_all(fd
, msg
, strlen(msg
));
702 if (cd
->storage_flags
& OSL_FIXED_SIZE
) {
703 msg
= make_message("\t\t.data_size = %u,\n", cd
->data_size
);
704 ret
= para_write_all(fd
, msg
, strlen(msg
));
709 msg
= make_message("\t\t.name = \"%s\",\n", cd
->name
);
710 ret
= para_write_all(fd
, msg
, strlen(msg
));
714 if (cd
->storage_flags
& OSL_RBTREE
) {
715 msg
= make_message("\t\t.compare_function = compare_func,\n");
716 ret
= para_write_all(fd
, msg
, strlen(msg
));
721 msg
= make_message("\t},\n");
722 ret
= para_write_all(fd
, msg
, strlen(msg
));
727 msg
= make_message("};\n");
728 ret
= para_write_all(fd
, msg
, strlen(msg
));
735 static int dump_row(struct osl_table
*t
, unsigned row_num
, const char *row_dir
)
738 const struct osl_column_description
*cd
;
740 struct osl_object obj
;
742 HASH_TYPE hash
[HASH_SIZE
];
745 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
746 ret
= get_mapped_object(t
, i
, row_num
, &obj
);
749 filename
= make_message("%s/col_%03u", row_dir
, i
);
750 ret
= para_write_file(filename
, obj
.data
, obj
.size
);
755 if (!t
->num_disk_storage_columns
)
757 dsnc
= t
->disk_storage_name_column
;
758 ret
= get_mapped_object(t
, dsnc
, row_num
, &obj
);
761 hash_object(&obj
, hash
);
762 ds_name
= disk_storage_name_of_hash(t
, hash
);
763 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
) {
764 filename
= disk_storage_path(t
, i
, ds_name
);
765 ret
= mmap_full_file(filename
, O_RDONLY
, &obj
.data
, &obj
.size
, NULL
);
769 filename
= make_message("%s/col_%03u", row_dir
, i
);
770 ret
= para_write_file(filename
, obj
.data
, obj
.size
);
781 static int dump_rows(char *dump_dir
, struct osl_table
*t
)
784 char *current_dir
= NULL
;
787 for (i
= 0; i
< t
->num_rows
; i
++) {
789 if (row_is_invalid(t
, i
))
793 current_dir
= make_message("%s/rows_%u-%u", dump_dir
, i
, i
+ 999);
794 PARA_NOTICE_LOG("dumping rows %u - %u\n", i
, i
+ 999);
795 ret
= para_mkdir(current_dir
, 0777);
796 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
799 row_dir
= make_message("%s/row_%03u", current_dir
, i
);
800 ret
= para_mkdir(row_dir
, 0777);
801 if (ret
< 0 && !is_errno(-ret
, EEXIST
)) {
805 ret
= dump_row(t
, i
, row_dir
);
815 static int dump_table(char *dump_dir
, struct osl_table_description
*desc
)
817 struct osl_table
*t
= NULL
;
818 int fd
, ret
= fsck_init(desc
, &t
);
820 char *table_dump_dir
= NULL
;
824 ret
= para_mkdir(dump_dir
, 0777);
825 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
827 table_dump_dir
= make_message("%s/%s", dump_dir
, desc
->name
);
828 ret
= para_mkdir(table_dump_dir
, 0777);
829 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
831 desc_file
= make_message("%s/table_description.c", table_dump_dir
);
832 ret
= para_open(desc_file
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
837 ret
= dump_table_desc(t
, fd
);
841 ret
= dump_rows(table_dump_dir
, t
);
843 free(table_dump_dir
);
848 static int fsck(struct osl_table_description
*desc
)
851 struct osl_table
*t
= NULL
;
852 uint32_t *lost_bytes
= NULL
;
854 ret
= fsck_init(desc
, &t
);
857 ret
= check_index_ranges(t
);
860 ret
= check_disk_storage_columns(t
);
863 ret
= prune_invalid_rows_from_index(t
);
866 ret
= check_for_invalid_objects(t
, &lost_bytes
);
869 if (ret
> 0) { /* at least one mapped data file needs pruning */
870 ret
= prune_objects(t
, lost_bytes
);
876 unmap_table(t
, OSL_MARK_CLEAN
);
882 static int check_table(char *base_dir
, char *table_name
)
884 struct osl_table_description desc
= {
885 .column_descriptions
= NULL
,
891 PARA_INFO_LOG("checking table %s\n", table_name
);
892 if (!conf
.no_fsck_given
) {
898 if (!conf
.dump_dir_given
|| !*conf
.dump_dir_arg
)
900 ret
= dump_table(conf
.dump_dir_arg
, &desc
);
903 PARA_ERROR_LOG("failed to check table %s\n", table_name
);
905 PARA_NOTICE_LOG("successfully checked table %s\n", table_name
);
909 static int check_all_tables(char *base_dir
)
912 struct dirent
*entry
;
913 int cwd_fd
, ret2
, ret
= para_opendir(base_dir
, &dir
, &cwd_fd
);
917 while ((entry
= readdir(dir
))) {
920 if (!strcmp(entry
->d_name
, "."))
922 if (!strcmp(entry
->d_name
, ".."))
924 if (lstat(entry
->d_name
, &s
) == -1)
929 ret
= check_table(base_dir
, entry
->d_name
);
934 ret2
= para_fchdir(cwd_fd
);
935 if (ret2
< 0 && ret
>= 0)
942 * The praslash database check program.
944 * \param argc Usual arg count.
945 * \param argv Usual arg vector.
947 * \return \p EXIT_SUCCESS or \p EXIT_FAILURE.
949 int main(int argc
, char **argv
)
952 char *base_dir
= NULL
;
954 ret
= fsck_cmdline_parser(argc
, argv
, &conf
);
956 ret
= -E_FSCK_SYNTAX
;
959 HANDLE_VERSION_FLAG("fsck", conf
);
960 loglevel
= get_loglevel_by_name(conf
.loglevel_arg
);
961 if (conf
.base_dir_given
)
962 base_dir
= para_strdup(conf
.base_dir_arg
);
964 char *home
= para_homedir();
965 base_dir
= make_message("%s/.paraslash/afs_database", home
);
968 if (!conf
.inputs_num
) {
969 ret
= check_all_tables(base_dir
);
972 for (i
= 0; i
< conf
.inputs_num
; i
++) {
973 ret
= check_table(base_dir
, conf
.inputs
[i
]);
979 PARA_ERROR_LOG("%s%s: %s\n",
980 base_dir
? "base_dir: " : "",
981 base_dir
? base_dir
: "",
985 PARA_NOTICE_LOG("success\n");
988 return ret
< 0? EXIT_FAILURE
: EXIT_SUCCESS
;