2 * Copyright (C) 1997-2007 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
;
22 INIT_STDERR_LOGGING(conf
.loglevel_arg
);
25 signed char hexval_table
[256] = {
26 -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
27 -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
28 -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
29 -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
30 -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
31 -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
32 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */
33 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */
34 -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
35 -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
36 -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
37 -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
38 -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
39 -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
40 -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
41 -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
42 -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
43 -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
44 -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
45 -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
46 -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
47 -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
48 -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
49 -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
50 -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
51 -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
52 -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
53 -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
54 -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
55 -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
56 -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
57 -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
60 int asc_to_hash(const char *asc_hash
, int len
, HASH_TYPE
*hash
)
63 const unsigned char *asc
= (const unsigned char *) asc_hash
;
65 while (*asc
&& i
++ < len
) {
66 unsigned int val
= (hexval_table
[asc
[0]] << 4) | hexval_table
[asc
[1]];
77 * check for object boundary violations
79 * test whether the range pointed to by the index entry for a given cell is
80 * contained in mapped data file. This should always be the case. Otherwise
81 * we are in real trouble.
83 static int check_range(struct osl_table
*t
, uint32_t row_num
, uint32_t col_num
)
86 struct osl_object obj
;
87 struct osl_column
*col
;
89 char *map_start
, *obj_start
;
91 ret
= get_cell_index(t
, row_num
, col_num
, &index_entry
);
94 ret
= get_mapped_object(t
, col_num
, row_num
, &obj
);
97 col
= t
->columns
+ col_num
;
99 map_start
= col
->data_map
.data
;
100 // PARA_INFO_LOG("obj: %p..%p\n", obj_start, obj_start + obj.size);
101 // PARA_INFO_LOG("map: %p..%p\n", map_start, map_start + col->data_map.size);
102 if (obj_start
< map_start
|| obj_start
+ obj
.size
> map_start
+ col
->data_map
.size
) {
103 PARA_CRIT_LOG("range violation in row %u, col %u\n", row_num
,
105 return -E_RANGE_VIOLATION
;
107 PARA_DEBUG_LOG("col %u: ok\n", col_num
);
112 * check all cells of the given table for boundary violations
114 static int check_index_ranges(struct osl_table
*t
)
118 PARA_NOTICE_LOG("checking for range violations in index\n");
119 //PARA_DEBUG_LOG("%d rows. %d columns\n", t->num_rows, t->desc->num_columns);
120 t
->num_invalid_rows
= 0;
121 for (i
= 0; i
< t
->num_rows
; i
++) {
122 if (row_is_invalid(t
, i
)) {
123 t
->num_invalid_rows
++;
126 for (j
= 0; j
< t
->desc
->num_columns
; j
++) { /* FXIME */
127 const struct osl_column_description
*cd
=
128 get_column_description(t
->desc
, j
);
129 if (cd
->storage_type
!= OSL_MAPPED_STORAGE
)
131 ret
= check_range(t
, i
, j
);
133 if (ret
!= -E_INVALID_OBJECT
&&
134 ret
!= -E_RANGE_VIOLATION
)
136 if (ret
== -E_INVALID_OBJECT
) {
137 PARA_CRIT_LOG("row %d, col %d maps to an "
138 "invalid object\n", i
, j
);
140 ret
= mark_row_invalid(t
, i
);
143 t
->num_invalid_rows
++;
149 if (t
->num_invalid_rows
)
150 PARA_NOTICE_LOG("ranges OK. %d invalid row(s) detected\n",
151 t
->num_invalid_rows
);
153 PARA_INFO_LOG("no invalid rows, no range violations, good\n");
159 static int move_index_entry(struct osl_table
*t
, uint32_t dest
, uint32_t src
)
161 char *dest_ie
, *src_ie
;
162 int ret
= get_row_index(t
, dest
, &dest_ie
);
166 ret
= get_row_index(t
, src
, &src_ie
);
169 PARA_INFO_LOG("moving entry #%u to position %u\n", src
, dest
);
170 memcpy(dest_ie
, src_ie
, t
->row_index_size
);
174 static int map_index(const struct osl_table_description
*desc
, struct osl_object
*map
)
176 char *filename
= index_filename(desc
);
179 ret
= mmap_full_file(filename
, O_RDWR
, &map
->data
, &map
->size
, NULL
);
180 PARA_INFO_LOG("mapping index %s: ret: %d, size: %zu\n", filename
, ret
, map
->size
);
185 static int prune_invalid_rows_from_index(struct osl_table
*t
)
187 uint32_t top
= 0, bottom
;
191 if (!t
->num_invalid_rows
) {
192 PARA_INFO_LOG("all rows are valid, good\n");
195 PARA_NOTICE_LOG("deleting %u invalid row(s) (%d bytes) from index\n",
196 t
->num_invalid_rows
, t
->row_index_size
* t
->num_invalid_rows
);
197 bottom
= t
->num_rows
- 1;
198 while (top
< bottom
) {
199 if (!row_is_invalid(t
, top
)) {
203 while (bottom
> top
) {
204 if (row_is_invalid(t
, bottom
)) {
208 /* move bottom index entry to top */
209 move_index_entry(t
, top
, bottom
);
215 PARA_INFO_LOG("unmapping index\n");
216 para_munmap(t
->index_map
.data
, t
->index_map
.size
);
217 filename
= index_filename(t
->desc
);
218 ret
= para_truncate(filename
, t
->row_index_size
219 * t
->num_invalid_rows
);
223 ret
= map_index(t
->desc
, &t
->index_map
);
226 t
->num_rows
= table_num_rows(t
);
230 static int check_for_invalid_objects(struct osl_table
*t
, uint32_t **lost_bytes
)
233 const struct osl_column_description
*cd
;
234 uint32_t *loss
= para_malloc(sizeof(uint32_t) * t
->desc
->num_columns
);
236 PARA_NOTICE_LOG("looking for mapped objects not contained in index\n");
237 /* first count used bytes */
238 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
239 loss
[i
] = t
->columns
[i
].data_map
.size
;
240 for (j
= 0; j
< t
->num_rows
; j
++) {
241 struct osl_object obj
;
242 ret
= get_mapped_object(t
, i
, j
, &obj
);
244 loss
[i
] -= obj
.size
+ 1; /* add one for header byte */
247 if (ret
!= -E_INVALID_OBJECT
)
249 PARA_CRIT_LOG("row %d, col %d points to an invalid "
250 "mapped object, bad\n", j
, i
);
254 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
256 PARA_NOTICE_LOG("column %u contains %u lost bytes\n",
262 PARA_INFO_LOG("all mapped objects are valid, good\n");
270 /* prune_invalid_rows() must be run on the table before calling this */
271 static int prune_mapped_column(struct osl_table
*t
, uint32_t col_num
, int fd
)
274 uint32_t written
= 0;
275 struct osl_column
*col
= t
->columns
+ col_num
;
277 PARA_INFO_LOG("pruning col %u\n", col_num
);
278 for (i
= 0; i
< t
->num_rows
; i
++) {
279 struct osl_object obj
;
282 PARA_DEBUG_LOG("checking row %u/%u\n", i
, t
->num_rows
);
283 ret
= get_mapped_object(t
, col_num
, i
, &obj
);
286 ret
= para_write_all(fd
, (char *)(obj
.data
) - 1, obj
.size
+ 1);
289 written
+= obj
.size
+ 1;
290 ret
= get_row_index(t
, i
, &index_entry
);
293 update_cell_index(index_entry
, col
, written
, obj
.size
);
298 static int prune_objects(struct osl_table
*t
, uint32_t *lost_bytes
)
301 const struct osl_column_description
*cd
;
302 char **col_filenames
= para_calloc(t
->desc
->num_columns
* sizeof(char *));
303 char **new_col_filenames
= para_calloc(t
->desc
->num_columns
* sizeof(char *));
304 char *idx_filename
= index_filename(t
->desc
);
305 char *old_idx_filename
= make_message("%s.bak", idx_filename
);
308 PARA_NOTICE_LOG("removing unreferenced objects from data files\n");
309 /* first make a copy of the index */
310 ret
= para_open(old_idx_filename
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
314 ret
= para_write_all(fd
, t
->index_map
.data
, t
->index_map
.size
);
318 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
321 col_filenames
[i
] = column_filename(t
, i
);
322 new_col_filenames
[i
] = make_message("%s.fsck", col_filenames
[i
]);
323 ret
= para_open(new_col_filenames
[i
], O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
325 goto out_unlink_data
;
327 ret
= prune_mapped_column(t
, i
, fd
);
330 goto out_unlink_data
;
332 ret
= unmap_table(t
, OSL_MARK_CLEAN
);
334 goto out_unlink_data
;
335 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
338 ret
= para_rename(new_col_filenames
[i
], col_filenames
[i
]);
339 if (ret
< 0) { /* we're kinda screwed here */
340 PARA_CRIT_LOG("rename of col %i failed: %s\n", i
,
345 unlink(old_idx_filename
);
346 ret
= map_table(t
, 0);
349 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
)
350 unlink(new_col_filenames
[i
]);
352 free(old_idx_filename
);
354 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
355 free(col_filenames
[i
]);
356 free(new_col_filenames
[i
]);
359 free(new_col_filenames
);
363 static struct osl_column_description hash_tree_table_cols
[] = {
365 .storage_type
= OSL_NO_STORAGE
,
366 .storage_flags
= OSL_RBTREE
| OSL_FIXED_SIZE
| OSL_UNIQUE
,
368 .compare_function
= uint32_compare
,
369 .data_size
= HASH_SIZE
373 static const struct osl_table_description hash_tree_table_desc
= {
374 .dir
= "/", /* irrelevant */
378 .column_descriptions
= hash_tree_table_cols
382 * The hash_tree table contains all hashes of the disk storage name column.
383 * of each row. It is used for checking if a disk storage file has a reference
386 static struct osl_table
*hash_tree_table
;
387 static HASH_TYPE
*hashes
;
389 static int check_disk_storage_column(struct osl_table
*t
, int row_num
,
390 int col_num
, char *ds_name
, unsigned *num_missing_objects
)
394 char *path
= disk_storage_path(t
, col_num
, ds_name
);
395 unsigned dsnc
= t
->disk_storage_name_column
;
396 struct osl_object obj
;
398 PARA_DEBUG_LOG("checking if %s is a regular file\n", path
);
399 ret
= stat(path
, &statbuf
);
400 if (ret
< 0 && errno
== ENOENT
) {
402 (*num_missing_objects
)++;
403 PARA_ERROR_LOG("row %d: object %s is missing\n", row_num
, path
);
404 PARA_NOTICE_LOG("trying to delete row %d\n", row_num
);
405 ret
= osl_get_row(t
, dsnc
, &obj
, &row
);
407 PARA_CRIT_LOG("unable to get row %d\n", row_num
);
408 mark_row_invalid(t
, row_num
);
409 PARA_CRIT_LOG("Please re-run fsck\n");
412 ret
= osl_del_row(t
, row
);
420 ret
= -E_NOT_A_REGULAR_FILE
;
421 if (!(S_IFREG
& statbuf
.st_mode
))
426 static int check_disk_storage_presence(struct osl_table
*t
)
429 struct osl_object obj
, hash_obj
= {.size
= HASH_SIZE
};
431 const struct osl_column_description
*cd
;
432 unsigned dsnc
= t
->disk_storage_name_column
, missing_objects
= 0;
436 hashes
= para_malloc(t
->num_rows
* HASH_SIZE
);
437 PARA_NOTICE_LOG("looking for missing disk storage objects\n");
438 for (i
= 0; i
< t
->num_rows
; i
++) {
439 if (row_is_invalid(t
, i
))
441 ret
= get_mapped_object(t
, dsnc
, i
, &obj
);
444 hash_object(&obj
, hashes
+ i
* HASH_SIZE
);
445 hash_obj
.data
= hashes
+ i
* HASH_SIZE
;
446 osl_add_row(hash_tree_table
, &hash_obj
);
447 ds_name
= disk_storage_name_of_hash(t
, hashes
+ i
* HASH_SIZE
);
448 FOR_EACH_DISK_STORAGE_COLUMN(j
, t
, cd
) {
449 ret
= check_disk_storage_column(t
, i
, j
, ds_name
,
456 if (!missing_objects
)
457 PARA_INFO_LOG("all referenced disk storage objects exist, good\n");
459 PARA_NOTICE_LOG("%d missing object(s)\n", missing_objects
);
460 return missing_objects
;
466 static int dummy_compare(const struct osl_object
*obj1
, const struct osl_object
*obj2
)
475 static unsigned files_pruned
;
477 int prune_disk_storage_file(const char *path
, const void *private_data
)
479 HASH_TYPE hash
[HASH_SIZE
];
480 unsigned flags
= *(unsigned *)private_data
;
481 struct osl_object obj
= {.data
= hash
, .size
= HASH_SIZE
};
484 size_t len
= strlen(path
);
487 PARA_DEBUG_LOG("path: %s\n", path
);
488 if (flags
& OSL_LARGE_TABLE
) {
489 if (len
< HASH_SIZE
* 2 + 2)
491 // PARA_NOTICE_LOG("p: %s\n", path + len - 2 * HASH_SIZE - 1);
492 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
- 1, 1, hash
);
495 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
+ 2, HASH_SIZE
- 1,
499 // PARA_INFO_LOG("high: %x, low: %x, hash: %x\n", high, low, hash);
501 if (len
< 2 * HASH_SIZE
+ 1)
503 ret
= asc_to_hash(path
+ len
- 2 * HASH_SIZE
, 2 * HASH_SIZE
, hash
);
506 // PARA_INFO_LOG("hash: %x\n", hash);
510 char asc
[2 * HASH_SIZE
+ 1];
511 hash_to_asc(hash
, asc
);
512 PARA_NOTICE_LOG("before: %s\nafter: %s\n", path
, asc
);
515 ret
= osl_get_row(hash_tree_table
, 0, &obj
, &row
);
518 PARA_NOTICE_LOG("unreferenced file in hash dir: %s\n", path
);
521 PARA_ERROR_LOG("could not read hash value of %s\n", path
);
523 PARA_NOTICE_LOG("removing %s\n", path
);
529 static int prune_disk_storage_files(struct osl_table
*t
)
532 const struct osl_column_description
*cd
;
534 PARA_NOTICE_LOG("looking for unreferenced disk storage files\n");
535 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
) {
536 char *dirname
= column_filename(t
, i
);
537 ret
= for_each_file_in_dir(dirname
, prune_disk_storage_file
, &t
->desc
->flags
);
541 PARA_NOTICE_LOG("%u disk storage files deleted\n",
544 PARA_INFO_LOG("all files are are referenced, good\n");
548 static int check_disk_storage_columns(struct osl_table
*t
)
551 const struct osl_column_description
*cd
;
553 if (!t
->num_disk_storage_columns
) {
554 PARA_NOTICE_LOG("no disk storage columns in table '%s', "
555 "skipping checks\n", t
->desc
->name
);
558 FOR_EACH_COLUMN(i
, t
->desc
, cd
)
559 t
->desc
->column_descriptions
[i
].compare_function
= dummy_compare
;
560 ret
= init_rbtrees(t
);
563 PARA_NOTICE_LOG("creating rbtree for disk storage hash values\n");
564 ret
= osl_open_table(&hash_tree_table_desc
, &hash_tree_table
);
567 ret
= check_disk_storage_presence(t
);
569 goto out_close_hash_tree
;
570 ret
= prune_disk_storage_files(t
);
572 osl_close_table(hash_tree_table
, 0);
576 clear_rbtrees(t
); /* TODO why are we doing that here? Seems odd */
580 static void set_dummy_contents(struct osl_table_description
*desc
)
583 struct osl_column_description
*cd
;
585 for (i
= 0; i
< desc
->num_columns
; i
++) {
586 cd
= get_column_description(desc
, i
);
587 cd
->compare_function
= dummy_compare
;
591 static int fsck_init(struct osl_table_description
*desc
, struct osl_table
**t
)
593 struct osl_object map
;
594 int ret
= map_index(desc
, &map
);
598 ret
= read_table_desc(&map
, desc
);
600 para_munmap(map
.data
, map
.size
);
603 set_dummy_contents(desc
);
604 ret
= init_table_structure(desc
, t
);
606 para_munmap(map
.data
, map
.size
);
609 PARA_INFO_LOG("unmapping index\n");
610 para_munmap(map
.data
, map
.size
);
611 if (conf
.force_given
)
612 ret
= map_table(*t
, (MAP_TBL_FL_IGNORE_DIRTY
));
614 ret
= map_table(*t
, 0);
616 (*t
)->num_rows
= table_num_rows(*t
);
621 static void fsck_cleanup(struct osl_table
*t
)
624 if (t
->desc
->column_descriptions
) {
625 struct osl_column_description
*cd
;
626 for (i
= 0; i
< t
->desc
->num_columns
; i
++) {
627 cd
= get_column_description(t
->desc
, i
);
628 free((char*)cd
->name
);
630 free(t
->desc
->column_descriptions
);
639 #define ST_CASE(st) case st: return #st
641 const char *get_asc_storage_type(enum osl_storage_type st
)
644 ST_CASE(OSL_MAPPED_STORAGE
);
645 ST_CASE(OSL_DISK_STORAGE
);
646 ST_CASE(OSL_NO_STORAGE
);
651 #define APPEND_ASC_SF(sf, flag, str) do { if (sf & flag) { \
652 if (str) str = para_strcat(str, " | " # flag); \
653 else str = para_strdup(#flag); }} while (0)
656 char *get_asc_storage_flags(enum osl_storage_type sf
)
660 APPEND_ASC_SF(sf
, OSL_RBTREE
, asc_sf
);
661 APPEND_ASC_SF(sf
, OSL_FIXED_SIZE
, asc_sf
);
662 APPEND_ASC_SF(sf
, OSL_UNIQUE
, asc_sf
);
666 static int dump_table_desc(struct osl_table
*t
, int fd
)
668 const struct osl_table_description
*desc
= t
->desc
;
670 struct osl_column_description
*cd
;
671 char *msg
= make_message("static struct osl_column_description cols[] = {\n");
672 ret
= para_write_all(fd
, msg
, strlen(msg
));
676 FOR_EACH_COLUMN(i
, desc
, cd
) {
678 msg
= make_message("\t[%d] = {\n", i
);
679 ret
= para_write_all(fd
, msg
, strlen(msg
));
683 asc_st
= get_asc_storage_type(cd
->storage_type
);
684 msg
= make_message("\t\t.storage_type = %s,\n", asc_st
);
685 ret
= para_write_all(fd
, msg
, strlen(msg
));
689 if (cd
->storage_flags
) {
690 char *asc_sf
= get_asc_storage_flags(cd
->storage_flags
);
691 msg
= make_message("\t\t,storage_flags = %s,\n", asc_sf
);
693 ret
= para_write_all(fd
, msg
, strlen(msg
));
698 if (cd
->storage_flags
& OSL_FIXED_SIZE
) {
699 msg
= make_message("\t\t.data_size = %u,\n", cd
->data_size
);
700 ret
= para_write_all(fd
, msg
, strlen(msg
));
705 msg
= make_message("\t\t.name = \"%s\",\n", cd
->name
);
706 ret
= para_write_all(fd
, msg
, strlen(msg
));
710 if (cd
->storage_flags
& OSL_RBTREE
) {
711 msg
= make_message("\t\t.compare_function = compare_func,\n");
712 ret
= para_write_all(fd
, msg
, strlen(msg
));
717 msg
= make_message("\t},\n");
718 ret
= para_write_all(fd
, msg
, strlen(msg
));
723 msg
= make_message("};\n");
724 ret
= para_write_all(fd
, msg
, strlen(msg
));
731 static int dump_row(struct osl_table
*t
, unsigned row_num
, const char *row_dir
)
734 const struct osl_column_description
*cd
;
736 struct osl_object obj
;
738 HASH_TYPE hash
[HASH_SIZE
];
741 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
) {
742 ret
= get_mapped_object(t
, i
, row_num
, &obj
);
745 filename
= make_message("%s/col_%03u", row_dir
, i
);
746 ret
= para_write_file(filename
, obj
.data
, obj
.size
);
751 if (!t
->num_disk_storage_columns
)
753 dsnc
= t
->disk_storage_name_column
;
754 ret
= get_mapped_object(t
, dsnc
, row_num
, &obj
);
757 hash_object(&obj
, hash
);
758 ds_name
= disk_storage_name_of_hash(t
, hash
);
759 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
) {
760 filename
= disk_storage_path(t
, i
, ds_name
);
761 ret
= mmap_full_file(filename
, O_RDONLY
, &obj
.data
, &obj
.size
, NULL
);
765 filename
= make_message("%s/col_%03u", row_dir
, i
);
766 ret
= para_write_file(filename
, obj
.data
, obj
.size
);
777 static int dump_rows(char *dump_dir
, struct osl_table
*t
)
780 char *current_dir
= NULL
;
783 for (i
= 0; i
< t
->num_rows
; i
++) {
785 if (row_is_invalid(t
, i
))
789 current_dir
= make_message("%s/rows_%u-%u", dump_dir
, i
, i
+ 999);
790 PARA_NOTICE_LOG("dumping rows %u - %u\n", i
, i
+ 999);
791 ret
= para_mkdir(current_dir
, 0777);
792 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
795 row_dir
= make_message("%s/row_%03u", current_dir
, i
);
796 ret
= para_mkdir(row_dir
, 0777);
797 if (ret
< 0 && !is_errno(-ret
, EEXIST
)) {
801 ret
= dump_row(t
, i
, row_dir
);
811 static int dump_table(char *dump_dir
, struct osl_table_description
*desc
)
813 struct osl_table
*t
= NULL
;
814 int fd
, ret
= fsck_init(desc
, &t
);
816 char *table_dump_dir
= NULL
;
820 ret
= para_mkdir(dump_dir
, 0777);
821 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
823 table_dump_dir
= make_message("%s/%s", dump_dir
, desc
->name
);
824 ret
= para_mkdir(table_dump_dir
, 0777);
825 if (ret
< 0 && !is_errno(-ret
, EEXIST
))
827 desc_file
= make_message("%s/table_description.c", table_dump_dir
);
828 ret
= para_open(desc_file
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
833 ret
= dump_table_desc(t
, fd
);
837 ret
= dump_rows(table_dump_dir
, t
);
839 free(table_dump_dir
);
844 static int fsck(struct osl_table_description
*desc
)
847 struct osl_table
*t
= NULL
;
848 uint32_t *lost_bytes
= NULL
;
850 ret
= fsck_init(desc
, &t
);
853 ret
= check_index_ranges(t
);
856 ret
= check_disk_storage_columns(t
);
859 ret
= prune_invalid_rows_from_index(t
);
862 ret
= check_for_invalid_objects(t
, &lost_bytes
);
865 if (ret
> 0) { /* at least one mapped data file needs pruning */
866 ret
= prune_objects(t
, lost_bytes
);
871 PARA_INFO_LOG("success\n");
873 unmap_table(t
, OSL_MARK_CLEAN
);
879 static int check_table(char *base_dir
, char *table_name
)
881 struct osl_table_description desc
= {
882 .column_descriptions
= NULL
,
888 if (!conf
.no_fsck_given
) {
893 if (!conf
.dump_dir_given
|| !*conf
.dump_dir_arg
)
895 return dump_table(conf
.dump_dir_arg
, &desc
);
898 static int check_all_tables(char *base_dir
)
901 struct dirent
*entry
;
902 int cwd_fd
, ret2
, ret
= para_opendir(base_dir
, &dir
, &cwd_fd
);
906 while ((entry
= readdir(dir
))) {
909 if (!strcmp(entry
->d_name
, "."))
911 if (!strcmp(entry
->d_name
, ".."))
913 if (lstat(entry
->d_name
, &s
) == -1)
918 ret
= check_table(base_dir
, entry
->d_name
);
923 ret2
= para_fchdir(cwd_fd
);
924 if (ret2
< 0 && ret
>= 0)
930 int main(int argc
, char **argv
)
933 char *base_dir
= NULL
;
935 ret
= fsck_cmdline_parser(argc
, argv
, &conf
);
937 ret
= -E_FSCK_SYNTAX
;
940 HANDLE_VERSION_FLAG("fsck", conf
);
941 if (conf
.base_dir_given
)
942 base_dir
= conf
.base_dir_arg
;
944 char *home
= para_homedir();
945 base_dir
= make_message("%s/.paraslash/afs_database", home
);
948 if (!conf
.inputs_num
) {
949 ret
= check_all_tables(base_dir
);
952 for (i
= 0; i
< conf
.inputs_num
; i
++) {
953 ret
= check_table(base_dir
, conf
.inputs
[i
]);
958 if (!conf
.base_dir_given
)
961 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret
));
962 return ret
< 0? EXIT_FAILURE
: EXIT_SUCCESS
;